/************************************************************************/
/*                                                                      */
/*    *****                       *****                                 */
/*      *****                   *****                                   */
/*        *****               *****                                     */
/*          *****           *****                                       */
/*  ***************       ***************                               */
/*  *****************   *****************                               */
/*  ***************       ***************                               */
/*          *****           *****           TheNetNode                  */
/*        *****               *****         Portable                    */
/*      *****                   *****       Network                     */
/*    *****                       *****     Software                    */
/*                                                                      */
/* File os/linux/ax25ip.c (maintained by: DG9OBU)                       */
/*                                                                      */
/* This file is part of "TheNetNode" - Software Package                 */
/*                                                                      */
/* Copyright (C) 1998 - 2008 NORD><LINK e.V. Braunschweig               */
/*                                                                      */
/* This program is free software; you can redistribute it and/or modify */
/* it under the terms of the NORD><LINK ALAS (Allgemeine Lizenz fuer    */
/* Amateurfunk Software) as published by Hans Georg Giese (DF2AU)       */
/* on 13/Oct/1992; either version 1, or (at your option) any later      */
/* version.                                                             */
/*                                                                      */
/* This program is distributed WITHOUT ANY WARRANTY only for further    */
/* development and learning purposes. See the ALAS (Allgemeine Lizenz   */
/* fuer Amateurfunk Software).                                          */
/*                                                                      */
/* You should have received a copy of the NORD><LINK ALAS (Allgemeine   */
/* Lizenz fuer Amateurfunk Software) along with this program; if not,   */
/* write to NORD><LINK e.V., Hinter dem Berge 5, D-38108 Braunschweig   */
/*                                                                      */
/* Dieses Programm ist PUBLIC DOMAIN, mit den Einschraenkungen durch    */
/* die ALAS (Allgemeine Lizenz fuer Amateurfunk Software), entweder     */
/* Version 1, veroeffentlicht von Hans Georg Giese (DF2AU),             */
/* am 13.Oct.1992, oder (wenn gewuenscht) jede spaetere Version.        */
/*                                                                      */
/* Dieses Programm wird unter Haftungsausschluss vertrieben, aus-       */
/* schliesslich fuer Weiterentwicklungs- und Lehrzwecke. Naeheres       */
/* koennen Sie der ALAS (Allgemeine Lizenz fuer Amateurfunk Software)   */
/* entnehmen.                                                           */
/*                                                                      */
/* Sollte dieser Software keine ALAS (Allgemeine Lizenz fuer Amateur-   */
/* funk Software) beigelegen haben, wenden Sie sich bitte an            */
/* NORD><LINK e.V., Hinter dem Berge 5, D-38108 Braunschweig            */
/*                                                                      */
/************************************************************************/

/************************************************************************/
/*                                                                      */
/* AX25IP.C V1.05 01.9.98 by DG1KWA / Andreas                           */
/*                        extended by DG9OBU / Marc                     */
/************************************************************************/

#include "tnn.h"

#ifdef AX25IP
#include "ax25ip.h"

/*************************************************************************/
/* AX25IP-Hausaufgaben : RX, TX und Routingtabellenpflege                */
/*************************************************************************/
void ax25ip(void)
{
  register UBYTE uEntry = 0;

  /* nur wenn Interface aktiv ist */
  if (!ax25ip_active)
    return;

  /* empfangene Frames abholen */
  ax25ip_recv();

  /* IP-Adressen der Routentabelle pflegen */
  if ((uNamesUpdate != 0) && (tic10 % uNamesUpdate == 0))
  {
    for (; uEntry <= TABLE_SIZE; ++uEntry)
      route_update(&route_tbl[uEntry]);

    route_update(&default_route);
  }

  /* ausstehende Frames senden wenn vorhanden */
  if (kick[ax25ip_port])
    ax25ip_send();
}

/*************************************************************************/
/* Einen Eintrag aus der Routingtabelle ausgeben                         */
/*************************************************************************/
void show_rt_entry(struct route_table_entry rp, MBHEAD *bufpoi)
{
  /* erst mal merken, wo wir sind */
  bufpoi->l4time = bufpoi->mbpc;

  /* Check auf Eintrag fuer die default-Route */
  if (rp.callsign[0] != 1)
    putprintf(bufpoi, "%s", call_to_a(&rp.callsign[0]));
  else
    putstr("default", bufpoi);

  putspa(11, bufpoi);

  /* IP-Adresse ausgeben */
  show_ip_addr(ntohl(rp.ip_addr), bufpoi);

  putspa(29, bufpoi);

  /* UDP-Port ausgeben wenn UDP-Port gesetzt */
  if (rp.udp_port != 0)
  {
    /* Eine UDP-Route */
    putstr("UDP", bufpoi);
    putspa(35, bufpoi);
    putnum(ntohs(rp.udp_port), bufpoi);
  }
  else
    /* Eine IP-Route */
    putstr("IP", bufpoi);

  /* Routen-Timeout anzeigen */
  if (rp.timeout != 0)
  {
    ULONG d = rp.timeout;
    ULONG h, m, s;                  /* Stunden, Minuten, Sekunden */

    s = d % 60L;
    d /= 60L;
    m = d % 60L;
    d /= 60L;
    h = d % 24L;

    putspa(40, bufpoi);
    putprintf(bufpoi, " %02lu:%02lu:%02lu", h, m, s); /* hh:mm:ss */
  }

  if (rp.hostname[0] != 0)
  {
    putspa(50, bufpoi);
    putprintf(bufpoi, "%s", rp.hostname);
  }

  putstr("\r", bufpoi);
}

/* Anzahl von IP- oder UDP-Routen in der Routingtabelle feststellen */
/* (IP-Routen haben keinen UDP-Port gesetzt) */
/* UDP = FALSE -> IP-Routen zaehlen, UDP = TRUE -> UDP-Routen zaehlen */
/* Default-Route wird beachtet */
unsigned int count_routes(BOOLEAN udp)
{
  register unsigned int i;
  register unsigned int j = 0;

  /* Gesamte Routingtabelle durchgehen */
  for (i = 0; i < (unsigned int)route_tbl_top; ++i)
  {
    if (   ((udp == TRUE) && (route_tbl[i].udp_port != 0))    /* UDP-Route */
        || ((udp == FALSE) && (route_tbl[i].udp_port == 0)))  /* IP-Route */
      ++j;
  }

  /* Default-Route */
  if (default_route.callsign[0] != 0)
  {
    if (   ((udp == TRUE) && (default_route.udp_port != 0))    /* UDP-Route */
        || ((udp == FALSE) && (default_route.udp_port == 0)))  /* IP-Route */
      ++j;
  }

  /* Anzahl Routen melden */
  return (j);
}

/* Timeout fuer gelernte Routen */
void route_age(void)
{
  register unsigned int i = 0;

#ifdef AXIPR_HTML
  /* htmlstatistik-timer. */
  h_timer();
#endif

  /* Gesamte Routingtabelle durchgehen */
  for (; i < (unsigned int)route_tbl_top; ++i)
  {
    /* Route mit laufendem Timer */
    if (route_tbl[i].timeout != 0)
    {
      /* Timer laeuft jetzt ab */
      if (--route_tbl[i].timeout == 0)
        /* Route loeschen */
        route_del(route_tbl[i].callsign);
    }
  }
}

/* Hostname -> IP-Adressen aufloesen */
void route_update(struct route_table_entry *rtentry)
{
  struct hostent *hstent;

  /* Kein Hostname, dann auch keine Umwandlung moeglich */
  if (rtentry->hostname[0] == 0)
    return;

  /* Hostname im IP-Adresse konvertieren */
  if ((hstent = gethostbyname((char*)&rtentry->hostname[0])) != NULL)
  {
    /* Bei Aenderung uebernehmen */
    if (memcmp(&rtentry->ip_addr, &hstent->h_addr_list[0][0], 4) != 0)
    {
      /* IP-Adresse hat sich geaendert */
      memcpy(&rtentry->ip_addr, &hstent->h_addr_list[0][0], 4);
      LOGL1("IP-adress for %s changed !", rtentry->hostname);
    }
  }

#ifdef AXIPR_UDP
  /* Original UDP-Port setzen. */
  rtentry->udp_port = rtentry->org_udp_port;
#endif /* AXIPR_UDP */
}

/* Partielle Frame-Analyse fuer dyn. Eintraege */
BOOLEAN route_canlearn(MBHEAD *fbp)
{
  char *p;                              /* Zeiger im Header             */
  register int n;
  UBYTE ctl;

  rwndmb(fbp);                          /* Frame von vorne              */

  for (p = rxfhdr, n = 1; n <= L2INUM + L2VNUM; ++n)
  {
    if (!getfid(p, fbp))                /* naechstes Call lesen         */
      return (FALSE);

    p += L2IDLEN;
    if (*(p - 1) & L2CEOA)
      break;                            /* Ende des Addressfeldes       */
  }
  *(p - 1) &= ~L2CEOA;
  *p = NUL;
  ctl = getchr(fbp);                    /* Control-Byte extrahieren     */
  ctl &= ~L2CPF;                        /* Poll-Flag loeschen           */
  /* Nur UI, UA und SABM/SABME erzeugen einen dynamischen Eintrag       */
  if (   ctl == L2CUI                   /* UI                           */
      || ctl == L2CUA                   /* UA                           */
      || ctl == L2CSABM                 /* SABM                         */
#ifdef EAX25
      || ctl == L2CSABME                /* SABME (EAX.25)               */
     )
#endif
    return (TRUE);

#ifndef AXIPR_UDP
  /* Alle anderen Kontrollcodes landen hier und bewirken keinen Eintrag */
  return (FALSE);
#else
  return (TRUE);
#endif
}

/*************************************************************************/
/* Kommandointerface fuer Operationen auf die AX25IP-Routentabelle       */
/*************************************************************************/

/* Eine Route anhand eines Calls loeschen, liefert TRUE wenn erfolgreich */
BOOLEAN route_del(unsigned char *call)
{
  register int i;
  register int j;

  /* Soll die Default-Route geloescht werden ? */
  if ((call == NULL) || (call[0] == NUL))
  {
    /* Als unbenutzt markieren */
    default_route.callsign[0] = NUL;

    /* Pruefen, ob Sockets noch notwendig */
    ax25ip_check_down();
    return (TRUE);
  }

  /* Uebergebenes Call in der Tabelle suchen */
  for (i = 0; i < route_tbl_top; ++i)
  {
    if (addrmatch(call, route_tbl[i].callsign))
    {
    /* BINGO ! */
    /* Problem: die Tabelle hat keinen Merker, ob ein Eintrag gueltig ist, */
    /*          die Eintraege haengen einfach hintereinander. Deshalb alle */
    /*          Eintraege oberhalb des gefundenen Eintrages einen nach     */
    /*          unten kopieren und den "table-top" einen herabsetzen.      */
      for (j = i; j < route_tbl_top; ++j)
        route_tbl[j] = route_tbl[j + 1];

      /* Ein Tabelleneintrag weniger */
      route_tbl_top--;

      /* Pruefen, ob Sockets noch notwendig */
      ax25ip_check_down();
      return(TRUE);
    }
  }

  return(FALSE);
}

static const char inv_mode[] = "Invalid Mode\r";
static const char inv_call[] = "Invalid Callsign\r";
static const char inv_ip[]   = "Invalid IP adress\r";
static const char inv_par[]  = "Invalid Parameter\r";

