Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Postfix/src/global/   (Postfix Mailserver Version 3.11©)  Datei vom 22.11.2025 mit Größe 19 kB image not shown  

Quelle  haproxy_srvr.c

  Sprache: C
 

/*++
/* NAME
/* haproxy_srvr 3
/* SUMMARY
/* server-side haproxy protocol support
/* SYNOPSIS
/* #include <haproxy_srvr.h>
/*
/* const char *haproxy_srvr_parse_sa(
/* const char *str,
/* ssize_t *str_len,
/* int *non_proxy,
/* MAI_HOSTADDR_STR *smtp_client_addr,
/* MAI_SERVPORT_STR *smtp_client_port,
/* MAI_HOSTADDR_STR *smtp_server_addr,
/* MAI_SERVPORT_STR *smtp_server_port,
/* struct sockaddr *client_sa,
/* SOCKADDR_SIZE *client_sa_len,
/* struct sockaddr *server_sa,
/* SOCKADDR_SIZE *server_sa_len)
/*
/* const char *haproxy_srvr_receive_sa(
/* int fd,
/* int *non_proxy,
/* MAI_HOSTADDR_STR *smtp_client_addr,
/* MAI_SERVPORT_STR *smtp_client_port,
/* MAI_HOSTADDR_STR *smtp_server_addr,
/* MAI_SERVPORT_STR *smtp_server_port,
/* struct sockaddr *client_sa,
/* SOCKADDR_SIZE *client_sa_len,
/* struct sockaddr *server_sa,
/* SOCKADDR_SIZE *server_sa_len)
/* ABI COMPATIBILITY
/* const char *haproxy_srvr_parse(str, str_len, non_proxy,
/* smtp_client_addr, smtp_client_port,
/* smtp_server_addr, smtp_server_port)
/* const char *str;
/* ssize_t *str_len;
/* int *non_proxy;
/* MAI_HOSTADDR_STR *smtp_client_addr,
/* MAI_SERVPORT_STR *smtp_client_port,
/* MAI_HOSTADDR_STR *smtp_server_addr,
/* MAI_SERVPORT_STR *smtp_server_port;
/*
/* const char *haproxy_srvr_receive(fd, non_proxy,
/* smtp_client_addr, smtp_client_port,
/* smtp_server_addr, smtp_server_port)
/* int fd;
/* int *non_proxy;
/* MAI_HOSTADDR_STR *smtp_client_addr,
/* MAI_SERVPORT_STR *smtp_client_port,
/* MAI_HOSTADDR_STR *smtp_server_addr,
/* MAI_SERVPORT_STR *smtp_server_port;
/* DESCRIPTION
/* haproxy_srvr_parse_sa() parses a haproxy v1 or v2 protocol
/* message. The result is null in case of success, a pointer
/* to text (with the error type) in case of error. If both
/* IPv6 and IPv4 support are enabled, IPV4_IN_IPV6 address
/* form (::ffff:1.2.3.4) is converted to IPV4 form. In case
/* of success, the str_len argument is updated with the number
/* of bytes parsed, and the non_proxy argument is true or false
/* if the haproxy message specifies a non-proxied connection.
/*
/* haproxy_srvr_receive_sa() receives and parses a haproxy protocol
/* handshake. This must be called before any I/O is done on
/* the specified file descriptor. The result is 0 in case of
/* success, -1 in case of error. All errors are logged.
/*
/* The haproxy v2 protocol support is limited to TCP over IPv4,
/* TCP over IPv6, and non-proxied connections. In the latter
/* case, the caller is responsible for any local or remote
/* address/port lookup.
/*
/* The client or server sockaddr and length storage are updated
/* when their pointers are non-null.
/*
/* haproxy_srvr_parse() and haproxy_srvr_receive() provide ABI
/* backwards compatibility, passing null pointers for the sockaddr
/* and length storage arguments.
/* LICENSE
/* .ad
/* .fi
/* The Secure Mailer license must be distributed with this software.
/* AUTHOR(S)
/* Wietse Venema
/* IBM T.J. Watson Research
/* P.O. Box 704
/* Yorktown Heights, NY 10598, USA
/*
/* Wietse Venema
/* Google, Inc.
/* 111 8th Avenue
/* New York, NY 10011, USA
/*
/* Wietse Venema
/* porcupine.org
/*--*/


