Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  gnutls.c   Sprache: C

 

/*****************************************************************************
 * gnutls.c
 *****************************************************************************
 * Copyright (C) 2004-2017 Rémi Denis-Courmont
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
 *****************************************************************************/


#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#include <limits.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <errno.h>
#include <assert.h>
#include <unistd.h>
#ifdef HAVE_SYS_UIO_H
# include <sys/uio.h>
#endif

#include <vlc_common.h>
#include <vlc_plugin.h>
#include <vlc_tls.h>
#include <vlc_block.h>
#include <vlc_dialog.h>

#include <gnutls/gnutls.h>
#include <gnutls/x509.h>

typedef struct vlc_tls_gnutls
{
    vlc_tls_t tls;
    gnutls_session_t session;
    vlc_object_t *obj;
} vlc_tls_gnutls_t;

static void gnutls_Banner(vlc_object_t *obj)
{
    msg_Dbg(obj, "using GnuTLS v%s (built with v"GNUTLS_VERSION")",
            gnutls_check_version(NULL));
}

static int gnutls_Error(vlc_tls_gnutls_t *priv, int val)
{
    switch (val)
    {
        case GNUTLS_E_AGAIN:
#ifdef _WIN32
            WSASetLastError (WSAEWOULDBLOCK);
#endif
            errno = EAGAIN;
            break;

        case GNUTLS_E_INTERRUPTED:
#ifdef _WIN32
            WSASetLastError (WSAEINTR);
#endif
            errno = EINTR;
            break;

        default:
            msg_Err(priv->obj, "%s", gnutls_strerror (val));
#ifndef NDEBUG
            if (!gnutls_error_is_fatal (val))
                msg_Err(priv->obj, "Error above should be handled");
#endif
#ifdef _WIN32
            WSASetLastError (WSAECONNRESET);
#endif
            errno = ECONNRESET;
    }
    return -1;
}

static ssize_t vlc_gnutls_read(gnutls_transport_ptr_t ptr, void *buf,
                               size_t length)
{
    vlc_tls_t *sock = ptr;
    struct iovec iov = {
        .iov_base = buf,
        .iov_len = length,
    };

    return sock->ops->readv(sock, &iov, 1);
}

static ssize_t vlc_gnutls_writev(gnutls_transport_ptr_t ptr,
                                 const giovec_t *giov, int iovcnt)
{
#ifdef IOV_MAX
    const long iovmax = IOV_MAX;
#else
    const long iovmax = sysconf(_SC_IOV_MAX);
#endif
    if (unlikely(iovcnt > iovmax))
    {
        errno = EINVAL;
        return -1;
    }
    if (unlikely(iovcnt == 0))
        return 0;

    vlc_tls_t *sock = ptr;
    struct iovec iov[iovcnt];

    for (int i = 0; i < iovcnt; i++)
    {
        iov[i].iov_base = giov[i].iov_base;
        iov[i].iov_len = giov[i].iov_len;
    }

    return sock->ops->writev(sock, iov, iovcnt);
}

static int gnutls_GetFD(vlc_tls_t *tls, short *restrict events)
{
    vlc_tls_gnutls_t *priv = (vlc_tls_gnutls_t *)tls;
    vlc_tls_t *sock = gnutls_transport_get_ptr(priv->session);

    return vlc_tls_GetPollFD(sock, events);
}

static ssize_t gnutls_Recv(vlc_tls_t *tls, struct iovec *iov, unsigned count)
{
    vlc_tls_gnutls_t *priv = (vlc_tls_gnutls_t *)tls;
    gnutls_session_t session = priv->session;
    size_t rcvd = 0;

    while (count > 0)
    {
        ssize_t val = gnutls_record_recv(session, iov->iov_base, iov->iov_len);
        if (val < 0)
            return rcvd ? (ssize_t)rcvd : gnutls_Error(priv, val);

        rcvd += val;

        if ((size_t)val < iov->iov_len)
            break;

        iov++;
        count--;
    }

    return rcvd;
}