void
ccpaxipr(void)
{
  MBHEAD         *mbp;
  int             port = 0;
  register int    i;
  int             tmp_fd = -1;
  int             new_udp = 0;
  unsigned char   call[L2IDLEN];
  char            hostname[HNLEN + 1];
  struct          hostent *host = NULL;
  int             uNewTimeout;
#ifdef AXIPR_UDP
  char           *Hostname = hostname;
  int             NewLogLevel = FALSE;
#endif
#ifdef AXIPR_HTML
  char           *timeset  = set_time();
  int             NewHtmlStat = FALSE;
#endif
  char            cBuf[BUFLEN + 1];
  unsigned int    uCmd = OP_NONE;
  unsigned int    uMode = NO_MODE;

#ifdef AXIPR_HTML
#define OP_HTMLSTAT 10
#endif

  memset(hostname, 0, sizeof(hostname));

  /* AX25IP ueberhaupt aktiv ? */
  if (ax25ip_active == FALSE)
  {
    putmsg("No AX25IP-interface present, nothing to configure or show !\r");
    return;
  }

  /* neuen Parser verwenden ? */
  if (new_parser == TRUE)
  {

  /* Sysop will aendern und noch was in der Zeile da ? */
  if (issyso() && skipsp(&clicnt, &clipoi))
  {
    /* Frischer Buffer */
    memset(cBuf, 0, sizeof(cBuf));

    /* Operation lesen (add, delete) */
    for (i = 0; i < BUFLEN; ++i)
    {
      if ((!clicnt) || (*clipoi == ' '))
        break;
      clicnt--;
      cBuf[i] = toupper(*clipoi++);
    }

    /* Zum alten Parser zurueckschalten */
    if (   (strcmp(cBuf, "OLD") == 0)
        || (cBuf[0] == 'O')
       )
    {
      new_parser = FALSE;
      putmsg("Switched to old axipr-parser !!!\r");
      return;
    }

    /* Hinzufuegen (add oder +) */
    if (   (strcmp(cBuf, "ADD") == 0)
        || (cBuf[0] == '+')
       )
      uCmd = OP_ADD;

    /* Loeschen (delete, del oder -) */
    if (   (strcmp(cBuf, "DELETE") == 0)
        || (strcmp(cBuf, "DEL") == 0)
        || (cBuf[0] == '-')
       )
      uCmd = OP_DEL;

    /* eigenen UDP-Port aendern */
    if (strcmp(cBuf, "MYUDP") == 0)
      uCmd = OP_MYUDP;

    /* Namensaufloesungsintervall einstellen */
    if (strcmp(cBuf, "LOOKUP") == 0)
      uCmd = OP_HNUPD;

    /* Loglevel */
    if (   (strcmp(cBuf, "LOGLEVEL") == 0)
        || (strcmp(cBuf, "LOG") == 0)
       )
      uCmd = OP_LOG;

    /* Timeout fuer dynamische Routen */
    if (strcmp(cBuf, "TIMEOUT") == 0)
      uCmd = OP_TIMEOUT;

#ifdef AXIPR_HTML
    /* htmlstatistik */
    if (   (strcmp(cBuf, "HTMLSTAT") == 0)
        || (strcmp(cBuf, "H") == 0))

      uCmd = OP_HTMLSTAT;
#endif

    /* Fehler bei der Auswertung ? */
    if (uCmd == OP_NONE)
    {
      putmsg(inv_mode);
      return;
    }

    /* Hier rein, wenn der Timeout fuer dynamische Routen geaendert werden soll */
    if (uCmd == OP_TIMEOUT)
    {
      int uNewTimeout;

      /* Noch was da in der Kommandozeile ? */
      if (!skipsp(&clicnt, &clipoi))
      {
        mbp = getmbp();
        putprintf(mbp, "the actual timeout for dynamic routes is : %u\r", uDynTimeout);
        /* und ab die Post ... */
        prompt(mbp);
        seteom(mbp);
        return;
      }

      /* Timeout lesen */
      uNewTimeout = nxtlong(&clicnt, &clipoi);

      if (  (uNewTimeout < 0)
          ||(uNewTimeout > (86400)))  /* max. 1 Tag */
      {
        putmsg("error: timeout out of range (0 - 86400) !\r");
        return;
      }

      /* Wert uebernehmen */
      uDynTimeout = uNewTimeout;

      /* und ab die Post ... */
      mbp = getmbp();
      prompt(mbp);
      seteom(mbp);
      return;
    }

    /* Hier rein, wenn das Intervall der Namensaufloesung geaendert werden soll */
    if (uCmd == OP_HNUPD)
    {
      UWORD uNewInterval;

      /* Noch was da in der Kommandozeile ? */
      if (!skipsp(&clicnt, &clipoi))
      {
        mbp = getmbp();

        putprintf(mbp, "Hostname-update ");

        if (uNamesUpdate != 0)
          putprintf(mbp, "occurs every %u seconds.\r", uNamesUpdate / 100);
        else
          putprintf(mbp, "is disabled at the moment.\r");

        /* und ab die Post ... */
        prompt(mbp);
        seteom(mbp);
        return;
      }

      /* Timeout lesen */
      uNewInterval = nxtnum(&clicnt, &clipoi);

      if (uNewInterval > 3600)  /* max. eine Stunde */
      {
        putmsg("error: value out of range (0 - 3600 seconds) !\r");
        return;
      }

      /* Wert uebernehmen */
      uNamesUpdate = uNewInterval * 100;

      /* und ab die Post ... */
      mbp = getmbp();
      prompt(mbp);
      seteom(mbp);
      return;
    }

    /* Hier rein, wenn der Loglevel geaendert werden soll */
    if (uCmd == OP_LOG)
    {
      int new_loglevel;

      /* Noch was da in der Kommandozeile ? */
      if (!skipsp(&clicnt, &clipoi))
      {
        mbp = getmbp();
        putprintf(mbp, "my actual loglevel is : %u\r", loglevel);
        /* und ab die Post ...                                            */
        prompt(mbp);
        seteom(mbp);
        return;
      }

      /* neues Loglevel lesen */
      new_loglevel = nxtnum(&clicnt, &clipoi);

      if (  (new_loglevel < 0)
          ||(new_loglevel > 4))
      {
        putmsg("error: loglevel out of range (0 - 4) !!!\r");
        return;
      }

      /* Wert uebernehmen */
      loglevel = new_loglevel;

      /* und ab die Post ... */
      mbp = getmbp();
      prompt(mbp);
      seteom(mbp);
      return;
    }

#ifdef AXIPR_HTML
    if (uCmd == OP_HTMLSTAT)
    {
      /* Noch was da in der Kommandozeile ? */
      if (!skipsp(&clicnt, &clipoi))
      {
        mbp = getmbp();
#ifdef SPEECH
        putprintf(mbp, speech_message(347),HtmlStat);
#else
        putprintf(mbp, "HTML-Statistic is %d\r", HtmlStat);
#endif
        /* und ab die Post ... */
        prompt(mbp);
        seteom(mbp);
        return;
      }

      /* Timeout lesen */
      NewHtmlStat = nxtnum(&clicnt, &clipoi);

      if (  (NewHtmlStat < 0)
          ||(NewHtmlStat > 1))
      {
#ifdef SPEECH
        putmsg(speech_message(300));
#else
        putmsg("errors: Log level worth from 0 to 1!\r");
#endif
        return;
      }

      /* Wert uebernehmen */
      HtmlStat = NewHtmlStat;

      /* und ab die Post ... */
      mbp = getmbp();
      prompt(mbp);
      seteom(mbp);
      return;
    }
#endif /* AXIPR_HTML */

    /* Hier nur rein, wenn wir als naechstes eine UDP-Portnummer erwarten */
    if (uCmd == OP_MYUDP)
    {
      /* Noch was da in der Kommandozeile ? */
      if (!skipsp(&clicnt, &clipoi))
      {
        mbp = getmbp();
        putprintf(mbp, "my actual UDP-port is : %u\r", ntohs(my_udp));
        /* und ab die Post ...                                            */
        prompt(mbp);
        seteom(mbp);
        return;
      }

      /* neue UDP-Portnummer lesen */
      new_udp = nxtlong(&clicnt, &clipoi);

      if (  (new_udp <= 0)
          ||(new_udp > 65535))
      {
        putmsg("error: UDP-port number not valid (0 - 65535) !!!\r");
        return;
      }

#ifdef AXIPR_UDP
      /* Neuer UDP-Port gleich MY-UDP?   */
      /* Keine Aenderungen durchfuehren. */
      if (my_udp == htons((unsigned short)new_udp))
      {
        /* und ab die Post ...           */
        mbp = getmbp();
        prompt(mbp);
        seteom(mbp);
        return;
      }
#endif /* AXIPR_UDP */

#ifndef AXIPR_UDP
      /* Laufendes UDP aendern */
      if (fd_udp != -1)
      {
#endif
        /* Versuchen, neuen UDP-Port anzulegen */
        if ((tmp_fd = setup_udp(htons((unsigned short)new_udp))) != -1)
        {
#ifndef AXIPR_UDP
          /* Neuen Descriptor eintragen und alten schliessen */
          close(fd_udp);
#else
          /* Nur wenn alter Socket noch lebt,  */
          if (fd_udp != EOF)
            /* dann schliessen wir den Socket. */
            close(fd_udp);
#endif /* AXIPR_UDP */

          fd_udp = tmp_fd;
          /* Gibt es einen Eintrag. */
          if (l1pp)
            /* Check, ob neuer Filedescriptor groesser ist */    
            l1pp->kisslink = max(fd_ip, fd_udp);

          /* Neuen UDP-Port merken */
          my_udp = htons((unsigned short)new_udp);
        }
        else
        {
          putmsg("error: changing the UDP-port failed !!!\r");
          return;
        }
#ifndef AXIPR_UDP
      }
      else
      {
        /* neuen UDP-Port merken */
        my_udp = htons(new_udp);
      }
#endif
      /* und ab die Post ...                                                  */
      mbp = getmbp();
      prompt(mbp);
      seteom(mbp);
      return;
    }

    /* Noch was da in der Kommandozeile ? */
    if (!skipsp(&clicnt, &clipoi))
    {
      putmsg("syntax error: callsign missing\r");
      return;
    }

    /* Frischer Buffer */
    memset(cBuf, 0, sizeof(cBuf));

    /* Call lesen */
    for (i = 0; i < BUFLEN; ++i)
    {
      if ((!clicnt) || (*clipoi == ' '))
        break;

      clicnt--;
      cBuf[i] = toupper(*clipoi++);
    }

    /* Operation auf Default-Route ? */
    if (strcmp(cBuf, "DEFAULT") == 0)
      call[0] = NUL;    /* Leere Callstring als Indikator fuer Defaultroute */
    else
    {
      /* Call konvertieren und pruefen */
      if (a_to_call(cBuf, call) != 0)
      {
        putmsg(inv_call);
        return;
      }
    }

    /* Wenn wir loeschen wollen, brauchen wir nichts mehr */
    if (uCmd != OP_DEL)
    {
      /* Noch was da in der Kommandozeile ? */
      if (!skipsp(&clicnt, &clipoi))
      {
        putmsg("syntax error: IP-adress missing\r");
        return;
      }

      /* Frische Buffer */
      memset(hostname, 0, sizeof(hostname));

      /* Hostnamen / IP-Adresse lesen */
      for (i = 0; i < HNLEN; ++i)
      {
        if (!clicnt || *clipoi == ' ')
          break;

        clicnt--;
        hostname[i] = *clipoi++;
      }

      /* eventuellen Hostnamen aufloesen */
      if ((host = gethostbyname(hostname)) == NULL)
      {
        mbp = getmbp();
        putprintf(mbp, "Warning: can't resolve IP for host %s ! I keep trying ...\r", hostname);
        prompt(mbp);
        seteom(mbp);
      }
      else
      {
        strncpy(hostname, host->h_name, sizeof(hostname));
      }
    } /* if (uCmd != OP_DEL) */

    /* Optionale UDP-Parameter lesen */
    skipsp(&clicnt, &clipoi);

    /* Frischer Buffer */
    memset(cBuf, 0, sizeof(cBuf));

    /* Mode lesen (IP, UDP) */
    for (i = 0; i < BUFLEN; ++i)
    {
      if ((!clicnt) || (*clipoi == ' '))
        break;

      clicnt--;
      cBuf[i] = toupper(*clipoi++);
    }

    /* Mode bestimmen (UDP) */
    /* Bei der Initialisierung wurde bereits IP eingestellt */
    if (   (strcmp(cBuf, "UDP") == 0)
        || (cBuf[0] == 'U')
       )
      uMode = UDP_MODE;

    /* Noch was da in der Kommandozeile ? */
    if (!skipsp(&clicnt, &clipoi))
      port = DEFAULT_UDP_PORT;
    else
    {
      /* neue UDP-Portnummer lesen */
      port = nxtlong(&clicnt, &clipoi);

      if (  (port <= 0)
          ||(port > 65535))
      {
        putmsg("error: UDP-port number not valid\r");
        return;
      }
    }

    /* Operation ausfuehren */
    switch (uCmd)
    {
      /* Route hinzufuegen */
      case OP_ADD :
#ifndef AXIPR_UDP
        route_add((unsigned char*)&hostname, host, call, (uMode == UDP_MODE ? port : 0), (call[0] == 0 ? TRUE : FALSE)
        break;
#else
        if (route_add((unsigned char*)&hostname
                                 , host
                                 , call
                                 , (uMode == UDP_MODE ? (int)port : 0)
                                 , (call[0] == 0 ? TRUE : FALSE)
                                 , 0
                                 , hostname
#ifdef AXIPR_HTML
                                 , timeset
                                 , P_USER
#endif /* AXIPR_HTML */
                                 ))
                      putmsg("Call is registerd.\r");

                    return;
#endif /* AXIPR_UDP */

      /* Route loeschen */
      case OP_DEL :
#ifndef AXIPR_UDP
                    route_del(call);
                    break;
#else

                    if (route_del(call))
                      putmsg("Call is deleted!\r");
                    else
                      putmsg("Call no deleted!\r");

                    return;
#endif /* AXIPR_UDP. */


      default     : break;
    }

    mbp = getmbp();
    /* und ab die Post ... */
    prompt(mbp);
    seteom(mbp);
    return;
  }

  }
  else
  {
  /* ALTE SYNTAX */

  /* Sysop will aendern und noch was in der Zeile da ? */
  if (issyso() && skipsp(&clicnt, &clipoi))
  {
    clicnt--;

    switch (toupper(*clipoi++))
    {
      case 'P':                                 /* Parser aendern       */
        /* wir koennen nur in den neuen Parser umschalten */
        new_parser = TRUE;
        putmsg("Switched to new axipr-parser !!!\r");
        return;
        break;

      case 'R':                                 /* Routen-Eintrag       */
        /* Kommandozeile pruefen */
        if (!skipsp(&clicnt, &clipoi))
        {
          putmsg(inv_par);
          return;
        }

        clicnt--;

        switch (*clipoi++)
        {
          case '-':                             /* Route loeschen       */
            /* Call lesen */
            if (getcal(&clicnt, &clipoi, TRUE, (char *)call) != YES)
            {
              invcal();
              return;
            }

            /* Call konvertieren */
            for (i = 0; i < L2CALEN; ++i)
              call[i] = call[i] << 1;

#ifdef AXIPR_UDP
            /* und ab die Post ...                                                  */
            mbp = getmbp();

            if (route_del(call))
              putstr("Call is delete!\r", mbp);
            else
              putstr("Call no delete!\r", mbp);

            prompt(mbp);
            seteom(mbp);
            return;
#else
           /* Route loeschen */
            route_del(call);
            break;
#endif /* AXIPR_UDP. */

          case '+':                             /* Route hinzufuegen    */
            /* Call lesen */
            if (getcal(&clicnt, &clipoi, TRUE, (char *)call) != YES)
            {
              invcal();
              return;
            }

            /* Call linksschieben   */
            for (i = 0; i < L2CALEN; ++i)
              call[i] = call[i] << 1;

            /* Noch was da in der Kommandozeile ? */
            if (!skipsp(&clicnt, &clipoi))
            {
              putmsg("IP-Adress/Hostname missing\r");
              return;
            }

            /* Frische Buffer */
            memset(hostname, 0, sizeof(hostname));

            /* Hostnamen / IP-Adresse lesen */
            for (i = 0; i < HNLEN; ++i)
            {
              if (!clicnt || *clipoi == ' ')
                break;

              clicnt--;
              hostname[i] = *clipoi++;
            }

            /* Hostname / IP-Adresse aufloesen */
            if ((host = gethostbyname(hostname)) == NULL)
            {
              putmsg(inv_ip);
              return;
            }

            /* Mode-Parameter vorhanden ? */
            if (skipsp(&clicnt, &clipoi))
            {
              --clicnt;
              switch (toupper(*clipoi++))
              {
                case 'I':       /* IP: Port steht fest */
                  break;
                case 'U':       /* UDP: abweichenden Port lesen wenn vorhanden */
                  nextspace(&clicnt, &clipoi);
                  if (!skipsp(&clicnt, &clipoi))
                    port = DEFAULT_UDP_PORT;
                  else
                    {
                      port = nxtlong(&clicnt, &clipoi);

                      if (  (port <= 0)
                          ||(port > 65535))
                      {
                        putmsg("error: UDP-port number not valid\r");
                        return;
                      }
                    }
                  break;

                default:        /* unbekannter Modus */
                  putmsg(inv_mode);
                  return;
              }
            }
            /* Route hinzufuegen */
#ifndef AXIPR_UDP
            route_add((unsigned char*)&hostname, host, call, (int) port, FALSE, 0);
            break;
#else
            if (route_add((unsigned char*)&hostname
                         , host
                         , call
                         , (int) port
                         , FALSE
                         , 0
                         , hostname
#ifdef AXIPR_HTML
                         , timeset
                         , P_USER
#endif /* AXIPR_HTML */
                         ))
              putmsg("Call is registerd.\r");

            return;
#endif /* AXIPR_UDP */

        }
        break;

      case 'D':                         /* Default Route aendern        */

        nextspace(&clicnt, &clipoi);

        /* Noch Eingaben da ? */
        if (!skipsp(&clicnt, &clipoi))
        {
          putmsg(inv_mode);
          return;
        }

        clicnt--;

        /* Operation bestimmen */
        switch (*clipoi++)
        {
          case '-':                     /* Default Route loeschen       */
#ifdef AXIPR_UDP
            /* und ab die Post ...                                                  */
            mbp = getmbp();

            if (route_del((unsigned char *)""))
              putstr("Call is delete!\r", mbp);
            else
              putstr("Call no delete!\r", mbp);

            prompt(mbp);
            seteom(mbp);
            return;
#else
            route_del((unsigned char *)"");
            break;
#endif /* AXIPR_UDP. */

          case '+':                     /* Default Route eingeben       */

            /* IP-Adresse / Hostname lesen */
            if (!skipsp(&clicnt, &clipoi))
            {
              putmsg(inv_ip);
              return;
            }

            /* Frische Buffer */
            memset(hostname, 0, sizeof(hostname));

            /* IP-Adresse / Hostname kopieren */
            for (i = 0; i < HNLEN; ++i)
            {
              if (!clicnt || *clipoi == ' ')
                break;
              clicnt--;
              hostname[i] = *clipoi++;
            }

            /* aufloesen */
            if ((host = gethostbyname(hostname)) == NULL)
            {
              putmsg(inv_ip);
              return;
            }

            /* Modus-Kennzeichner lesen falls vorhanden */
            if (skipsp(&clicnt, &clipoi))
            {
              --clicnt;
              switch (toupper(*clipoi++))
              {
                case 'I':   /* IP: Port steht fest */
                  break;
                case 'U':   /* UDP: eventuell abweichenden Port lesen */
                  nextspace(&clicnt, &clipoi);

                  if (!skipsp(&clicnt, &clipoi))
                    port = DEFAULT_UDP_PORT;
                  else
                    {
                      port = nxtlong(&clicnt, &clipoi);

                      if (  (port <= 0)
                          ||(port > 65535))
                      {
                        putmsg("error: UDP-port number not valid\r");
                        return;
                      }
                    }
                  break;
                default:    /* unbekannter Modus */
                  putmsg(inv_mode);
                  return;
              }
            }
            /* Route eintragen */
#ifndef AXIPR_UDP
            route_add((unsigned char*)&hostname, host, NULL, (int) port, TRUE, 0);
            break;
#else
            if (route_add((unsigned char*)&hostname
                         , host
                         , NULL
                         , (int) port
                         , TRUE
                         , 0
                         , hostname
#ifdef AXIPR_HTML
                         , timeset
                         , P_USER
#endif /* AXIPR_HTML */
                         ))
              putmsg("Call is registerd.\r");

            return;
#endif /* AXIPR_UDP */

          default:
            putmsg(inv_mode);
            return;
        }
        break;

      /* Eigenen UDP-Port aendern */
      case 'U':
        /* neuer Port muss vorhanden sein */
        if (!skipsp(&clicnt, &clipoi))
        {
          putmsg(inv_par);
          return;
        }

        /* Port ermitteln. */
        new_udp = nxtlong(&clicnt, &clipoi);

        if (  (new_udp <= 0)
            ||(new_udp >= 65535))
        {
          putmsg("UDP-Port not valid, not changed !!!\r");
          return;
        }

#ifdef AXIPR_UDP
        /* Neuer UDP-Port gleich MY-UDP?   */
        /* Keine Aenderungen durchfuehren. */
        if (my_udp == htons((unsigned short)new_udp))
        {
          /* und ab die Post ...           */
          mbp = getmbp();
         prompt(mbp);
         seteom(mbp);
         return;
        }
#endif /* AXIPR_UDP */

#ifndef AXIPR_UDP
        /* Laufendes UDP aendern */
        if (fd_udp != -1)
        {
#endif
          /* Versuchen, neuen UDP-Port anzulegen */
          if ((tmp_fd = setup_udp(htons((unsigned short)new_udp))) != -1)
          {
#ifndef AXIPR_UDP
          /* Neuen Descriptor eintragen und alten schliessen */
          close(fd_udp);
#else
          /* Nur wenn alter Socket noch lebt,  */
          if (fd_udp != EOF)
            /* dann schliessen wir den Socket. */
            close(fd_udp);
#endif /* AXIPR_UDP */

            fd_udp = tmp_fd;
            /* Gibt es einen Eintrag. */
            if (l1pp)
              /* Check, ob neuer Filedescriptor groesser ist */
              l1pp->kisslink = max(fd_ip, fd_udp);

            /* Neuen UDP-Port merken */
            my_udp = htons((unsigned short)new_udp);
          }
          else
          {
            putmsg("ERROR: Changing UDP-Port failed, Port not changed !!!\r");
            return;
          }
#ifndef AXIPR_UDP
        }
        else
        {
          /* neuen UDP-Port merken */
          my_udp = htons(new_udp);
        }
#endif

        putmsg("UDP-Port successfully changed\r");
#ifdef AXIPR_UDP
        return;
#else
        break;
#endif /* AXIPR_UDP. */

      /* Timeout fuer dynamische Routen aendern. */
      case 'T':

      /* Noch was da in der Kommandozeile ? */
      if (!skipsp(&clicnt, &clipoi))
      {
        mbp = getmbp();
        putprintf(mbp, "the actual timeout for dynamic routes is : %u\r", uDynTimeout);
        /* und ab die Post ... */
        prompt(mbp);
        seteom(mbp);
        return;
      }

      /* Timeout lesen */
      uNewTimeout = nxtlong(&clicnt, &clipoi);

      if (  (uNewTimeout < 0)
          ||(uNewTimeout > (86400)))  /* max. 1 Tag */
      {
        putmsg("error: timeout out of range (0 - 86400) !\r");
        return;
      }

      /* Wert uebernehmen */
      uDynTimeout = uNewTimeout;

      /* und ab die Post ... */
      mbp = getmbp();
      prompt(mbp);
      seteom(mbp);
      return;

#ifdef AXIPR_UDP
      /* Loglevel aendern. */
      case 'L':

      /* Noch was da in der Kommandozeile ? */
      if (!skipsp(&clicnt, &clipoi))
      {
        mbp = getmbp();
#ifdef SPEECH
        putprintf(mbp, speech_message(299),loglevel);
#else
        putprintf(mbp, "My LogLevel: %d\r",loglevel);
#endif
        /* und ab die Post ... */
        prompt(mbp);
        seteom(mbp);
        return;
      }

      /* Loglevel lesen */
      NewLogLevel = nxtnum(&clicnt, &clipoi);

      if (  (NewLogLevel < 0)
          ||(NewLogLevel > 4))
      {
#ifdef SPEECH
         putmsg(speech_message(300));
#else
         putmsg("errors: Log level worth from 0 to 4!\r");
#endif
         return;
      }

      /* Wert uebernehmen */
      loglevel = NewLogLevel;

      /* und ab die Post ... */
      mbp = getmbp();
      prompt(mbp);
      seteom(mbp);
      return;
#endif /* AXIPR_UDP */

#ifdef AXIPR_HTML
      /* htmlstatistik aendern. */
      case 'H':

      /* Noch was da in der Kommandozeile ? */
      if (!skipsp(&clicnt, &clipoi))
      {
        mbp = getmbp();
#ifdef SPEECH
        putprintf(mbp, speech_message(347),HtmlStat);
#else
        putprintf(mbp, "HTML-Statistic is %d\r", HtmlStat);
#endif
        /* und ab die Post ... */
        prompt(mbp);
        seteom(mbp);
        return;
      }

      /* Loglevel lesen */
      NewHtmlStat = nxtnum(&clicnt, &clipoi);

      if (  (NewHtmlStat < 0)
          ||(NewHtmlStat > 1))
      {
#ifdef SPEECH
         putmsg(speech_message(348));
#else
         putmsg("Error: HTML-Statistic worth from 0 to 1!\r\r");
#endif
         return;
      }

      /* Wert uebernehmen */
      HtmlStat = NewHtmlStat;

      /* und ab die Post ... */
      mbp = getmbp();
      prompt(mbp);
      seteom(mbp);
      return;
#endif

      /* Unbekannte Kommandos */
      default:
        putmsg(inv_par);
        return;
    }
  }
  }

  mbp = putals("AXIP-Routes:\rCall-------IP----------------Mode--Port--Timeout--IP/Hostname----\r");

  /* Alle Routingtabelleneintraege durchgehen und ausgeben */
  for (i = 0; i < route_tbl_top; ++i)
    show_rt_entry(route_tbl[i], mbp);

  /* Default-Route ausgeben wenn vorhanden */
  if (default_route.callsign[0] == 1)
    show_rt_entry(default_route, mbp);

  putprintf(mbp, "-----------------------------------------------------------------\r");

  /* Eigenen UDP-Port ausgeben */
  putprintf(mbp, "UDP-port (%u): ", ntohs(my_udp));
  putprintf(mbp, "%s", (fd_udp != -1 ? "active" : "not active"));

  /* IP-Status ausgeben */
  putprintf(mbp, ", IP-protocol (family %u): ", IPPROTO_AX25);
  putprintf(mbp, "%s", (fd_ip != -1 ? "active" : "not active"));

  /* Timeout des dynamischen Routenlerners ausgeben */
  putprintf(mbp, "\rTimeout for dynamically learned routes is %u seconds.\r", uDynTimeout);

  /* Hostname -> IP-Adresse Aktualisierungsintervall ausgeben */
  putprintf(mbp, "Hostname-to-IP-adress conversion ");

  if (uNamesUpdate != 0)
    putprintf(mbp, "occurs every %u seconds.\r", uNamesUpdate / 100);
  else
    putprintf(mbp, "is disabled.\r");

#ifdef AXIPR_UDP
#ifdef SPEECH
  putprintf(mbp, speech_message(299), loglevel);
#else
  putprintf(mbp, "My LOGLevel is %u.\r", loglevel);
#endif /* SPEECH */
#endif /* AXIPR_UDP */

#ifdef AXIPR_HTML
#ifdef SPEECH
  putprintf(mbp, speech_message(347), HtmlStat);
#else
  putprintf(mbp, "My HTML-Statistic is %d.\r", HtmlStat);
#endif /* SPEECH */
#endif /* AXIPR_HTML */

  /* und ab die Post ... */
  prompt(mbp);
  seteom(mbp);
}

/* Routeneintraege fuer parms.tnb ausgeben */
void dump_ax25ip(MBHEAD* mbp)
{
  register int i;

  putstr(";\r; AX25IP Routes\r;\r", mbp);

  if (ax25ip_active == FALSE)
    return;

  if (new_parser == TRUE)
  {
    putstr("; WARNING: this section is written using the new syntax of the AXIPR-command,\r", mbp);
    putstr(";          DO NOT USE with older TNNs compiled only for old style syntax !!!\r;\r", mbp);

    /* Falls wir noch im alten Parser sind, dann umschalten */
    putprintf(mbp, "AXIPR P\r");
    /* eigenen UDP-Port ausgeben */
    putprintf(mbp, "AXIPR MYUDP %u\r", ntohs(my_udp));
    /* Timeout fuer dynamische Routen ausgeben */
    putprintf(mbp, "AXIPR TIMEOUT %u\r", uDynTimeout);
    /* Intervall fuer Hostnamen-Update ausgeben */
    putprintf(mbp, "AXIPR LOOKUP %u\r", uNamesUpdate);
  }

#ifdef AXIPR_UDP
  /* eigenen UDP-Port ausgeben */
  putprintf(mbp, "AXIPR U %u\r", ntohs(my_udp));
  /* Timeout fuer dynamische Routen ausgeben */
  putprintf(mbp, "AXIPR T %u\r", uDynTimeout);

  putprintf(mbp, "AXIPR L %d\r",loglevel);

#ifdef AXIPR_HTML
  putprintf(mbp, "AXIPR H %d\r",HtmlStat);
#endif /* AXIPR_HTML */
#endif /* AXIPR_UDP */

  /* alle Routingtabelleneintraege durchgehen und ausgeben */
  for (i = 0; i < route_tbl_top; ++i)
  {
    /* Dynamisch gelernte Routen werden inaktiv zur Information geschrieben ! */
    if (route_tbl[i].timeout != 0)
      continue;

/*    putprintf(mbp, "; ");*/

    /* Eintrag einer Route erzeugen */
    putprintf(mbp, (new_parser == FALSE ? "AXIPR R + %s " : "AXIPR ADD %s ")
                 , call_to_a(&route_tbl[i].callsign[0]));

    /* Den Hostnamen ausgeben */
    putprintf(mbp, "%s", route_tbl[i].hostname);

    /* Mode und ggf. Port ausgeben wenn UDP */
    if (route_tbl[i].udp_port != 0)
    {
      putstr((new_parser == FALSE ? " U " : " UDP "), mbp);
      putnum(ntohs(route_tbl[i].udp_port), mbp);
    }

    putstr("\r", mbp);
  }

  /* Die Defaultroute ausgeben wenn gesetzt */
  if (default_route.callsign[0] == 1)
  {
    /* Kommandokopf schreiben */
    putstr((new_parser == FALSE ? "AXIPR D + " : "AXIPR ADD DEFAULT "), mbp);

    /* Den Hostnamen ausgeben */
    putprintf(mbp, "%s", default_route.hostname);

    /* Mode und ggf. Port ausgeben wenn UDP */
    if (default_route.udp_port != 0)
    {
      putstr((new_parser == FALSE ? " U " : " UDP "), mbp);
      putnum(ntohs(default_route.udp_port), mbp);
    }
    putstr("\r", mbp);
  }
}

/************************************************************************/
/* AX25IP Initialisierung                                               */
/************************************************************************/
BOOLEAN ax25ip_l1init(int l2port)
{
  if (ax25ip_active == TRUE)
    return(TRUE);

  /* Socketstrukturen initialisieren */
  memset((char*)&to, 0, sizeof(to));
  memset((char*)&from, 0, sizeof(from));

  ax25ip_port = l2port;
  l1pp = &l1port[l1ptab[l2port]];
  l2flp = (LHEAD *) &txl2fl[l2port];

  /* Routingtabelle initialisieren */
  route_init();

  /* Konfiguration einlesen */
  if (config_read() == FALSE)
    return(FALSE);

  /* Notwendige Sockets anlegen */
#ifndef AXIPR_UDP
  ax25ip_check_up();
#endif

  /* Wir sind fertig */
  ax25ip_active = TRUE;
  return(TRUE);
}

/* Einen Filedescriptor fuer IP anlegen */
int setup_ip(void)
{
  /* Temporaerer Filedescriptor */
  int tmp_fd = -1;

  /* Adressstruktur loeschen */
  memset((char*)&ipbind, 0, sizeof(ipbind));

  /* Rohen IP-Socket anlegen */
  if ((tmp_fd = socket(AF_INET, SOCK_RAW, IPPROTO_AX25)) < 0)
  {
    xprintf("AX25IP : cannot create ip raw-socket: %s\n", strerror(errno));
    LOGL2("AX25IP : cannot create ip raw-socket: %s\n", strerror(errno));
    return(-1);
  }

  /* Nonblocking-IO */
  if (fcntl(tmp_fd, F_SETFL, FNDELAY) < 0)
  {
    xprintf("AX25IP : cannot set non-blocking I/O on ip raw-socket\n");
    LOGL2("AX25IP : cannot set non-blocking I/O on ip raw-socket\n");
    close(tmp_fd);
    return(-1);
  }

  /* Adressstruktur mit notwendigen Werten fuellen */
  ipbind.sin_family = AF_INET;
  ipbind.sin_addr.s_addr = htonl(INADDR_ANY);

  return (tmp_fd);
}

/* Einen Filedescriptor fuer UDP anlegen */
/* Es muss der UDP-Port angegeben werden, auf dem wir hoeren wollen */
/* (Achtung: UDP-Port in Network-Byteorder) */
int setup_udp(unsigned short udp_port)
{
  /* Temporaerer Filedescriptor */
  int tmp_fd = -1;

  /* Port-Check */
  if (udp_port == 0)
    return(-1);

  /* Adressstruktur loeschen */
  memset((char*)&udpbind, 0, sizeof(udpbind));

  /* UDP-Socket anlegen */
  if ((tmp_fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
  {
    xprintf("AX25IP : cannot create socket: %s\n", strerror(errno));
    LOGL2("AX25IP : cannot create socket: %s\n", strerror(errno));
    return(-1);
  }

  /* Nonblocking-IO */
  if (fcntl(tmp_fd, F_SETFL, FNDELAY) < 0)
  {
    xprintf("AX25IP : cannot set non-blocking I/O on udp socket\n");
    LOGL2("AX25IP : cannot set non-blocking I/O on udp socket\n");
    close(tmp_fd);
    return(-1);
  }

  /* Socket-Optionen fuer UDP einstellen */
  /* SO_KEEPALIVE einschalten */
  if (sockopt_keepalive == TRUE)
  {
    int iFlag = 1;

    if (setsockopt(tmp_fd, SOL_SOCKET, SO_KEEPALIVE, (const char *)&iFlag, sizeof(int)) < 0)
    {
      xprintf("AX25IP : cannot set SO_KEEPALIVE for udp : %s\n", strerror(errno));
      LOGL2("AX25IP : cannot set SO_KEEPALIVE for udp : %s\n", strerror(errno));
    }
  }

  /* Socket auf max. Durchsatz */
  if (sockopt_throughput == TRUE)
  {
    int iFlag = IPTOS_THROUGHPUT;

    if (setsockopt(tmp_fd, IPPROTO_IP, IP_TOS, (const char *)&iFlag, sizeof(IPTOS_THROUGHPUT)) < 0)
    {
      xprintf("AX25IP : cannot set IPTOS_THROUGHPUT for udp : %s\n", strerror(errno));
      LOGL2("AX25IP : cannot set IPTOS_THROUGHPUT for udp : %s\n", strerror(errno));
    }
  }

  /* Adressstruktur fuellen */
  udpbind.sin_addr.s_addr = htonl(INADDR_ANY);
  udpbind.sin_port = udp_port;
  udpbind.sin_family = AF_INET;

  /* Adresse und Port binden */
  if (bind(tmp_fd, (struct sockaddr *)&udpbind, sizeof(udpbind)) < 0)
  {
    xprintf("AX25IP : cannot bind udp socket: %s\n", strerror(errno));
    LOGL2("AX25IP : cannot bind udp socket: %s\n", strerror(errno));
    close(tmp_fd);
    return(-1);
  }

  return(tmp_fd);
}

/* Pruefen, ob schon Sockets angelegt sind, wenn nicht, dann notwendige */
/* Sockets oeffnen */
void ax25ip_check_up(void)
{
  /* IP notwendig ? */
  if ((count_routes(FALSE) != 0) && (fd_ip == -1))
    fd_ip = setup_ip(); /* IP starten */

  /* UDP notwendig ? */
  if ((count_routes(TRUE) != 0) && (fd_udp == -1))
    fd_udp = setup_udp(my_udp); /* UDP starten */

#ifdef AXIPR_UDP
  if (l1pp != NULL)
#endif
    /* Markieren, ob IP oder UDP laufen */
    l1pp->kisslink = max(fd_ip, fd_udp);
}

/* Pruefen, ob Sockets angelegt sind und diese noch benoetigt werden. */
/* Falls nicht, Sockets schliessen */
void ax25ip_check_down(void)
{
  /* IP notwendig ? */
  if ((count_routes(FALSE) == 0) && (fd_ip != -1))
  {
    close(fd_ip);
    fd_ip = -1;
  }

  /* UDP notwendig ? */
  if ((count_routes(TRUE) == 0) && (fd_udp != -1))
  {
    close(fd_udp);
    fd_udp = -1;
  }

  /* Gibt es einen Eintrag. */
  if (l1pp)
    /* Markieren, ob IP oder UDP laufen */
    l1pp->kisslink = max(fd_ip, fd_udp);
}

/*************************************************************************/
/* AX25IP Pakete empfangen                                               */
/*************************************************************************/
void ax25ip_recv(void)
{
  struct       iphdr *ipptr;
  struct       timeval tv;
  int          l = 0;
  int          iDescriptorsReady = 0;
  register int i = 0;
  register int max_fd = -1;
  socklen_t    fromlen = sizeof(from);
  int          hdr_len = 0;
  UBYTE        buf[MAX_FRAME+1];
  UBYTE        *bufptr;
  MBHEAD       *rxfhd;
  BOOLEAN      UDP_Frame = FALSE;
  fd_set       rmask;

#ifdef AXIPR_UDP
  if (LookAX25IP)
    return;
#endif /* AX25IP_UDP */

  FD_ZERO(&rmask);

  /* IP-Filedescriptor eintragen */
  if (fd_ip != -1)
  {
    FD_SET((unsigned int)fd_ip, &rmask);
    if (fd_ip > max_fd - 1)
      max_fd = fd_ip + 1;
  }

  /* UDP-Filedescriptor eintragen */
  if (fd_udp != -1)
  {
    FD_SET((unsigned int)fd_udp, &rmask);
    if (fd_udp > max_fd - 1)
      max_fd = fd_udp + 1;
  }

  /* ist was aktiv ? */
  if (max_fd == -1)
    return;

  tv.tv_usec = 0;
  tv.tv_sec = 0;

  iDescriptorsReady = select(max_fd, &rmask, NULL, NULL, &tv);

  /* nix da */
  if (iDescriptorsReady == 0)
    return;
 
  /* Fehler */
  if (iDescriptorsReady == -1)
  {
    LOGL2("select()-Error %i: %s", errno, strerror(errno));
    return;
  }

  /* RX fuer IP */
  if ((fd_ip != -1) && (FD_ISSET(fd_ip, &rmask)))
  {
    l = recvfrom(fd_ip, (UBYTE *)(bufptr = buf), MAX_FRAME, 0, (struct sockaddr *) &from, &fromlen);

    if (l > 0)
    {
      if (l > (signed)sizeof(struct iphdr*))
      {
        ipptr = (struct iphdr*)buf;
        hdr_len = 20;

        if (!ok_crc(buf + hdr_len, l - hdr_len))
        { /* stimmt die CRC ? */
          LOGL2("IP-RX: CRC-Error, frame dropped");
          return; /* Fehler */
        }
      }
      else
      {
        LOGL2("IP-RX: frame too short, frame dropped");
        return; /* Fehler */
      }
    }
    i = hdr_len;
  }

  /* RX fuer UDP, aber nur, wenn nicht schon TCP was empfangen hat */
  if ((fd_udp != -1) && (FD_ISSET(fd_udp, &rmask) && (l == 0)))
  {
    l = recvfrom(fd_udp, (UBYTE *)(bufptr = buf), MAX_FRAME, 0, (struct sockaddr *) &from, &fromlen);

    if (l > 0)
    {
      if (!ok_crc(buf, l))
      { /* stimmt die CRC ? */
        LOGL2("UDP-RX: CRC-Error, frame dropped");
        return; /* Fehler */
      }
    }
    UDP_Frame = TRUE;
  }

  /* Ab hier gemeinsame Behandlung */
  /* Erst mal gucken, ob was gekommen ist */
  if (l == 0)
    return; /* nix da */

  l -= 2; /* CRC abschneiden */

  if (l < 15)
  {
    LOGL2("AX25IP: RX frame dropped, length wrong, frame is too short !");
    return;
  }

  /* Hier angekommen haben wir (hoffentlich) ein gueltiges Frame. Die */
  /* weitere Behandlung ist fuer IP und UDP nahezu identisch.         */

  /* Frame in Buffer umkopieren und Empfangsport eintragen */
  rxfhd = cpyflatmb((char*)&buf[i], l - i);
  rxfhd->l2port = ax25ip_port;

  /* Frame in die L2-RX-Kette einhaengen */
  relink((LEHEAD *)rxfhd, (LEHEAD *)rxfl.tail);

  /* Jetzt noch das Frame an den Routenlerner zur Analyse */
  if (route_canlearn(rxfhd))
  {
    /* Frame kann zum Lernen benutzt werden */
    /* Absenderrufzeichen ermitteln und konvertieren      */
    /* Wir brauchen hier nicht den eigentlichen Absender, */
    /* sondern den, der als letztes gedigipeated hat.     */
    struct in_addr ipaddr;
#ifndef AXIPR_UDP
    struct route_table_entry* rtptr;
#endif /* AXIPR_UDP */
    unsigned char srccall[L2IDLEN + 1];

    cpyid((char *)srccall, dheardcall(rxfhdr));

    /* Call linksschieben */
    for (i = 0; i < L2CALEN; ++i)
      srccall[i] = srccall[i] << 1;

    /* Gibt es schon eine IP fuer dieses Call, dann kann sich die IP  */
    /* geaendert haben. Falls ja und es sich um einen dynamischen Eintrag */
    /* handelt, dann loeschen und neuen Eintrag anlegen. */
#ifndef AXIPR_UDP
    if ((rtptr = call_to_ip(srccall)) != NULL)
    {
      if ((   (rtptr->ip_addr != from.sin_addr.s_addr)
           || (rtptr->udp_port != (UDP_Frame == TRUE ? ntohs(from.sin_port) : 0)))
           && (rtptr->timeout != 0))
        route_del(srccall); /* Route loeschen */
      else
      {
        if (rtptr != &default_route)
          return;             /* Adresse unveraendert */
      }
    }
#endif /* AXIPR_UDP */

    ipaddr.s_addr = from.sin_addr.s_addr;

#ifndef AXIPR_UDP
    /* Route eintragen */
    route_add((unsigned char*)inet_ntoa(ipaddr), NULL, srccall,
              (UDP_Frame == TRUE ? ntohs(from.sin_port) : 0), FALSE, uDynTimeout);
#else
    route_analyse(ipaddr, srccall,
                  (UDP_Frame == TRUE ? ntohs(from.sin_port) : 0));
  }
#endif /* AXIPR_UDP */
}

/*************************************************************************/
/* AX25IP Pakete senden                                                  */
/*************************************************************************/
void ax25ip_send(void)
{
  unsigned char  buf[MAX_FRAME];
  int            len = 0;
  int            fd = -1;
  MBHEAD        *txfhdl;

  struct route_table_entry *rt;
  struct hostent *hstent;

  unsigned char *call;

  if (kick[ax25ip_port]) /* haben wir was zu senden ? */
  {
    /* Sicherheitsabfrage, da kick[] auch manipuliert werden kann */
    if (l2flp->head == l2flp)
    {
      kick[ax25ip_port] = FALSE;
      return;
    }

    ulink((LEHEAD *)(txfhdl = (MBHEAD *) l2flp->head));/*Zeiger holen*/

    /* Daten aus den internen Puffern in einen linearen Puffer uebertragen */
    len = cpymbflat((char*)&buf[0], txfhdl);

    relink((LEHEAD *)txfhdl,          /* als gesendet betrachten und in */
           (LEHEAD *)stfl.tail);      /* die gesendet Liste umhaengen   */

    kick[ax25ip_port] = ((LHEAD *)l2flp->head != l2flp);

    /* Naechstes Call der Digipeaterkette holen */
    call = next_addr(buf);

    /* IP-Adresse des naechsten Digipeaters ermitteln */
    rt = call_to_ip(call);

    if (rt == NULL) /* wir kennen das Ziel nicht */
    {
#ifdef AXIPR_UDP
       if ((rt = search_route(call)) == NULL)
       {
#endif
         LOGL2("no route for %s, can't send", call_to_a(call));
         return;
       }
#ifdef AXIPR_UDP
    }
#endif

    /* Ein Host, zu dem bisher keine IP-Adresse aufgeloest werden konnte */
    if (rt->ip_addr == 0L)
    {
      /* Versuchen, jetzt eine IP-Adresse zu bestimmen */
      if ((hstent = gethostbyname((char*)rt->hostname)) != NULL)
         memcpy((unsigned char*)&rt->hostname, (unsigned char *)&hstent->h_addr_list[0][0], 4);
    }

    /* Checksumme anhaengen */
    add_ax25crc(buf, len);
    /* CRC-Bytes sind dazugekommen */
    len += 2;
    /* Ziel-IP-Adresse in Sendestruktur eintragen */
    memcpy((char *) &to.sin_addr, (char *)&rt->ip_addr, 4);
    /* Ziel-Port nur bei UDP eintragen */
    if (rt->udp_port != 0)
      memcpy((char *) &to.sin_port, (char *)&rt->udp_port, 2);
    else
      to.sin_port = 0;  /* bei IP kein Port */

    to.sin_family = AF_INET;

    /* Unterscheidung IP oder UDP */
    if (to.sin_port == 0)
      fd = fd_ip;
    else
      fd = fd_udp;

    if (fd == -1)
    {
      LOGL2("error: no descriptor, can't send !!!");
      return;
    }

    if (sendto(fd, (char *)buf, len, 0, (struct sockaddr *)&to, sizeof(to)) < 0)
      LOGL2("error: sendto failed, fd=%u error=%s", fd, strerror(errno));
  }
}

/*************************************************************************/
/* AX25IP Deinitialisierung, Port und Sockets schliessen                 */
/*************************************************************************/
void ax25ip_l1exit(void)
{
  /* Nur wenn aktiv */
  if (ax25ip_active == FALSE)
    return;

  /* IP-Socket schliessen wenn offen */
  if (fd_ip != -1)
  {
    close(fd_ip);
    fd_ip = -1;
  }

  /* UDP-Socket schliessen wenn offen */
  if (fd_udp != -1)
  {
    close(fd_udp);
    fd_udp = -1;
  }

  /* AX25IP nicht mehr aktiv */
  ax25ip_active = FALSE;

  /* Gibt es einen Eintrag. */
  if (l1pp)
    l1pp->kisslink = -1;
}

void ax25ip_l1ctl(int req, int port)
{
  /* nur eigene Ports bearbeiten */
  if ((ax25ip_active == FALSE) || (kissmode(port) != KISS_AXIP))
    return;

  switch (req)
  {
    /* Testpattern auf dem Port senden (wird unterdrueckt) */
    case L1CTST : testflag[port] = FALSE;
                  kick[port] = FALSE;
                  break;

    default : break;
  }
}

void ax25ip_hwstr(int port, MBHEAD *mbp)
{
#ifdef AXIPR_UDP
    putstr(" ", mbp);
    /* My UDP-Port ausgeben. */
    putprintf(mbp,"%u", ntohs(my_udp));
#endif
}

BOOLEAN ax25ip_dcd(int port)
{
  return (FALSE);
}


/* routing.c    Routing table manipulation routines
 *
 * Copyright 1991, Michael Westerhof, Sun Microsystems, Inc.
 * This software may be freely used, distributed, or modified, providing
 * this header is not removed.
 *

 * Modified for use in TNN by DG1KWA & DG9OBU

 */



/* Initialize the routing table */
void route_init(void)
{
  /* Defaultroute initialisieren */
  memset(&default_route, 0 , sizeof(struct route_table_entry));

  /* Routen initialisieren */
  memset(&route_tbl, 0, (TABLE_SIZE * sizeof(struct route_table_entry)));
}

/* Add a new route entry */
#ifndef AXIPR_UDP
static void route_add(unsigned char* hostname, struct hostent *hstent, unsigned char *call, int udpport,
                      int default_rt, int timeout)
#else
static BOOLEAN route_add(unsigned char *host, struct hostent *hstent, unsigned char *call, int udpport,
                         int default_rt, int timeout, char *hostname
#ifdef AXIPR_HTML
                      , char *timeset, UWORD protokoll
#endif /* AXIPR_HTML */
                      )
#endif /* AXIPR_UDP */
{
   register unsigned int i = 0;
   struct route_table_entry *rtptr;
   struct hostent *hst;

   /* Eintrag soll Defaultroute sein */
   if (default_rt)
   {
      for (i = 1; i < 7 ;++i)
        default_route.callsign[i] = 0;

      if (hstent != NULL)
        memcpy(&default_route.ip_addr, (unsigned char *)&hstent->h_addr_list[0][0], 4);
      else
        memset(&default_route.ip_addr, 0, 4);

      default_route.udp_port = htons((unsigned short)udpport);
      default_route.timeout = 0;                /* Defaultroute ohne Timeout */

      if (hostname != NULL)
        strncpy((char*)&default_route.hostname, (char*)hostname, HNLEN);

      default_route.callsign[0] = 1;            /* mark valid */

      /* Socket ggf. installieren */
      ax25ip_check_up();

#ifndef AXIPR_UDP
      return;
#else
      return(TRUE);
#endif /* AXIPR_UDP */
   }

   /* Normale Routen nur wenn Call vorhanden */
   if (call == NULL)
#ifndef AXIPR_UDP
     return;
#else
     return(FALSE);
#endif /* AXIPR_UDP */

   /* Check auf doppelte Eintraege, Eintrag wird nur angenommen wenn */
   /* es noch noch keinen Eintrag unter diesem Call gibt. */
   if (((rtptr = call_to_ip(call)) != NULL) && (rtptr->ip_addr != default_route.ip_addr))
   {
#ifndef AXIPR_UDP
     putmsg("A route to this callsign is already set up, delete it first.\r");
     return;
#else
#ifndef AXIPR_HTML
     UpdateRoute(call, udpport, timeout, hostname);
#else
     UpdateRoute(call, udpport, timeout, hostname, timeset, protokoll);
#endif /* AXIPR_HTML */
     return(FALSE);
#endif /* AXIPR_UDP */
   }

   /* Passt noch ein Eintrag in die Tabelle ? */
   if (route_tbl_top >= TABLE_SIZE)
   {
     xprintf("Routing table is full; entry ignored.\n");
     LOGL2("Routing table is full; entry ignored");
     putmsg("Routing table is full, entry ignored\r");
#ifndef AXIPR_UDP
     return;
#else
     return(FALSE);
#endif /* AXIPR_UDP */
   }

   /* Call eintragen */
   for (i = 0; i < 6; ++i)
     route_tbl[route_tbl_top].callsign[i] = call[i] & 0xfe;

   /* SSID eintragen */
   route_tbl[route_tbl_top].callsign[6] = (call[6] & 0x1e) | 0x60;

   if (hstent != NULL && hstent->h_length != 0)
   {
     memcpy((unsigned char*)&route_tbl[route_tbl_top].ip_addr, (unsigned char *)&hstent->h_addr_list[0][0], 4);
   }
   else
     memset(&route_tbl[route_tbl_top].ip_addr, 0, sizeof(route_tbl[route_tbl_top].ip_addr));

   route_tbl[route_tbl_top].udp_port = htons((unsigned short)udpport);
   route_tbl[route_tbl_top].timeout = timeout;

   if (hostname != NULL)
   {
     memcpy(&route_tbl[route_tbl_top].hostname, hostname, strlen((char*)hostname));
     /* Keine Adressaufloesung bisher, dann probieren wir es noch einmal */
     if (hstent == NULL)
     {
       if ((hst = gethostbyname((char*)hostname)) != NULL)
         memcpy((unsigned char*)&route_tbl[route_tbl_top].ip_addr, &hst->h_addr_list[0][0], 4);
     }
   }

#ifdef AXIPR_UDP
   /* Original UDP-Port eintragen. */
   route_tbl[route_tbl_top].org_udp_port = htons((UWORD)udpport);
#endif

#ifdef AXIPR_HTML
   /* Hostname eintragen. */
   (void)memcpy(route_tbl[route_tbl_top].timeset, timeset, 17);
   /* Protokoll eintragen. */
   route_tbl[route_tbl_top].protokoll = P_USER;
   /* Route als offline markieren. */
   route_tbl[route_tbl_top].online = FALSE;
#endif

   /* Ein Routeneintrag mehr */
   route_tbl_top++;

   /* Socket ggf. installieren */
   ax25ip_check_up();

#ifndef AXIPR_UDP
   return;
#else
   return(TRUE);
#endif /* AXIPR_UDP */
}

/*
 * Return an IP address and port number given a callsign.
 * We return a pointer to the route structure for this callsign
 * or a NULL-pointer if no route was found.
 */

struct route_table_entry* call_to_ip(unsigned char *call)
{
   register int i;
   unsigned char mycall[7];

   /* Leere Calls koennen nicht verarbeitet werden */
   if (call == NULL)
     return (NULL);

   /* Call-Bytes kopieren, letztes Bit abschneiden (entspricht >> 1 & 7F) */
   for (i = 0; i < 6; ++i)
     mycall[i] = call[i] & 0xfe;

   /* SSID kopieren */
   mycall[6] = (call[6] & 0x1e) | 0x60;

   /* Routingtabelle durchsuchen */
   for (i = 0; i < route_tbl_top; ++i)
   {
     if (addrmatch(mycall , route_tbl[i].callsign))
     {
       /* Aktivitaet auf diesem Eintrag, dann Timeout erneuern */
       if (route_tbl[i].timeout)
         route_tbl[i].timeout = uDynTimeout;

       return (&route_tbl[i]); /* Eintrag gefunden */
     }
   }

   /* Kein Routingeintrag vorhanden, Default-Route melden wenn vorhanden */
   if (default_route.callsign[0])
      return (&default_route);

   /* Kein Routingeintrag gefunden, keine Defaultroute */
   return (NULL);
}

/*
 * tack on the CRC for the frame.  Note we assume the buffer is long
 * enough to have the two bytes tacked on.
 */
void add_ax25crc(unsigned char *buf, int l)
{
   unsigned short int u = compute_crc(buf, l);

   buf[l] = u & 0xff;              /* lsb first */
   buf[l + 1] = (u >> 8) & 0xff;   /* msb next */
}


/*
 **********************************************************************
 * The following code was taken from Appendix B of RFC 1171
 * (Point-to-Point Protocol)
 *
 * The RFC credits the following sources for this implementation:
 *
 *   Perez, "Byte-wise CRC Calculations", IEEE Micro, June, 1983.
 *
 *   Morse, G., "Calculating CRC's by Bits and Bytes", Byte,
 *   September 1986.
 *
 *   LeVan, J., "A Fast CRC", Byte, November 1987.
 *
 *
 * The HDLC polynomial: x**0 + x**5 + x**12 + x**16
 */

/*
 * u16 represents an unsigned 16-bit number.  Adjust the typedef for
 * your hardware.
 */
/*typedef unsigned short u16;*/


/*
 * FCS lookup table as calculated by the table generator in section 2.
 */
static UWORD fcstab[256] = {
   0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
   0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
   0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
   0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
   0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
   0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
   0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
   0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
   0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
   0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
   0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,
   0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
   0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
   0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
   0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
   0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
   0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
   0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
   0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
   0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,
   0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
   0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
   0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
   0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
   0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
   0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,
   0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
   0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
   0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
   0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
   0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
   0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78
};

#define PPPINITFCS      0xffff  /* Initial FCS value */
#define PPPGOODFCS      0xf0b8  /* Good final FCS value */

/*
 * Calculate a new fcs given the current fcs and the new data.
 */
UWORD pppfcs(register UWORD fcs, register unsigned char *cp, register int len)
{
    while (len--)
       fcs = (fcs >> 8) ^ fcstab[(fcs ^ *cp++) & 0xff];

    return(fcs);
}

/*
 * End code from Appendix B of RFC 1171
 **********************************************************************
 */

/*
 *  The following routines are simply convenience routines...
 *  I'll merge them into the mainline code when suitably debugged
 */

/* Return the computed CRC */
UWORD compute_crc(unsigned char *buf, int l)
{
   UWORD fcs = (pppfcs(PPPINITFCS, buf, l) ^ 0xFFFF);

   return (fcs);
}

/* Return true if the CRC is correct */
BOOLEAN ok_crc(unsigned char *buf, int l)
{
   UWORD fcs = pppfcs(PPPINITFCS, buf, l);

   return (fcs == PPPGOODFCS);
}

/* return true if the addresses supplied match */
int addrmatch(unsigned char *a, unsigned char *b)
{
   if ((*a == '\0') || (*b == '\0')) return(0);

   if ((*a++ ^ *b++) & 0xfe) return(0);   /* "K" */
   if ((*a++ ^ *b++) & 0xfe) return(0);   /* "A" */
   if ((*a++ ^ *b++) & 0xfe) return(0);   /* "9" */
   if ((*a++ ^ *b++) & 0xfe) return(0);   /* "W" */
   if ((*a++ ^ *b++) & 0xfe) return(0);   /* "S" */
   if ((*a++ ^ *b++) & 0xfe) return(0);   /* "B" */
   if ((*a++ ^ *b++) & 0x1e) return(0);   /* ssid */
   return(1);
}

/* return pointer to the next station to get this packet */
unsigned char *next_addr(unsigned char *f)
{
   unsigned char *a;

   /* If no digis, return the destination address */
   if (NO_DIGIS(f)) return(f);

   /* check each digi field.  The first one that hasn't seen it is the one */
   a = f + 7;
   do {
      a += 7;
      if (NOTREPEATED(a)) return(a);
   } while (NOT_LAST(a));

/* all the digis have seen it.  return the destination address */
   return(f);
}

/* Open and read the config file */
int config_read(void)
{
   FILE *cf;
   char buf[256], cbuf[256];
   int errflag = 0;
   int e = 0;
   int lineno = 0;
   char cfgfile[256];

   strcpy(cfgfile, confpath);
   strcat(cfgfile, CONFIG_FILE);

   if ((cf = fopen(cfgfile, "r")) == NULL)
   {
#ifdef INIPATH
     strcpy(cfgfile, INIPATH);
     strcat(cfgfile, CONFIG_FILE);

     if ((cf = fopen(cfgfile, "r")) == NULL)
     {
#endif
#ifdef AXIPR_UDP
       /* AX25IP.CFG ist nicht mehr noetig,   */
       /* kann aber weiterhin benutzt werden. */
       return(TRUE);
#endif
       xprintf("AX25IP: Config file %s not found or could not be opened\n", CONFIG_FILE);
       return(FALSE);
#ifdef INIPATH
     }
#endif
   }

   while (fgets(buf, 255, cf) != NULL)
   {
     (void)strcpy(cbuf, buf);
     ++lineno;

     if ((e = parse_line(buf)) < 0)
     {
       xprintf("Config error at line %d: ", lineno);

       switch (e)
       {
         case -1 : xprintf("Missing argument\n"); break;
         case -2 : xprintf("Bad callsign format\n"); break;
         case -3 : xprintf("Bad option - on/off\n"); break;
         case -4 : xprintf("Bad option - tnc/digi\n"); break;
         case -5 : xprintf("Host not known\n"); break;
         case -6 : xprintf("Unknown command\n"); break;
         case -7 : xprintf("Text string too long\n"); break;
         case -8 : xprintf("Bad option - every/after\n"); break;
         case -9 : xprintf("Bad option - ip/udp\n"); break;

         default: xprintf("Unknown error\n"); break;
       }
       xprintf("%s", cbuf);
       ++errflag;
     }
   }

   if (errflag)
     exit(1);

   if (mode == NO_MODE)
   {
     xprintf("Must specify ip and/or udp sockets\n");
     return(FALSE);
   }

   fclose(cf);

   return(TRUE);
}

/* Process each line from the config file.  The return value is encoded. */
int parse_line(char *buf)
{
   char *p;
   char *q;
   char hostname[HNLEN];
   unsigned char tcall[7];
   struct hostent hstent;
   struct hostent *he;
   int i = 0;
   int uport = 0;
   int dfalt = 0;
#ifdef AXIPR_HTML
   char *timeset = set_time();
#endif

   p = strtok(buf, " \t\n\r");

   memset(&hstent, 0, sizeof(hstent));

   /* Leere Zeilen */
   if (p == NULL)
     return(0);

   /* Kommentarzeilen nicht auswerten */
   if (*p == '#')
     return(0);

   if (strcmp(p, "socket") == 0)
   {
     q = strtok(NULL, " \t\n\r");

     if (q == NULL)
       return(-1);

     if (strcmp(q, "ip") == 0)
       mode = IP_MODE;
     else if (strcmp(q, "udp") == 0)
     {
        mode = UDP_MODE;
        my_udp = htons(DEFAULT_UDP_PORT);

        /* Bei UDP optionale Portnummer lesen */
        q = strtok(NULL, " \t\n\r");

        /* Nummer war vorhanden, auswerten */
        if (q != NULL)
        {
           i = atoi(q);
           /* Nummer ist gueltig, uebernehmen */
           if (i > 0)
             my_udp = htons((unsigned short)i);
        }
     }
     else
       return(-9);

     return(0);
   }
   else if (strcmp(p, "socketoption") == 0)
     {
       q = strtok(NULL, " \t\n\r");

       /* Pruefung auf leere Zeile */
       if (q == NULL)
         return(-1);

       if (strcmp(q, "SO_KEEPALIVE") == 0)
       {
          sockopt_keepalive = TRUE;
       }
       else if (strcmp(q, "IPTOS_THROUGHPUT") == 0)
       {
          sockopt_throughput = TRUE;
       }
       else return(-9);

     return(0);

    } else if (strcmp(p, "loglevel") == 0) {
      q = strtok(NULL, " \t\n\r");
      if (q == NULL) return(-1);
      loglevel = atoi(q);
      return(0);

   } else if (strcmp(p, "route") == 0) {
      uport = 0;
      dfalt = 0;

      q = strtok(NULL, " \t\n\r");

      /* Pruefung auf leere Zeile */
      if (q == NULL)
        return(-1);

      /* Pruefung auf default-Route */
      if (strcmp(q, "default") == 0)
        dfalt = 1;
      else
      {
         /* Ziel der Route pruefen */
         if (a_to_call(q, tcall) != 0)
           return(-2);
      }

      /* Hostnamen holen, dies kann auch eine IP-Adresse sein */
      q = strtok(NULL, " \t\n\r");
      if (q == NULL)
        return(-1);

      he = gethostbyname(q);

      /* Keine gueltige IP-Adresse ? */
      if (he == NULL)
      {
        /* Da route_add() eine hostent-Struktur braucht, bauen wir ihm eine. */
        /* Den Hostname sichern und den Zeiger auf den Puffer im hostent eintragen. */
        strncpy(hostname, q, HNLEN);

         he = &hstent;
         he->h_name = hostname;
      }

      q = strtok(NULL, " \t\n\r");

      if (q != NULL)
      {
        if (strcmp(q, "udp") == 0)
        {
          uport = DEFAULT_UDP_PORT;

          q = strtok(NULL, " \t\n\r");
          if (q != NULL)
          {
            i = atoi(q);
            if (i > 0)
               uport = i;
          }
        }
      }
#ifndef AXIPR_UDP
      route_add((unsigned char*)he->h_name, he, tcall, uport, dfalt, 0);
#else
      route_add((unsigned char*)he->h_name, he, tcall, uport, dfalt, 0, (char*)he->h_name
#ifdef AXIPR_HTML
               , timeset, P_USER
#endif /* AXIPR_HTML */
               );
#endif /* AXIPR_UDP */
      return(0);
   }
   return(-999);
}


/* Convert ascii callsign to internal format */
int a_to_call(char *text, unsigned char *tcall)
{
   register size_t  i = 0;
   int           ssid = 0;
   unsigned char c;

   if (strlen(text) == 0)
     return(-1);

   for (i = 0; i < 6; ++i)
     tcall[i] = (' ' << 1);

   tcall[6] = '\0';

   for (i = 0; i < strlen(text); ++i)
   {
     c = text[i];
     if (c == '-')
     {
       ssid = atoi(&text[i + 1]);
       if (ssid > 15)
         return(-1);
       tcall[6] = (ssid << 1);
       return(0);
     }

     if (islower(c))
       c = toupper(c);

     if (i > 5)
       return(-1);

     tcall[i] = (c << 1);
   }
   return(0);
}

/* Convert internal callsign to printable format */
char *call_to_a(unsigned char *tcall)
{
   int i = 0;
   int ssid = 0;
   char *tptr;
   static char t[10];

   for (i = 0, tptr = t; i < 6; ++i)
   {
     if (tcall[i] == (' ' << 1))
       break;
     *tptr = tcall[i] >> 1;
     tptr++;
   }

   ssid = (tcall[6] >> 1) & 0x0f;
   if (ssid > 0)
   {
     *tptr = '-';
     tptr++;

     if (ssid > 9)
     {
       *tptr = '1';
       tptr++;
       ssid -= 10;
     }
     *tptr = '0' + ssid;
     tptr++;
   }

   *tptr = '\0';
   return(&t[0]);
}

void write_log(const char *format, ...)
{
  FILE *fp;
  va_list arg_ptr;
  struct tm *lt;

  fp = fopen("ax25ip.log", "a+");

  if (fp != NULL)
  {
    lt = localtime(&sys_time);
    fprintf(fp, "%16.16s:", ctime(&sys_time));
    va_start(arg_ptr, format);
    vfprintf(fp, format, arg_ptr);
    va_end(arg_ptr);
    fprintf(fp, "\n");
    fclose(fp);
  }
  else
    loglevel = 0;   /* Logging aus da Fehler mit der Logdatei */
}

#ifdef AXIPR_UDP
/* AX25IP-Frame untersuchen, wenn Aenderungen     */
/* z.B. UDP-Port, IP-Adresse in TBL Aktualisieren.*/
void route_analyse(struct in_addr ip, unsigned char *call, int uport)
{
  register int   i;
  char           mycall[7];

#ifdef AXIPR_HTML
  char        *timeset = set_time();
#endif /* AXIPR_HTML */

  /* Leere Calls koennen nicht verarbeitet werden */
  if (call[0] == FALSE)
    return;

   /* Call-Bytes kopieren, letztes Bit abschneiden (entspricht >> 1 & 7F) */
   for (i = 0; i < 6; ++i)
     mycall[i] = call[i] & 0xfe;

   /* SSID kopieren */
   mycall[6] = (call[6] & 0x1e) | 0x60;

   /* Routingtabelle durchsuchen */
   for (i = 0; i < route_tbl_top; ++i)
   {
      if (addrmatch((unsigned char *)mycall , route_tbl[i].callsign))
      {
        /* Bei Aenderung uebernehmen */
        if (memcmp(&ip, &route_tbl[i].ip_addr, 4) != 0)
          /* IP-Adresse hat sich geaendert */
          memcpy(&route_tbl[i].ip_addr, &ip, 4);

        /* UDP-Port unterschiedlich? */
        if (route_tbl[i].udp_port != htons((UWORD)uport))
           /* UDP-Port updaten. */
           route_tbl[i].udp_port = htons((UWORD)uport);

#ifdef AXIPR_HTML
        /* Route ggf. auf online setzen. */
        if (!route_tbl[i].online)
          route_tbl[i].online = TRUE;
#endif
        /* Aktivitaet auf diesem Eintrag, dann Timeout erneuern */
        if (route_tbl[i].timeout)
          route_tbl[i].timeout = uDynTimeout;

        /* Aktualisierung beendet. */
        return;
      }
   }

#ifndef AXIPR_HTML
   /* Auto-Route eintragen */
   route_add((unsigned char*)inet_ntoa(ip), NULL, call,uport, FALSE, uDynTimeout, (char*)inet_ntoa(ip));
#else
   /* Auto-Route eintragen */
   route_add((unsigned char*)inet_ntoa(ip), NULL, call,uport, FALSE, uDynTimeout, (char*)inet_ntoa(ip), timeset, P_USER);
#endif /* AXIPR_HTML */
}

/* Per Rufzeichen, ohne die SSID zubeachten, einen */
/* Routeneintrag suchen. Gibt es keinen Eintrag,   */
/* wird sofort abgebrochen, ansonsten werden die   */
/* routeneintraege IP-Adresse und UDP-Port ueber-  */
/* nommen und das neue Rufzeichen wird eingetragen.*/
struct route_table_entry *search_route(unsigned char *call)
{
  register int i;
  char         srccall[10];
#ifdef AXIPR_HTML
  char        *timeset = set_time();
#endif /* AXIPR_HTML */

  /* Konvertiere Rufzeichen. */
  callss2str((char *)srccall, call_to_a(call));

  /* TB durchgehen. */
  for (i = 0; i < route_tbl_top; ++i)
  {
    /* Suche Rufzeichen. */
    if (cmpcal(srccall, call_to_a(&route_tbl[i].callsign[0])))
    {
#ifndef AXIPR_HTML
      /* Auto-Route eintragen */
      route_add((unsigned char *)&route_tbl[i].ip_addr, NULL, (unsigned char *)call, htons(route_tbl[i].org_udp_port), FALSE, uDynTimeout, (char *)route_tbl[i].hostname);
#else
      /* Auto-Route eintragen */
      route_add((unsigned char *)&route_tbl[i].ip_addr, NULL, (unsigned char *)call, htons(route_tbl[i].org_udp_port), FALSE, uDynTimeout, (char *)route_tbl[i].hostname, timeset, P_USER);
#endif /* AXIPR_HTML */
      /* Aktuellen Eintrag weiterreichen. */
       return (&route_tbl[i]); /* Eintrag gefunden */
    }
  }
 /* Kein Rufzeichen gefunden. */
 return (NULL);
}

/* Eine AX-Route Updaten. */
void UpdateRoute(unsigned char *srccall,
                  int           udpport,
                  int           timeout,
                  char         *hostname
#ifdef AXIPR_HTML
                , char          *timeset
                , UWORD          protokoll
#endif /* AXIPR_HTML */
)
{
  MBHEAD      *mbp;
  register int i;

  /* TB durchgehen. */
  for (i = 0; i < route_tbl_top; ++i)
  {
    /* Suche Rufzeichen. */
    if (addrmatch((unsigned char *)srccall , route_tbl[i].callsign))
    {
      route_tbl[i].org_udp_port = route_tbl[i].udp_port = htons((unsigned short)udpport);
      /* Hostname eintragen. */
      (void)memcpy(route_tbl[i].hostname, hostname, HNLEN);
      /* ggf. Timeout setzen. */
      route_tbl[i].timeout = timeout;

#ifdef AXIPR_HTML
      /* Hostname eintragen. */
      (void)memcpy(route_tbl[i].timeset, timeset, 17);
      /* Protokoll eintragen. */
      route_tbl[i].protokoll = P_USER;
#endif /* AXIPR_HTML */

      mbp = getmbp();

      /* und ab die Post ... */
      prompt(mbp);
      seteom(mbp);
      return;
    }
  }
}

#ifdef AXIPR_HTML

/* Protokoll fuer HTML-Ausgabe setzen. */
void SetHTML(int port, char *call, PEER *pp, BOOLEAN flag)
{
  register int  i;
  char          srccall[10 + 1];

  call2str(srccall, call);

  /* Routingtabelle durchsuchen */
  for (i = 0; i < route_tbl_top; ++i)
  {
    if (!strcmp(srccall , call_to_a(&route_tbl[i].callsign[0])))
    {
      /* Protokoll setzen. */
      set_status(i, port, pp);

      /* Route auf online/offline stellen. */
      route_tbl[i].online = flag;

      break;
    }
  }
}

/* Aktualisiere Loginzeit fuer TB */
char *set_time(void)
{
  time_t      go;
  struct tm  *zeit;
  static char c[17];
  char       *timeset = c;

  /* aktuelle Kalenderzeit */
  time(&go);
  /* konvertiert Datum und Uhrzeit. */
  zeit = localtime(&go);
  /* Formatiere eine Zeit-/Datumsangabe */
  strftime(c,17,"%d-%b-%H:%M:%S",zeit);

/* Aktuelle Login Date/Time */
return(timeset);
}

/* Protokoll setzen. */
void set_status(register int a, int port, PEER *pp)
{
  if (pp == NULL)                                  /* Route wird ausgetragen. */
  {
    route_tbl[a].protokoll = P_OFFLINE;          /* Route auf OFFLINE setzen. */
    route_tbl[a].online    = FALSE;
    return;
  }

  switch(pp->typ)                                            /* Protokol-TYP. */
  {
    case INP    :                                                 /* INP-TYP. */
     route_tbl[a].protokoll = P_INP;                       /* INP-TYP setzen. */
    break;

    case TNN    :                                                  /* TNN-TYP */
    case THENET :                                              /* THENET-TYP. */
      route_tbl[a].protokoll = P_THENET;            /* TNN/THENET-TYP setzen. */
     break;

    case FLEXNET:                                             /* FLEXNET-TYP. */
      route_tbl[a].protokoll = P_FLEXNET;              /* FLEXNET-TYP setzen. */
     break;

    default:
      route_tbl[a].protokoll = P_OFFLINE;        /* Route auf OFFLINE setzen. */
      route_tbl[a].online    = FALSE;
     return;
  }

  route_tbl[a].online    = TRUE;
}

/* Timer fuer htmlstatistik. */
void h_timer(void)
{
  static LONG h_Timer = 0L;

  /* Timer abgelaufen. */
  if (h_Timer <= 0L)
  {
    /* Timer neu setzen. */
    /* fest auf 60 sec.  */
    h_Timer = 60L;
    /* html auf Platte schreiben. */
    w_statistik();
  }
  /* Timer runter zaehlen. */
  h_Timer--;
}

/* Hiermit checken wir ob die rstat.css existiert. */
/* FALSE = keine rstat.css gefunden                */
/* TRUE  = es existiert eine rstat.css             */
static int check_css_file(void)
{
  char cssfile[255];

  /* Path kopieren. */
  strcpy(cssfile, htmlpath);
  /* CSS-Dateiname anhaengen.  */
  strcat(cssfile, CSS_RFILE);

  /* Pruefe ob Datei existiert.*/
  if (!access(cssfile,FALSE))
      /* Datei existiert. */
          return(TRUE);
  else
      /* Nix gefunden! */
          return(FALSE);
}

/* Eine default RSTAT.CSS schreiben */
static void rstat_css_default_file(void)
{
  FILE *fp;
  char cfgfile[255];

  if (HtmlStat == FALSE)
      return;

  strcpy(cfgfile, htmlpath);
  strcat(cfgfile, CSS_RFILE);
  if ((fp = fopen(cfgfile, "w+t")) == NULL)
      return;

  fprintf(fp,"BODY {\n"
             " BACKGROUND-IMAGE: url('bg.jpg');\n"
             " BACKGROUND-COLOR: #000080;\n"
             " COLOR: #FFFFFF;\n"
             " FONT-FAMILY: Verdana, Arial;\n"
             " FONT-SIZE: 12;\n"
             "}\n");

  fprintf(fp,"H1 {\n"
             " FONT-SIZE: 18; COLOR: #FFCC00;\n"
             "}\n");

  fprintf(fp,"H2 {\n"
             " FONT-SIZE: 12; COLOR: #FFFFFF;\n"
             "}\n");

  fprintf(fp,"a:link, a:visited {\n"
             " COLOR: #FFFFFF;\n"
             " text-weight: bold;\n"
             " text-decoration: none;\n"
             "}\n");

  fprintf(fp,"a:hover {\n"
             " COLOR: #FF6600;\n"
             " text-weight: bold;\n"
             " text-decoration: none;\n"
             "}\n"
             ".info {\n"
             " FONT-SIZE: 12; COLOR: #99CCFF;\n"
             "}\n");

  fprintf(fp,"table.box {\n"
             " background-color: #ffbb00;\n"
             " backcolor: #220000;\n"
             " backcolorlight: #00ff00;\n"
             " backcolordark: #000000;\n"
             " border-width: 0px;\n"
             " width: 95%%;\n"
             " border-style: outset;\n"
             " border-spacing: 2px;\n"
             " border-padding: 8px;\n"
             "}\n");
  
fprintf(fp,".status {\n"
             " FONT-SIZE: 10; BACKGROUND: #000000; COLOR: #FFCC00; TEXT-ALIGN: center;\n"
             "}\n");

  fprintf(fp,".off {\n"
             " FONT-SIZE: 10; BACKGROUND: #000000; COLOR: #ffffff;\n"
             "}");

  fprintf(fp,".offline {\n"
             " FONT-SIZE: 10; BACKGROUND: #9f00FF; COLOR: #ffffff;\n"
             "}");

  fprintf(fp,".user {\n"
             " FONT-SIZE: 10; BACKGROUND: #E06060; COLOR: #ffffff;\n"
             "}\n");

  fprintf(fp,".thenet {\n"
             " FONT-SIZE: 10; BACKGROUND: #E00060; COLOR: #ffffff;\n"
             "}\n");

  fprintf(fp,".inp {\n"
             " FONT-SIZE: 10; BACKGROUND: #0070C0; COLOR: #ffffff;\n"
             "}\n");

  fprintf(fp,".Flexnet {\n"
             " FONT-SIZE: 10; BACKGROUND: #006060; COLOR: #ffffff;\n"
             "}\n");

  fprintf(fp,".about {\n"
             " FONT-SIZE: 13; COLOR: #ffffff;\n"
             "}\n");

  fclose(fp);
}

/* Wie viel Ports sind nich frei ? */
static int free_ports(void)
{
  int free = FALSE;

  return(free = TABLE_SIZE - route_tbl_top);
}

/* Wie viel L2-Links (USER) - sind aktiv ? */
static int activ_user(void)
{
  register int a;
  int          user = FALSE;

  /* TB durchgehen */
  for (a = 0; a < route_tbl_top; a++)
    /* nur die als User markiert sind */
    if (   route_tbl[a].protokoll == P_USER
        /* und online sind !!!        */
        && route_tbl[a].online > 0)
        /* Alle USER zaehlen */
        user++;

  return(user);
}

/* Wie viel L2/L4_links sind aktiv ? */
static int activ_links(void)
{
  register int a;
  int          links = FALSE;

  /* TB durchgehen */
  for (a = 0; a < route_tbl_top; a++)
    /* L2/L4-Link online ?      */
    if (   route_tbl[a].online > 0
    /* wir zaehlen alle Protokolle, ausser USER */
        && route_tbl[a].protokoll != 1)
        links++;

  return(links);
}

/* Die Aktuelle Uhrzeit fuer HTML ausgeben */
static char *puttim_stat(void)
{
  struct      tm *p;
  static char tim[20];
  time_t      timet;
  char       *atime = tim;

  time(&timet);

  p = localtime(&timet);
  sprintf(tim,"%02i.%02i.%02i %02i:%02i:%02i",
              p->tm_mday,  p->tm_mon+1, p->tm_year % 100,
              p->tm_hour, p->tm_min, p->tm_sec);

  return(atime);
}

static char *set_class(UWORD flag, register int a)
{
  static char    o[8];
  char *protokoll = o;

  switch(flag)
  {
    case P_OFFLINE :
     if (!route_tbl[a].online)
       strcpy(o, "offline");

    break;

    case P_USER :
    if (route_tbl[a].online)
       strcpy(o, "user");
     else
       strcpy(o, "offline");
     break;

    case P_THENET :
    if (route_tbl[a].online)
       strcpy(o, "thenet");
     else
       strcpy(o, "offline");
     break;

    case P_INP :
     if (route_tbl[a].online)
       strcpy(o, "inp");
     else
       strcpy(o, "offline");

     break;

    case P_FLEXNET :
     if (route_tbl[a].online)
       strcpy(o, "flexnet");
     else
       strcpy(o, "offline");

     break;

    default:
      strcpy(o, "unknown");
     break;
  }

 return(protokoll);
}

static char *set_pname(register int a)
{
  static char p[8];
  char *protokoll = p;

  switch(route_tbl[a].protokoll)
  {
    case P_USER :
      strcpy(p, "L2");
     break;

    case P_THENET :
      strcpy(p, "THENET");
     break;

    case P_INP :
      strcpy(p, "INP");
     break;

    case P_FLEXNET :
      strcpy(p, "FLEXNET");
     break;

    default:
      strcpy(p, "UNKNOWN");
     break;
  }

return(protokoll);
}

static void all_route(FILE *fp, UWORD flag, register int a)
{
  char *pclass = set_class(flag, a);
  char *protokoll = set_pname(a);

  /* Nummer einer Route. */
  fprintf(fp,"  <td class=\"%s\"><center>%d</td>\n"
            , pclass, a);

  /* Rufzeichen. */
  fprintf(fp,"  <td class=\"%s\">%s</td>\n"
            , pclass
            , call_to_a(&route_tbl[a].callsign[0]));

  /* IP-Adresse. */
  fprintf(fp,"  <td class=\"%s\">"
             "  <a href=\"http://%s\" target=\"_http\">%s</a></td>\n"
            , pclass
            , route_tbl[a].hostname
            , route_tbl[a].hostname);

  /* Hostname/IP-Adresse. */
  fprintf(fp,"  <td class=\"%s\"><center>%s</td>\n"
            , pclass
            , route_tbl[a].hostname);

  /* UDP-Port */
  fprintf(fp,"  <td class=\"%s\"><center>%d</td>\n"
            , pclass
            , htons(route_tbl[a].udp_port));

  /* Loginzeit */
  fprintf(fp,"  <td class=\"%s\"><center>%s</td>\n"
            , pclass
            , route_tbl[a].timeset);

  if (route_tbl[a].timeout)
    /* Timeout */
    fprintf(fp,"  <td class=\"%s\"><center>%d</td>\n"
              , pclass
              , route_tbl[a].timeout);
  else
    /* Statische Route */
    fprintf(fp,"  <td class=\"%s\"><center>Statisch</td>\n"
              , pclass);

  /* Protokoll */
  fprintf(fp,"  <td class=\"%s\"><center>%s</td></tr>\n"
            , pclass
            , protokoll);
}

static void show_route(FILE *fp, UWORD flag, register int a)
{
  switch(flag)
  {
    case P_OFFLINE :
      if (!route_tbl[a].online)
         fprintf(fp," <tr class=\"offline\">\n");

      break;

    case P_USER :
      if (   route_tbl[a].protokoll == flag
          && route_tbl[a].online)
         fprintf(fp," <tr class=\"user\">\n");
      else
         fprintf(fp," <tr class=\"offline\">\n");

      break;

    case P_THENET :
      if (   route_tbl[a].protokoll == flag
          && route_tbl[a].online)
         fprintf(fp," <tr class=\"thenet\">\n");
      else
         fprintf(fp," <tr class=\"offline\">\n");

      break;


    case P_INP :
      if (   route_tbl[a].protokoll == flag
          && route_tbl[a].online)
         fprintf(fp," <tr class=\"inp\">\n");
      else
         fprintf(fp," <tr class=\"offline\">\n");

      break;

    case P_FLEXNET :
      if (   route_tbl[a].protokoll == flag
          && route_tbl[a].online)
         fprintf(fp," <tr class=\"flexnet\">\n");
      else
         fprintf(fp," <tr class=\"offline\">\n");

      break;


    default:
      if (   route_tbl[a].protokoll == flag
          && route_tbl[a].online)
         fprintf(fp," <tr class=\"unknow\">\n");
      else
         fprintf(fp," <tr class=\"offline\">\n");

     break;
  }
  all_route(fp, flag, a);
}

/* Schreibe htmlstatistik auf Platte. */
void w_statistik(void)
{
  FILE         *fp;
  char          cfgfile[255],mycall[10];
  char         *atime = puttim_stat();
  register int  a;
  char          runtime[8 + 1];
#ifndef CONNECTTIME
  unsigned long upt,
                upd,
                uph;
#endif /* CONNECTTIME */

  /* Pruefe, ob htmlstatistik eingeschaltet ist. */
  if (HtmlStat == FALSE)
      /* Ist nicht eingeschaltet, abbruch. */
      return;
  else
  {
    /* Pruefe ob CSS-Datei exisiert. */
    if (!check_css_file())
       /* Nein, dann neuschreiben.  */
       rstat_css_default_file();

  }

  strcpy(cfgfile,htmlpath );
  strcat(cfgfile, HTML_RFILE);
  if ((fp = fopen(cfgfile,"w+t")) == NULL)
      return;

  call2str(mycall,myid);

#ifndef CONNECTTIME
  upt = sys_time - start_time;                 /* Uptime in seconds     */
  upd = upt / SECONDS_PER_DAY;                 /* Uptime days           */
  upt %= SECONDS_PER_DAY;
  uph = upt / SECONDS_PER_HOUR;                /* Uptime hours          */
  upt %= SECONDS_PER_HOUR;

  if (upd > 0)
    sprintf(runtime, "%2lud,%2luh", upd, uph);
  else
    if (uph > 0)
      sprintf(runtime, "%2luh,%2lus", uph, upt);
    else
      if (upt > 0)
        sprintf(runtime, "    %2lus", upt);
      else
        sprintf(runtime, "       ");
#else
  sprintf(runtime, "%s", ConnectTime(tic10 / 100));
#endif /* CONNECTTIME */

  fprintf(fp,"<html>"
             "<head>\n"
             "<meta http-equiv=\"refresh\" content=\"%d\">\n"
             "<title>AXIPR-Statistik</title>\n"
             "<link rel=stylesheet type=text/css href=rstat.css>\n"
             "</head>\n<body>\n",HTML_INV);

  fprintf(fp,"<DIV ALIGN=\"CENTER\"><CENTER>\n"
             "<H1><U>%s%s)</U></H1>\n",signon,mycall);

  fprintf(fp,"<H2>Date/Time: %s Runtime: %s, %d Frei</H2>\n\n",atime, runtime, free_ports());
  fprintf(fp,"<FONT class=\"info\"><B> %d L2/L4-LINKS</b></FONT>\n",activ_links());
  fprintf(fp,"<table class=\"box\" cellspacing=\"2\" cellpadding=\"2\" style=\"border-collapse: collapse\">\n");
  fprintf(fp," <tr class=\"status\">\n"
             "  <td class=\"status\">Nr.</td>\n"
             "  <td class=\"status\">Rufzeichen</td>\n"
             "  <td class=\"status\">IP-Adresse</td>\n"
             "  <td class=\"status\">DYNDNS</td>\n"
             "  <td class=\"status\">UDP</td>\n"
             "  <td class=\"status\">Login</td>\n"
             "  <td class=\"status\">Timeout</td>\n"
             "  <td class=\"status\">Protokoll</td>\n"
             " </tr>\n");

   /* TB durchgehen. */
   for (a = 0; a < route_tbl_top; a++)
   {
     /* Nur aktive Routen zeigen. */
     if (route_tbl[a].online == FALSE)
       /* zum naechsten Eintrag. */
       continue;

     /* Nur INP- oder FLEXNET-Routen bearbeiten */
     if (route_tbl[a].protokoll > P_USER)
       /* und anzeigen. */
       show_route(fp, route_tbl[a].protokoll, a);
   }

   fprintf(fp,"</table><br><br>\n");

   fprintf(fp,"<FONT class=\"info\"><B>%d USER</B></FONT>\n",activ_user());
   fprintf(fp,"<table class=\"box\" cellspacing=\"2\" cellpadding=\"2\" style=\"border-collapse: collapse\">\n");
   fprintf(fp," <tr class=\"status\">\n"
              "  <td class=\"status\">Nr.</td>\n"
              "  <td class=\"status\">Rufzeichen</td>\n"
              "  <td class=\"status\">IP-Adresse</td>\n"
              "  <td class=\"status\">DYNDNS</td>\n"
              "  <td class=\"status\">UDP</td>\n"
              "  <td class=\"status\">Login</td>\n"
              "  <td class=\"status\">Timeout</td>\n"
              "  <td class=\"status\">Protokoll</td>\n"
              " </tr>\n");

   /* TB durchgehen */
   for (a = 0; a < route_tbl_top; a++)
   {
     /* Nur aktive Routen zeigen. */
     if (route_tbl[a].online == FALSE)
       /* zum naechsten Eintrag. */
       continue;

     /* Nur USER-Routen bearbeiten */
     if (route_tbl[a].protokoll == P_USER)
       /* und anzeigen. */
       show_route(fp, route_tbl[a].protokoll, a);
   }

   fprintf(fp,"</table>\n\n");
   fprintf(fp,"<BR><FONT class=\"info\"><B>My UDP-Port: %u</B></FONT>\n",htons(my_udp));
   fprintf(fp,"</body></html>\n");
   fclose(fp);
}

#endif /* AXIPR_HTML */
#endif /* AXIPR_UDP */

#endif

/* End of os/win32/ax25ip.c */