/* System library. */

#include <sys_defs.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>

#ifdef STRCASECMP_IN_STRINGS_H
#include <strings.h>
#endif

/* Utility library. */

#include <msg.h>
#include <myaddrinfo.h>
#include <valid_hostname.h>
#include <stringops.h>
#include <mymalloc.h>
#include <inet_proto.h>
#include <split_at.h>
#include <sock_addr.h>
#include <normalize_v4mapped_addr.h>

/* Global library. */

#define _HAPROXY_SRVR_INTERNAL_
#include <haproxy_srvr.h>

static const INET_PROTO_INFO *proto_info;

#define STR_OR_NULL(str) ((str) ? (str) : "(null)")

/* haproxy_srvr_parse_lit - extract and validate string literal */

static int haproxy_srvr_parse_lit(const char *str,...)
{
    va_list ap;
    const char *cp;
    int     result = -1;
    int     count;

    if (msg_verbose)
 msg_info("haproxy_srvr_parse: %s", STR_OR_NULL(str));

    if (str != 0) {
 va_start(ap, str);
 for (count = 0; (cp = va_arg(ap, const char *)) != 0; count++) {
     if (strcmp(str, cp) == 0) {
  result = count;
  break;
     }
 }
 va_end(ap);
    }
    return (result);
}

/* haproxy_srvr_parse_proto - parse and validate the protocol type */

static int haproxy_srvr_parse_proto(const char *str, int *addr_family)
{
    if (msg_verbose)
 msg_info("haproxy_srvr_parse: proto=%s", STR_OR_NULL(str));

    if (str == 0)
 return (-1);
#ifdef HAS_IPV6
    if (strcasecmp(str, "TCP6") == 0) {
 if (strchr((char *) proto_info->sa_family_list, AF_INET6) != 0) {
     *addr_family = AF_INET6;
     return (0);
 }
    } else
#endif
    if (strcasecmp(str, "TCP4") == 0) {
 if (strchr((char *) proto_info->sa_family_list, AF_INET) != 0) {
     *addr_family = AF_INET;
     return (0);
 }
    }
    return (-1);
}

/* haproxy_srvr_parse_addr - extract and validate IP address */

static int haproxy_srvr_parse_addr(const char *str, MAI_HOSTADDR_STR *addr,
               int addr_family,
               struct sockaddr *sa,
               SOCKADDR_SIZE *sa_len)
{
    struct addrinfo *res;
    int     err;
    struct sockaddr_storage ss;
    SOCKADDR_SIZE ss_len;

    if (msg_verbose)
 msg_info("haproxy_srvr_parse: addr=%s proto=%d",
   STR_OR_NULL(str), addr_family);

    if (str == 0 || strlen(str) >= sizeof(MAI_HOSTADDR_STR))
 return (-1);

    switch (addr_family) {
#ifdef HAS_IPV6
    case AF_INET6:
 if (!valid_ipv6_hostaddr(str, DONT_GRIPE))
     return (-1);
 break;
#endif
    case AF_INET:
 if (!valid_ipv4_hostaddr(str, DONT_GRIPE))
     return (-1);
 break;
    default:
 msg_panic("haproxy_srvr_parse: unexpected address family: %d",
    addr_family);
    }

    /*
     * Convert the printable address to canonical form. Don't rely on the
     * proxy. This requires a conversion to binary form and back, even if a
     * caller such as postscreen does not need the binary form.
     */

    if ((err = hostaddr_to_sockaddr(str, (char *) 00, &res)) != 0) {
 msg_warn("haproxy_srvr_parse: hostaddr_to_sockaddr(\"%s\") failed: %s",
   str, MAI_STRERROR(err));
 return (-1);
    }
    if (sa == 0) {
 sa = (struct sockaddr *) &ss;
 ss_len = sizeof(ss);
 sa_len = &ss_len;
    } else {
 if (sa_len == 0)
     msg_panic("haproxy_srvr_parse: sockaddr length not specified");
    }
    if (*sa_len < res->ai_addrlen)
 msg_panic("haproxy_srvr_parse: sockaddr size %d too small",
    (int) *sa_len);
    *sa_len = res->ai_addrlen;
    memcpy((void *) sa, res->ai_addr, res->ai_addrlen);
    freeaddrinfo(res);
#ifdef HAS_IPV6
    if (sa->sa_family == AF_INET6)
 normalize_v4mapped_sockaddr(sa, sa_len);
#endif
    if ((err = sockaddr_to_hostaddr(sa, *sa_len,
        addr, (MAI_SERVPORT_STR *) 00)) != 0) {
 msg_warn("haproxy_srvr_parse: sockaddr_to_hostaddr() failed: %s",
   MAI_STRERROR(err));
 return (-1);
    }
    return (0);
}