static ssize_t gnutls_Send (vlc_tls_t *tls, const struct iovec *iov,
                            unsigned count)
{
    vlc_tls_gnutls_t *priv = (vlc_tls_gnutls_t *)tls;
    gnutls_session_t session = priv->session;
    ssize_t val;

    if (!gnutls_record_check_corked(session))
    {
        gnutls_record_cork(session);

        while (count > 0)
        {
            val = gnutls_record_send(session, iov->iov_base, iov->iov_len);
            if (val < (ssize_t)iov->iov_len)
                break;

            iov++;
            count--;
        }
    }

    val = gnutls_record_uncork(session, 0);
    return (val < 0) ? gnutls_Error(priv, val) : val;
}

static int gnutls_Shutdown(vlc_tls_t *tls, bool duplex)
{
    vlc_tls_gnutls_t *priv = (vlc_tls_gnutls_t *)tls;
    gnutls_session_t session = priv->session;
    ssize_t val;

    /* Flush any pending data */
    val = gnutls_record_uncork(session, 0);
    if (val < 0)
        return gnutls_Error(priv, val);

    val = gnutls_bye(session, duplex ? GNUTLS_SHUT_RDWR : GNUTLS_SHUT_WR);
    if (val < 0)
        return gnutls_Error(priv, val);

    return 0;
}

static void gnutls_Close (vlc_tls_t *tls)
{
    vlc_tls_gnutls_t *priv = (vlc_tls_gnutls_t *)tls;

    gnutls_deinit(priv->session);
    free(priv);
}

static const struct vlc_tls_operations gnutls_ops =
{
    gnutls_GetFD,
    gnutls_Recv,
    gnutls_Send,
    gnutls_Shutdown,
    gnutls_Close,
};

static vlc_tls_gnutls_t *gnutls_SessionOpen(vlc_object_t *obj, int type,
                                         gnutls_certificate_credentials_t x509,
                                           vlc_tls_t *sock,
                                           const char *const *alpn)
{
    vlc_tls_gnutls_t *priv = malloc(sizeof (*priv));
    if (unlikely(priv == NULL))
        return NULL;

    gnutls_session_t session;
    const char *errp;
    int val;

    type |= GNUTLS_NONBLOCK | GNUTLS_ENABLE_FALSE_START;

    val = gnutls_init(&session, type);
    if (val != 0)
    {
        msg_Err(obj, "cannot initialize TLS session: %s",
                gnutls_strerror(val));
        free(priv);
        return NULL;
    }

    char *priorities = var_InheritString(obj, "gnutls-priorities");
    if (unlikely(priorities == NULL))
        goto error;

    val = gnutls_priority_set_direct (session, priorities, &errp);
    if (val < 0)
        msg_Err(obj, "cannot set TLS priorities \"%s\": %s", errp,
                gnutls_strerror(val));
    free (priorities);
    if (val < 0)
        goto error;

    val = gnutls_credentials_set (session, GNUTLS_CRD_CERTIFICATE, x509);
    if (val < 0)
    {
        msg_Err(obj, "cannot set TLS session credentials: %s",
                gnutls_strerror(val));
        goto error;
    }

    if (alpn != NULL)
    {
        gnutls_datum_t *protv;
        size_t protc = 0;
        const char *const *alpn_count = alpn;

        while (*alpn_count != NULL)
        {
            protc++;
            alpn_count++;
        }

        protv = vlc_alloc(protc, sizeof (*protv));
        if (unlikely(protv == NULL))
            goto error;

        for (size_t i=0; i < protc; i++)
        {
            protv[i].data = (void *)*alpn;
            protv[i].size = strlen(*alpn);
            alpn++;
        }

        val = gnutls_alpn_set_protocols (session, protv, protc, 0);
        free (protv);
    }

    gnutls_transport_set_ptr(session, sock);
    gnutls_transport_set_vec_push_function(session, vlc_gnutls_writev);
    gnutls_transport_set_pull_function(session, vlc_gnutls_read);

    priv->session = session;
    priv->obj = obj;

    vlc_tls_t *tls = &priv->tls;

    tls->ops = &gnutls_ops;
    return priv;

error:
    gnutls_deinit (session);
    free(priv);
    return NULL;
}

/**
 * Starts or continues the TLS handshake.
 *
 * @return -1 on fatal error, 0 on successful handshake completion,
 * 1 if more would-be blocking recv is needed,
 * 2 if more would-be blocking send is required.
 */

static int gnutls_Handshake(vlc_tls_t *tls, char **restrict alp)
{
    vlc_tls_gnutls_t *priv = (vlc_tls_gnutls_t *)tls;
    vlc_object_t *obj = priv->obj;
    gnutls_session_t session = priv->session;
    int val = gnutls_handshake(session);

    if (gnutls_error_is_fatal(val))
    {
        msg_Err(obj, "TLS handshake error: %s", gnutls_strerror(val));
        return -1;
    }

    switch (val)
    {
        case GNUTLS_E_SUCCESS:
            break;
        case GNUTLS_E_AGAIN:
        case GNUTLS_E_INTERRUPTED:
            /* I/O event: return to caller's poll() loop */
            return 1 + gnutls_record_get_direction(session);
        default:
            msg_Warn(obj, "TLS handshake error: %s", gnutls_strerror(val));
            return 1;
    }

    msg_Dbg(obj, "TLS handshake complete");

    unsigned flags = gnutls_session_get_flags(session);

    if (flags & GNUTLS_SFLAGS_SAFE_RENEGOTIATION)
        msg_Dbg(obj, " - safe renegotiation (RFC5746) enabled");
    if (flags & GNUTLS_SFLAGS_EXT_MASTER_SECRET)
        msg_Dbg(obj, " - extended master secret (RFC7627) enabled");
    if (flags & GNUTLS_SFLAGS_ETM)
        msg_Dbg(obj, " - encrypt then MAC (RFC7366) enabled");
    if (flags & GNUTLS_SFLAGS_FALSE_START)
        msg_Dbg(obj, " - false start (RFC7918) enabled");

    if (alp != NULL)
    {
        gnutls_datum_t datum;

        val = gnutls_alpn_get_selected_protocol (session, &datum);
        if (val == 0)
        {
            if (memchr (datum.data, 0, datum.size) != NULL)
                return -1; /* Other end is doing something fishy?! */

            *alp = strndup ((char *)datum.data, datum.size);
            if (unlikely(*alp == NULL))
                return -1;
        }
        else
            *alp = NULL;
    }
    return 0;
}

static vlc_tls_t *gnutls_ClientSessionOpen(vlc_tls_client_t *crd,
                                           vlc_tls_t *sk, const char *hostname,
                                           const char *const *alpn)
{
    vlc_tls_gnutls_t *priv = gnutls_SessionOpen(VLC_OBJECT(crd), GNUTLS_CLIENT,
                                                crd->sys, sk, alpn);
    if (priv == NULL)
        return NULL;

    gnutls_session_t session = priv->session;

    if (likely(hostname != NULL))
        /* fill Server Name Indication */
        gnutls_server_name_set (session, GNUTLS_NAME_DNS,
                                hostname, strlen (hostname));

    return &priv->tls;
}