/* haproxy_srvr_parse_port - extract and validate TCP port */

static int haproxy_srvr_parse_port(const char *str, MAI_SERVPORT_STR *port,
               struct sockaddr *sa)
{
    if (msg_verbose)
 msg_info("haproxy_srvr_parse: port=%s", STR_OR_NULL(str));
    if (str == 0 || strlen(str) >= sizeof(MAI_SERVPORT_STR)
 || !valid_hostport(str, DONT_GRIPE)) {
 return (-1);
    } else {
 memcpy(port->buf, str, strlen(str) + 1);
 if (sa != 0) {
     switch (sa->sa_family) {
#ifdef HAS_IPV6
     case AF_INET6:
  SOCK_ADDR_IN6_PORT(sa) = htons(atoi(str));
  break;
#endif
     case AF_INET:
  SOCK_ADDR_IN_PORT(sa) = htons(atoi(str));
  break;
     default:
  msg_panic("haproxy_srvr_parse: unexpected address family: %d",
     sa->sa_family);
     }
 }
 return (0);
    }
}

/* haproxy_srvr_parse_v2_addr_v4 - parse IPv4 info from v2 header */

static int haproxy_srvr_parse_v2_addr_v4(uint32_t sin_addr,
              unsigned sin_port,
              MAI_HOSTADDR_STR *addr,
              MAI_SERVPORT_STR *port,
              struct sockaddr *sa,
              SOCKADDR_SIZE *sa_len)
{
    struct sockaddr_in sin;
    SOCKADDR_SIZE sin_len;

    /*
     * Convert the binary address and port to printable form.
     */

    if (sa == 0) {
 sa = (struct sockaddr *) &sin;
 sin_len = sizeof(sin);
 sa_len = &sin_len;
    } else {
 if (sa_len == 0)
     msg_panic("haproxy_srvr_parse: sockaddr length not specified");
 if (*sa_len < sizeof(sin))
     msg_panic("haproxy_srvr_parse: sockaddr size %d too small",
        (int) *sa_len);
 *sa_len = sizeof(sin);
    }
    memset((void *) sa, 0, *sa_len);
    SOCK_ADDR_IN_FAMILY(sa) = AF_INET;
    SOCK_ADDR_IN_ADDR(sa).s_addr = sin_addr;
    SOCK_ADDR_IN_PORT(sa) = sin_port;
    if (sockaddr_to_hostaddr(sa, *sa_len, addr, port, 0) < 0)
 return (-1);
    return (0);
}

#ifdef HAS_IPV6

/* haproxy_srvr_parse_v2_addr_v6 - parse IPv6 info from v2 header */

static int haproxy_srvr_parse_v2_addr_v6(uint8_t *sin6_addr,
              unsigned sin6_port,
              MAI_HOSTADDR_STR *addr,
              MAI_SERVPORT_STR *port,
              struct sockaddr *sa,
              SOCKADDR_SIZE *sa_len)
{
    struct sockaddr_in6 sin6;
    SOCKADDR_SIZE sin6_len;

    /*
     * Convert the binary address and port to printable form.
     */

    if (sa == 0) {
 sa = (struct sockaddr *) &sin6;
 sin6_len = sizeof(sin6);
 sa_len = &sin6_len;
    } else {
 if (sa_len == 0)
     msg_panic("haproxy_srvr_parse: sockaddr length not specified");
 if (*sa_len < sizeof(sin6))
     msg_panic("haproxy_srvr_parse: sockaddr size %d too small",
        (int) *sa_len);
 *sa_len = sizeof(sin6);
    }
    memset((void *) sa, 0, *sa_len);
    SOCK_ADDR_IN6_FAMILY(sa) = AF_INET6;
    memcpy(&SOCK_ADDR_IN6_ADDR(sa), sin6_addr, sizeof(SOCK_ADDR_IN6_ADDR(sa)));
    SOCK_ADDR_IN6_PORT(sa) = sin6_port;
    normalize_v4mapped_sockaddr(sa, sa_len);
    if (sockaddr_to_hostaddr(sa, *sa_len, addr, port, 0) < 0)
 return (-1);
    return (0);
}