static int gnutls_ClientHandshake(vlc_tls_t *tls,
                                  const char *host, const char *service,
                                  char **restrict alp)
{
    vlc_tls_gnutls_t *priv = (vlc_tls_gnutls_t *)tls;
    vlc_object_t *obj = priv->obj;

    int val = gnutls_Handshake(tls, alp);
    if (val)
        return val;

    /* certificates chain verification */
    gnutls_session_t session = priv->session;
    unsigned status;

    val = gnutls_certificate_verify_peers3 (session, host, &status);
    if (val)
    {
        msg_Err(obj, "Certificate verification error: %s",
                gnutls_strerror(val));
        goto error;
    }

    if (status == 0) /* Good certificate */
        return 0;

    /* Bad certificate */
    gnutls_datum_t desc;

    if (gnutls_certificate_verification_status_print(status,
                         gnutls_certificate_type_get (session), &desc, 0) == 0)
    {
        msg_Err(obj, "Certificate verification failure: %s", desc.data);
        gnutls_free (desc.data);
    }

    status &= ~GNUTLS_CERT_INVALID; /* always set / catch-all error */
    status &= ~GNUTLS_CERT_SIGNER_NOT_FOUND; /* unknown CA */
    status &= ~GNUTLS_CERT_UNEXPECTED_OWNER; /* mismatched hostname */

    if (status != 0 || host == NULL)
        goto error; /* Really bad certificate */

    /* Look up mismatching certificate in store */
    const gnutls_datum_t *datum;
    unsigned count;

    datum = gnutls_certificate_get_peers (session, &count);
    if (datum == NULL || count == 0)
    {
        msg_Err(obj, "Peer certificate not available");
        goto error;
    }

    msg_Dbg(obj, "%u certificate(s) in the list", count);
    val = gnutls_verify_stored_pubkey (NULL, NULL, host, service,
                                       GNUTLS_CRT_X509, datum, 0);
    const char *msg;
    switch (val)
    {
        case 0:
            msg_Dbg(obj, "certificate key match for %s", host);
            return 0;
        case GNUTLS_E_NO_CERTIFICATE_FOUND:
            msg_Dbg(obj, "no known certificates for %s", host);
            msg = N_("However, the security certificate presented by the "
                "server is unknown and could not be authenticated by any "
                "trusted Certificate Authority.");
            break;
        case GNUTLS_E_CERTIFICATE_KEY_MISMATCH:
            msg_Dbg(obj, "certificate keys mismatch for %s", host);
            msg = N_("However, the security certificate presented by the "
                "server changed since the previous visit and was not "
                "authenticated by any trusted Certificate Authority.");
            break;
        default:
            msg_Err(obj, "certificate key match error for %s: %s", host,
                    gnutls_strerror(val));
            goto error;
    }

    if (vlc_dialog_wait_question(obj, VLC_DIALOG_QUESTION_WARNING,
            _("Abort"), _("View certificate"), NULL,
            _("Insecure site"),
            _("You attempted to reach %s. %s\n"
            "This problem may be stem from an attempt to breach your security, "
            "compromise your privacy, or a configuration error.\n\n"
            "If in doubt, abort now.\n"), host, vlc_gettext(msg)) != 1)
        goto error;

    gnutls_x509_crt_t cert;

    if (gnutls_x509_crt_init (&cert))
        goto error;
    if (gnutls_x509_crt_import (cert, datum, GNUTLS_X509_FMT_DER)
     || gnutls_x509_crt_print (cert, GNUTLS_CRT_PRINT_ONELINE, &desc))
    {
        gnutls_x509_crt_deinit (cert);
        goto error;
    }
    gnutls_x509_crt_deinit (cert);

    val = vlc_dialog_wait_question(obj, VLC_DIALOG_QUESTION_WARNING,
            _("Abort"), _("Accept 24 hours"), _("Accept permanently"),
            _("Insecure site"),
            _("This is the certificate presented by %s:\n%s\n\n"
            "If in doubt, abort now.\n"), host, desc.data);
    gnutls_free (desc.data);

    time_t expiry = 0;
    switch (val)
    {
        case 1:
            time (&expiry);
            expiry += 24 * 60 * 60;
            /* fall through */
        case 2:
            val = gnutls_store_pubkey (NULL, NULL, host, service,
                                       GNUTLS_CRT_X509, datum, expiry, 0);
            if (val)
                msg_Err(obj, "cannot store X.509 certificate: %s",
                         gnutls_strerror (val));
            break;
        default:
            goto error;
    }
    return 0;

error:
    if (alp != NULL)
        free(*alp);
    return -1;
}

static void gnutls_ClientDestroy(vlc_tls_client_t *crd)
{
    gnutls_certificate_credentials_t x509 = crd->sys;

    gnutls_certificate_free_credentials(x509);
}

static const struct vlc_tls_client_operations gnutls_ClientOps =
{
    .open = gnutls_ClientSessionOpen,
    .handshake = gnutls_ClientHandshake,
    .destroy = gnutls_ClientDestroy,
};

/**
 * Initializes a client-side TLS credentials.
 */

static int OpenClient(vlc_tls_client_t *crd)
{
    gnutls_certificate_credentials_t x509;

    gnutls_Banner(VLC_OBJECT(crd));

    int val = gnutls_certificate_allocate_credentials (&x509);
    if (val != 0)
    {
        msg_Err (crd, "cannot allocate credentials: %s",
                 gnutls_strerror (val));
        return VLC_EGENERIC;
    }

    if (var_InheritBool(crd, "gnutls-system-trust"))
    {
        val = gnutls_certificate_set_x509_system_trust(x509);
        if (val < 0)
            msg_Err(crd, "cannot load trusted Certificate Authorities "
                    "from %s: %s", "system", gnutls_strerror(val));
        else
            msg_Dbg(crd, "loaded %d trusted CAs from %s", val, "system");
    }

    char *dir = var_InheritString(crd, "gnutls-dir-trust");
    if (dir != NULL)
    {
        val = gnutls_certificate_set_x509_trust_dir(x509, dir,
                                                    GNUTLS_X509_FMT_PEM);
        if (val < 0)
            msg_Err(crd, "cannot load trusted Certificate Authorities "
                    "from %s: %s", dir, gnutls_strerror(val));
        else
            msg_Dbg(crd, "loaded %d trusted CAs from %s", val, dir);
        free(dir);
    }

    gnutls_certificate_set_verify_flags (x509,
                                         GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT);

    crd->ops = &gnutls_ClientOps;
    crd->sys = x509;
    return VLC_SUCCESS;
}

#ifdef ENABLE_SOUT
/**
 * Server-side TLS credentials private data
 */

typedef struct vlc_tls_creds_sys
{
    gnutls_certificate_credentials_t x509_cred;
    gnutls_dh_params_t dh_params;
} vlc_tls_creds_sys_t;

/**
 * Initializes a server-side TLS session.
 */

static vlc_tls_t *gnutls_ServerSessionOpen(vlc_tls_server_t *crd,
                                           vlc_tls_t *sk,
                                           const char *const *alpn)
{
    vlc_tls_creds_sys_t *sys = crd->sys;
    vlc_tls_gnutls_t *priv = gnutls_SessionOpen(VLC_OBJECT(crd), GNUTLS_SERVER,
                                                sys->x509_cred, sk, alpn);
    return (priv != NULL) ? &priv->tls : NULL;
}

static void gnutls_ServerDestroy(vlc_tls_server_t *crd)
{
    vlc_tls_creds_sys_t *sys = crd->sys;

    /* all sessions depending on the server are now deinitialized */
    gnutls_certificate_free_credentials(sys->x509_cred);
    gnutls_dh_params_deinit(sys->dh_params);
    free(sys);
}

static const struct vlc_tls_server_operations gnutls_ServerOps =
{
    .open = gnutls_ServerSessionOpen,
    .handshake = gnutls_Handshake,
    .destroy = gnutls_ServerDestroy,
};

/**
 * Allocates a whole server's TLS credentials.
 */