#endif

/* haproxy_srvr_parse_v2_hdr - parse v2 header address info */

static const char *haproxy_srvr_parse_v2_hdr(const char *str, ssize_t *str_len,
                  int *non_proxy,
             MAI_HOSTADDR_STR *smtp_client_addr,
             MAI_SERVPORT_STR *smtp_client_port,
             MAI_HOSTADDR_STR *smtp_server_addr,
             MAI_SERVPORT_STR *smtp_server_port,
              struct sockaddr *client_sa,
                   SOCKADDR_SIZE *client_sa_len,
              struct sockaddr *server_sa,
                   SOCKADDR_SIZE *server_sa_len)
{
    const char myname[] = "haproxy_srvr_parse_v2_hdr";
    struct proxy_hdr_v2 *hdr_v2;

    if (*str_len < PP2_HEADER_LEN)
 return ("short protocol header");
    hdr_v2 = (struct proxy_hdr_v2 *) str;
    if (memcmp(hdr_v2->sig, PP2_SIGNATURE, PP2_SIGNATURE_LEN) != 0)
 return ("unrecognized protocol header");
    if ((hdr_v2->ver_cmd & PP2_VERSION_MASK) != PP2_VERSION)
 return ("unrecognized protocol version");
    if (*str_len < PP2_HEADER_LEN + ntohs(hdr_v2->len))
 return ("short version 2 protocol header");

    switch (hdr_v2->ver_cmd & PP2_CMD_MASK) {

 /*
  * Proxied connection, use the proxy-provided connection info.
 */

    case PP2_CMD_PROXY:
 switch (hdr_v2->fam) {
 case PP2_FAM_INET | PP2_TRANS_STREAM:{ /* TCP4 */
  if (strchr((char *) proto_info->sa_family_list, AF_INET) == 0)
      return ("Postfix IPv4 support is disabled");
  if (ntohs(hdr_v2->len) < PP2_ADDR_LEN_INET)
      return ("short address field");
  if (haproxy_srvr_parse_v2_addr_v4(hdr_v2->addr.ip4.src_addr,
        hdr_v2->addr.ip4.src_port,
      smtp_client_addr, smtp_client_port,
           client_sa, client_sa_len) < 0)
      return ("client network address conversion error");
  if (msg_verbose)
      msg_info("%s: smtp_client_addr=%s smtp_client_port=%s",
        myname, smtp_client_addr->buf, smtp_client_port->buf);
  if (haproxy_srvr_parse_v2_addr_v4(hdr_v2->addr.ip4.dst_addr,
        hdr_v2->addr.ip4.dst_port,
        smtp_server_addr,
        smtp_server_port,
        server_sa,
        server_sa_len) < 0)
      return ("server network address conversion error");
  if (msg_verbose)
      msg_info("%s: smtp_server_addr=%s smtp_server_port=%s",
        myname, smtp_server_addr->buf, smtp_server_port->buf);
  break;
     }
 case PP2_FAM_INET6 | PP2_TRANS_STREAM:{/* TCP6 */
#ifdef HAS_IPV6
  if (strchr((char *) proto_info->sa_family_list, AF_INET6) == 0)
      return ("Postfix IPv6 support is disabled");
  if (ntohs(hdr_v2->len) < PP2_ADDR_LEN_INET6)
      return ("short address field");
  if (haproxy_srvr_parse_v2_addr_v6(hdr_v2->addr.ip6.src_addr,
        hdr_v2->addr.ip6.src_port,
        smtp_client_addr,
        smtp_client_port,
        client_sa,
        client_sa_len) < 0)
      return ("client network address conversion error");
  if (msg_verbose)
      msg_info("%s: smtp_client_addr=%s smtp_client_port=%s",
        myname, smtp_client_addr->buf, smtp_client_port->buf);
  if (haproxy_srvr_parse_v2_addr_v6(hdr_v2->addr.ip6.dst_addr,
        hdr_v2->addr.ip6.dst_port,
        smtp_server_addr,
        smtp_server_port,
        server_sa,
        server_sa_len) < 0)
      return ("server network address conversion error");
  if (msg_verbose)
      msg_info("%s: smtp_server_addr=%s smtp_server_port=%s",
        myname, smtp_server_addr->buf, smtp_server_port->buf);
  break;
#else
  return ("Postfix IPv6 support is not compiled in");
#endif
     }
 default:
     return ("unsupported network protocol");
 }
 /* For now, skip and ignore TLVs. */
 *str_len = PP2_HEADER_LEN + ntohs(hdr_v2->len);
 return (0);

 /*
  * Non-proxied connection, use the proxy-to-server connection info.
 */

    case PP2_CMD_LOCAL:
 /* For now, skip and ignore TLVs. */
 *non_proxy = 1;
 *str_len = PP2_HEADER_LEN + ntohs(hdr_v2->len);
 return (0);
    default:
 return ("bad command in proxy header");
    }
}

/* haproxy_srvr_parse_sa - parse haproxy line */

const char *haproxy_srvr_parse_sa(const char *str, ssize_t *str_len,
              int *non_proxy,
              MAI_HOSTADDR_STR *smtp_client_addr,
              MAI_SERVPORT_STR *smtp_client_port,
              MAI_HOSTADDR_STR *smtp_server_addr,
              MAI_SERVPORT_STR *smtp_server_port,
              struct sockaddr *client_sa,
              SOCKADDR_SIZE *client_sa_len,
              struct sockaddr *server_sa,
              SOCKADDR_SIZE *server_sa_len)
{
    const char *err;

    if (proto_info == 0)
 proto_info = inet_proto_info();

    *non_proxy = 0;

    /*
     * XXX We don't accept connections with the "UNKNOWN" protocol type,
     * because those would sidestep address-based access control mechanisms.
     */


    /*
     * Try version 1 protocol.
     */

    if (strncmp(str, "PROXY "6) == 0) {
 char   *saved_str = mystrndup(str, *str_len);
 char   *cp = saved_str;
 char   *beyond_header = split_at(saved_str, '\n');
 int     addr_family;

#define NEXT_TOKEN mystrtok(&cp, " \r")
 if (beyond_header == 0)
     err = "missing protocol header terminator";
 else if (haproxy_srvr_parse_lit(NEXT_TOKEN, "PROXY", (char *) 0) < 0)
     err = "bad or missing protocol header";
 else if (haproxy_srvr_parse_proto(NEXT_TOKEN, &addr_family) < 0)
     err = "bad or missing protocol type";
 else if (haproxy_srvr_parse_addr(NEXT_TOKEN, smtp_client_addr,
      addr_family, client_sa,
      client_sa_len) < 0)
     err = "bad or missing client address";
 else if (haproxy_srvr_parse_addr(NEXT_TOKEN, smtp_server_addr,
      addr_family, server_sa,
      server_sa_len) < 0)
     err = "bad or missing server address";
 else if (haproxy_srvr_parse_port(NEXT_TOKEN, smtp_client_port,
      client_sa) < 0)
     err = "bad or missing client port";
 else if (haproxy_srvr_parse_port(NEXT_TOKEN, smtp_server_port,
      server_sa) < 0)
     err = "bad or missing server port";
 else {
     err = 0;
     *str_len = beyond_header - saved_str;
 }
 myfree(saved_str);

 return (err);
    }

    /*
     * Try version 2 protocol.
     */

    else {
 return (haproxy_srvr_parse_v2_hdr(str, str_len, non_proxy,
       smtp_client_addr, smtp_client_port,
       smtp_server_addr, smtp_server_port,
       client_sa, client_sa_len,
       server_sa, server_sa_len));
    }
}

/* haproxy_srvr_parse - ABI compatibility */

#undef haproxy_srvr_parse

const char *haproxy_srvr_parse(const char *str, ssize_t *str_len,
                  int *non_proxy,
                  MAI_HOSTADDR_STR *smtp_client_addr,
                  MAI_SERVPORT_STR *smtp_client_port,
                  MAI_HOSTADDR_STR *smtp_server_addr,
                  MAI_SERVPORT_STR *smtp_server_port);