static int OpenServer(vlc_tls_server_t *crd, const char *cert, const char *key)
{
    gnutls_Banner(VLC_OBJECT(crd));

    vlc_tls_creds_sys_t *sys = malloc (sizeof (*sys));
    if (unlikely(sys == NULL))
        return VLC_ENOMEM;

    /* Sets server's credentials */
    int val = gnutls_certificate_allocate_credentials (&sys->x509_cred);
    if (val != 0)
    {
        msg_Err (crd, "cannot allocate credentials: %s",
                 gnutls_strerror (val));
        free (sys);
        return VLC_ENOMEM;
    }

    block_t *certblock = block_FilePath(cert, false);
    if (certblock == NULL)
    {
        msg_Err (crd, "cannot read certificate chain from %s: %s", cert,
                 vlc_strerror_c(errno));
        goto error;
    }

    block_t *keyblock = block_FilePath(key, false);
    if (keyblock == NULL)
    {
        msg_Err (crd, "cannot read private key from %s: %s", key,
                 vlc_strerror_c(errno));
        block_Release (certblock);
        goto error;
    }

    gnutls_datum_t pub = {
       .data = certblock->p_buffer,
       .size = certblock->i_buffer,
    }, priv = {
       .data = keyblock->p_buffer,
       .size = keyblock->i_buffer,
    };

    val = gnutls_certificate_set_x509_key_mem (sys->x509_cred, &pub, &priv,
                                                GNUTLS_X509_FMT_PEM);
    block_Release (keyblock);
    block_Release (certblock);
    if (val < 0)
    {
        msg_Err (crd, "cannot load X.509 key: %s", gnutls_strerror (val));
        goto error;
    }

    /* FIXME:
     * - regenerate these regularly
     * - support other cipher suites
     */

    val = gnutls_dh_params_init (&sys->dh_params);
    if (val >= 0)
    {
        gnutls_sec_param_t sec = GNUTLS_SEC_PARAM_MEDIUM;
        unsigned bits = gnutls_sec_param_to_pk_bits (GNUTLS_PK_DH, sec);

        msg_Dbg (crd, "generating Diffie-Hellman %u-bits parameters...", bits);
        val = gnutls_dh_params_generate2 (sys->dh_params, bits);
        if (val == 0)
            gnutls_certificate_set_dh_params (sys->x509_cred,
                                              sys->dh_params);
    }
    if (val < 0)
    {
        msg_Err (crd, "cannot initialize DHE cipher suites: %s",
                 gnutls_strerror (val));
    }

    msg_Dbg (crd, "ciphers parameters loaded");

    crd->ops = &gnutls_ServerOps;
    crd->sys = sys;
    return VLC_SUCCESS;

error:
    gnutls_certificate_free_credentials (sys->x509_cred);
    free (sys);
    return VLC_EGENERIC;
}
#endif

#define SYSTEM_TRUST_TEXT N_("Use system trust database")
#define SYSTEM_TRUST_LONGTEXT N_( \
    "Trust the root certificates of Certificate Authorities stored in " \
    "the operating system trust database to authenticate TLS sessions.")

#define DIR_TRUST_TEXT N_("Trust directory")
#define DIR_TRUST_LONGTEXT N_( \
    "Trust the root certificates of Certificate Authorities stored in " \
    "the specified directory to authenticate TLS sessions.")

#define PRIORITIES_TEXT N_("TLS cipher priorities")
#define PRIORITIES_LONGTEXT N_("Ciphers, key exchange methods, " \
    "hash functions and compression methods can be selected. " \
    "Refer to GNU TLS documentation for detailed syntax.")
static const char *const priorities_values[] = {
    "PERFORMANCE",
    "NORMAL",
    "SECURE128",
    "SECURE256",
};
static const char *const priorities_text[] = {
    N_("Performance (prioritize faster ciphers)"),
    N_("Normal"),
    N_("Secure 128-bit (or higher)"),
    N_("Secure 256-bit (exclude 128-bit ciphers)"),
};

vlc_module_begin ()
    set_shortname( "GNU TLS" )
    set_description( N_("GNU TLS transport layer security") )
    set_capability( "tls client", 1 )
    set_callback(OpenClient)
    set_subcategory( SUBCAT_ADVANCED_NETWORK )
    add_bool("gnutls-system-trust", true, SYSTEM_TRUST_TEXT,
             SYSTEM_TRUST_LONGTEXT)
    add_string("gnutls-dir-trust", NULL, DIR_TRUST_TEXT,
               DIR_TRUST_LONGTEXT)
    add_string ("gnutls-priorities", "NORMAL", PRIORITIES_TEXT,
                PRIORITIES_LONGTEXT)
        change_string_list (priorities_values, priorities_text)
#ifdef ENABLE_SOUT
    add_submodule ()
        set_description( N_("GNU TLS server") )
        set_capability( "tls server", 1 )
        set_subcategory( SUBCAT_ADVANCED_NETWORK )
        set_callback(OpenServer)
#endif
vlc_module_end ()

Messung
CHZG
849710093

¤ Dauer der Verarbeitung: 0.11 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover


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.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Open Source Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Statistik
#Sources=1104842
#Domains=499164