const char *haproxy_srvr_parse(const char *str, ssize_t *str_len,
                  int *non_proxy,
                  MAI_HOSTADDR_STR *smtp_client_addr,
                  MAI_SERVPORT_STR *smtp_client_port,
                  MAI_HOSTADDR_STR *smtp_server_addr,
                  MAI_SERVPORT_STR *smtp_server_port)
{
    return (haproxy_srvr_parse_sa(str, str_len, non_proxy,
      smtp_client_addr, smtp_client_port,
      smtp_server_addr, smtp_server_port,
      (struct sockaddr *) 0, (SOCKADDR_SIZE *) 0,
          (struct sockaddr *) 0, (SOCKADDR_SIZE *) 0));
}

/* haproxy_srvr_receive_sa - receive and parse haproxy protocol handshake */

int     haproxy_srvr_receive_sa(int fd, int *non_proxy,
            MAI_HOSTADDR_STR *smtp_client_addr,
            MAI_SERVPORT_STR *smtp_client_port,
            MAI_HOSTADDR_STR *smtp_server_addr,
            MAI_SERVPORT_STR *smtp_server_port,
            struct sockaddr *client_sa,
            SOCKADDR_SIZE *client_sa_len,
            struct sockaddr *server_sa,
            SOCKADDR_SIZE *server_sa_len)
{
    const char *err;
    VSTRING *escape_buf;
    char    read_buf[HAPROXY_HEADER_MAX_LEN + 1];
    ssize_t read_len;

    /*
     * We must not read(2) past the end of the HaProxy handshake. The v2
     * protocol assumes that the handshake will never be fragmented,
     * therefore we peek, parse the entire input, then read(2) only the
     * number of bytes parsed.
     */

    if ((read_len = recv(fd, read_buf, sizeof(read_buf) - 1, MSG_PEEK)) <= 0) {
 msg_warn("haproxy read: EOF");
 return (-1);
    }

    /*
     * Parse the haproxy handshake, and determine the handshake length.
     */

    read_buf[read_len] = 0;

    if ((err = haproxy_srvr_parse_sa(read_buf, &read_len, non_proxy,
         smtp_client_addr, smtp_client_port,
         smtp_server_addr, smtp_server_port,
         client_sa, client_sa_len,
         server_sa, server_sa_len)) != 0) {
 escape_buf = vstring_alloc(read_len * 2);
 escape(escape_buf, read_buf, read_len);
 msg_warn("haproxy read: %s: %s", err, vstring_str(escape_buf));
 vstring_free(escape_buf);
 return (-1);
    }

    /*
     * Try to pop the haproxy handshake off the input queue.
     */

    if (recv(fd, read_buf, read_len, 0) != read_len) {
 msg_warn("haproxy read: %m");
 return (-1);
    }
    return (0);
}

/* haproxy_srvr_receive - ABI compatibility */

#undef haproxy_srvr_receive

int     haproxy_srvr_receive(int fd, int *non_proxy,
                MAI_HOSTADDR_STR *smtp_client_addr,
                MAI_SERVPORT_STR *smtp_client_port,
                MAI_HOSTADDR_STR *smtp_server_addr,
                MAI_SERVPORT_STR *smtp_server_port);

int     haproxy_srvr_receive(int fd, int *non_proxy,
                MAI_HOSTADDR_STR *smtp_client_addr,
                MAI_SERVPORT_STR *smtp_client_port,
                MAI_HOSTADDR_STR *smtp_server_addr,
                MAI_SERVPORT_STR *smtp_server_port)
{
    return (haproxy_srvr_receive_sa(fd, non_proxy,
        smtp_client_addr, smtp_client_port,
        smtp_server_addr, smtp_server_port,
        (struct sockaddr *) 0,
        (SOCKADDR_SIZE *) 0,
        (struct sockaddr *) 0,
        (SOCKADDR_SIZE *) 0));
}

Messung V0.5 in Prozent
C=74 H=95 G=84

¤ Dauer der Verarbeitung: 0.14 Sekunden  (vorverarbeitet am  2026-08-08) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.