Eine aufbereitete Darstellung der Quelle

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

Benutzer

Quelle  lib.rs

  Sprache: Rust
 

//! Custom derive support for `zeroize`

#![crate_type = "proc-macro"]
#![forbid(unsafe_code)]
#![warn(rust_2018_idioms, trivial_casts, unused_qualifications)]

use proc_macro2::{Ident, TokenStream};
use quote::{format_ident, quote};
use syn::{
    parse::{Parse, ParseStream},
    parse_quote,
    punctuated::Punctuated,
    token::Comma,
    visit::Visit,
    Attribute, Data, DeriveInput, Expr, ExprLit, Field, Fields, Lit, Meta, Result, Variant,
    WherePredicate,
};

/// Name of zeroize-related attributes
const ZEROIZE_ATTR: &str = "zeroize";

/// Derive the `Zeroize` trait.
///
/// Supports the following attributes:
///
/// On the item level:
/// - `#[zeroize(drop)]`: *deprecated* use `ZeroizeOnDrop` instead
/// - `#[zeroize(bound = "T: MyTrait")]`: this replaces any trait bounds
///   inferred by zeroize-derive
///
/// On the field level:
/// - `#[zeroize(skip)]`: skips this field or variant when calling `zeroize()`
#[proc_macro_derive(Zeroize, attributes(zeroize))]
pub fn derive_zeroize(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
    derive_zeroize_impl(syn::parse_macro_input!(input as DeriveInput)).into()
}

fn derive_zeroize_impl(input: DeriveInput) -> TokenStream {
    let attributes = ZeroizeAttrs::parse(&input);

    let mut generics = input.generics.clone();

    let extra_bounds = match attributes.bound {
        Some(bounds) => bounds.0,
        None => attributes
            .auto_params
            .iter()
            .map(|type_param| -> WherePredicate {
                parse_quote! {#type_param: Zeroize}
            })
            .collect(),
    };

    generics.make_where_clause().predicates.extend(extra_bounds);

    let ty_name = &input.ident;

    let (impl_gen, type_gen, where_) = generics.split_for_impl();

    let drop_impl = if attributes.drop {
        quote! {
            #[doc(hidden)]
            impl #impl_gen Drop for #ty_name #type_gen #where_ {
                fn drop(&mut self) {
                    self.zeroize()
                }
            }
        }
    } else {
        quote! {}
    };

    let zeroizers = generate_fields(&input, quote! { zeroize });
    let zeroize_impl = quote! {
        impl #impl_gen ::zeroize::Zeroize for #ty_name #type_gen #where_ {
            fn zeroize(&mut self) {
                #zeroizers
            }
        }
    };

    quote! {
        #zeroize_impl
        #drop_impl
    }
}

/// Derive the `ZeroizeOnDrop` trait.
///
/// Supports the following attributes:
///
/// On the field level:
/// - `#[zeroize(skip)]`: skips this field or variant when calling `zeroize()`
#[proc_macro_derive(ZeroizeOnDrop, attributes(zeroize))]
pub fn derive_zeroize_on_drop(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
    derive_zeroize_on_drop_impl(syn::parse_macro_input!(input as DeriveInput)).into()
}

fn derive_zeroize_on_drop_impl(input: DeriveInput) -> TokenStream {
    let zeroizers = generate_fields(&input, quote! { zeroize_or_on_drop });

    let (impl_gen, type_gen, where_) = input.generics.split_for_impl();
    let name = input.ident.clone();

    let drop_impl = quote! {
        impl #impl_gen Drop for #name #type_gen #where_ {
            fn drop(&mut self) {
                use ::zeroize::__internal::AssertZeroize;
                use ::zeroize::__internal::AssertZeroizeOnDrop;
                #zeroizers
            }
        }
    };
    let zeroize_on_drop_impl = impl_zeroize_on_drop(&input);

    quote! {
        #drop_impl
        #zeroize_on_drop_impl
    }
}

/// Custom derive attributes for `Zeroize`
#[derive(Default)]
struct ZeroizeAttrs {
    /// Derive a `Drop` impl which calls zeroize on this type
    drop: bool,
    /// Custom bounds as defined by the user
    bound: Option<Bounds>,
    /// Type parameters in use by fields
    auto_params: Vec<Ident>,
}

/// Parsing helper for custom bounds
struct Bounds(Punctuated<WherePredicate, Comma>);

impl Parse for Bounds {
    fn parse(input: ParseStream<'_>) -> Result<Self> {
        Ok(Self(Punctuated::parse_terminated(input)?))
    }
}

struct BoundAccumulator<'a> {
    generics: &'a syn::Generics,
    params: Vec<Ident>,
}

impl<'ast> Visit<'ast> for BoundAccumulator<'ast> {
    fn visit_path(&mut self, path: &'ast syn::Path) {
        if path.segments.len() != 1 {
            return;
        }

        if let Some(segment) = path.segments.first() {
            for param in &self.generics.params {
                if let syn::GenericParam::Type(type_param) = param {
                    if type_param.ident == segment.ident && !self.params.contains(&segment.ident) {
                        self.params.push(type_param.ident.clone());
                    }
                }
            }
        }
    }
}

impl ZeroizeAttrs {
    /// Parse attributes from the incoming AST
    fn parse(input: &DeriveInput) -> Self {
        let mut result = Self::default();
        let mut bound_accumulator = BoundAccumulator {
            generics: &input.generics,
            params: Vec::new(),
        };

        for attr in &input.attrs {
            result.parse_attr(attr, None, None);
        }

        match &input.data {
            syn::Data::Enum(enum_) => {
                for variant in &enum_.variants {
                    for attr in &variant.attrs {
                        result.parse_attr(attr, Some(variant), None);
                    }
                    for field in &variant.fields {
                        for attr in &field.attrs {
                            result.parse_attr(attr, Some(variant), Some(field));
                        }
                        if !attr_skip(&field.attrs) {
                            bound_accumulator.visit_type(&field.ty);
                        }
                    }
                }
            }
            syn::Data::Struct(struct_) => {
                for field in &struct_.fields {
                    for attr in &field.attrs {
                        result.parse_attr(attr, None, Some(field));
                    }
                    if !attr_skip(&field.attrs) {
                        bound_accumulator.visit_type(&field.ty);
                    }
                }
            }
            syn::Data::Union(union_) => panic!("Unsupported untagged union {:?}", union_),
        }

        result.auto_params = bound_accumulator.params;

        result
    }

    /// Parse attribute and handle `#[zeroize(...)]` attributes
    fn parse_attr(&mut self, attr: &Attribute, variant: Option<&Variant>, binding: Option<&Field>) {
        let meta_list = match &attr.meta {
            Meta::List(list) => list,
            _ => return,
        };

        // Ignore any non-zeroize attributes
        if !meta_list.path.is_ident(ZEROIZE_ATTR) {
            return;
        }

        for meta in attr
            .parse_args_with(Punctuated::<Meta, Comma>::parse_terminated)
            .unwrap_or_else(|e| panic!("error parsing attribute: {:?} ({})", attr, e))
        {
            self.parse_meta(&meta, variant, binding);
        }
    }

    /// Parse `#[zeroize(...)]` attribute metadata (e.g. `drop`)
    fn parse_meta(&mut self, meta: &Meta, variant: Option<&Variant>, binding: Option<&Field>) {
        if meta.path().is_ident("drop") {
            assert!(!self.drop, "duplicate #[zeroize] drop flags");

            match (variant, binding) {
                (_variant, Some(_binding)) => {
                    // structs don't have a variant prefix, and only structs have bindings outside of a variant
                    let item_kind = match variant {
                        Some(_) => "enum",
                        None => "struct",
                    };
                    panic!(
                        concat!(
                            "The #[zeroize(drop)] attribute is not allowed on {} fields. ",
                            "Use it on the containing {} instead.",
                        ),
                        item_kind, item_kind,
                    )
                }
                (Some(_variant), None) => panic!(concat!(
                    "The #[zeroize(drop)] attribute is not allowed on enum variants. ",
                    "Use it on the containing enum instead.",
                )),
                (None, None) => (),
            };

            self.drop = true;
        } else if meta.path().is_ident("bound") {
            assert!(self.bound.is_none(), "duplicate #[zeroize] bound flags");

            match (variant, binding) {
                (_variant, Some(_binding)) => {
                    // structs don't have a variant prefix, and only structs have bindings outside of a variant
                    let item_kind = match variant {
                        Some(_) => "enum",
                        None => "struct",
                    };
                    panic!(
                        concat!(
                            "The #[zeroize(bound)] attribute is not allowed on {} fields. ",
                            "Use it on the containing {} instead.",
                        ),
                        item_kind, item_kind,
                    )
                }
                (Some(_variant), None) => panic!(concat!(
                    "The #[zeroize(bound)] attribute is not allowed on enum variants. ",
                    "Use it on the containing enum instead.",
                )),
                (None, None) => {
                    if let Meta::NameValue(meta_name_value) = meta {
                        if let Expr::Lit(ExprLit {
                            lit: Lit::Str(lit), ..
                        }) = &meta_name_value.value
                        {
                            if lit.value().is_empty() {
                                self.bound = Some(Bounds(Punctuated::new()));
                            } else {
                                self.bound = Some(lit.parse().unwrap_or_else(|e| {
                                    panic!("error parsing bounds: {:?} ({})", lit, e)
                                }));
                            }

                            return;
                        }
                    }

                    panic!(concat!(
                        "The #[zeroize(bound)] attribute expects a name-value syntax with a string literal value.",
                        "E.g. #[zeroize(bound = \"T: MyTrait\")]."
                    ))
                }
            }
        } else if meta.path().is_ident("skip") {
            if variant.is_none() && binding.is_none() {
                panic!(concat!(
                    "The #[zeroize(skip)] attribute is not allowed on a `struct` or `enum`. ",
                    "Use it on a field or variant instead.",
                ))
            }
        } else {
            panic!("unknown #[zeroize] attribute type: {:?}", meta.path());
        }
    }
}

fn field_ident(n: usize, field: &Field) -> Ident {
    if let Some(ref name) = field.ident {
        name.clone()
    } else {
        format_ident!("__zeroize_field_{}", n)
    }
}

fn generate_fields(input: &DeriveInput, method: TokenStream) -> TokenStream {
    let input_id = &input.ident;
    let fields: Vec<_> = match input.data {
        Data::Enum(ref enum_) => enum_
            .variants
            .iter()
            .filter_map(|variant| {
                if attr_skip(&variant.attrs) {
                    if variant.fields.iter().any(|field| attr_skip(&field.attrs)) {
                        panic!("duplicate #[zeroize] skip flags")
                    }
                    None
                } else {
                    let variant_id = &variant.ident;
                    Some((quote! { #input_id :: #variant_id }, &variant.fields))
                }
            })
            .collect(),
        Data::Struct(ref struct_) => vec![(quote! { #input_id }, &struct_.fields)],
        Data::Union(ref union_) => panic!("Cannot generate fields for untagged union {:?}", union_),
    };

    let arms = fields.into_iter().map(|(name, fields)| {
        let method_field = fields.iter().enumerate().filter_map(|(n, field)| {
            if attr_skip(&field.attrs) {
                None
            } else {
                let name = field_ident(n, field);
                Some(quote! { #name.#method() })
            }
        });

        let field_bindings = fields
            .iter()
            .enumerate()
            .map(|(n, field)| field_ident(n, field));

        let binding = match fields {
            Fields::Named(_) => quote! {
                #name { #(#field_bindings),* }
            },
            Fields::Unnamed(_) => quote! {
                #name ( #(#field_bindings),* )
            },
            Fields::Unit => quote! {
                #name
            },
        };

        quote! {
            #[allow(unused_variables)]
            #binding => {
                #(#method_field);*
            }
        }
    });

    quote! {
        match self {
            #(#arms),*
            _ => {}
        }
    }
}

fn attr_skip(attrs: &[Attribute]) -> bool {
    let mut result = false;
    for attr in attrs.iter().map(|attr| &attr.meta) {
        if let Meta::List(list) = attr {
            if list.path.is_ident(ZEROIZE_ATTR) {
                for meta in list
                    .parse_args_with(Punctuated::<Meta, Comma>::parse_terminated)
                    .unwrap_or_else(|e| panic!("error parsing attribute: {:?} ({})", list, e))
                {
                    if let Meta::Path(path) = meta {
                        if path.is_ident("skip") {
                            assert!(!result, "duplicate #[zeroize] skip flags");
                            result = true;
                        }
                    }
                }
            }
        }
    }
    result
}

fn impl_zeroize_on_drop(input: &DeriveInput) -> TokenStream {
    let name = input.ident.clone();
    let (impl_gen, type_gen, where_) = input.generics.split_for_impl();
    quote! {
        #[doc(hidden)]
        impl #impl_gen ::zeroize::ZeroizeOnDrop for #name #type_gen #where_ {}
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[track_caller]
    fn test_derive(
        f: impl Fn(DeriveInput) -> TokenStream,
        input: TokenStream,
        expected_output: TokenStream,
    ) {
        let output = f(syn::parse2(input).unwrap());
        assert_eq!(format!("{output}"), format!("{expected_output}"));
    }

    #[track_caller]
    fn parse_zeroize_test(unparsed: &str) -> TokenStream {
        derive_zeroize_impl(syn::parse_str(unparsed).expect("Failed to parse test input"))
    }

    #[test]
    fn zeroize_without_drop() {
        test_derive(
            derive_zeroize_impl,
            quote! {
                struct Z {
                    a: String,
                    b: Vec<u8>,
                    c: [u8; 3],
                }
            },
            quote! {
                impl ::zeroize::Zeroize for Z {
                    fn zeroize(&mut self) {
                        match self {
                            #[allow(unused_variables)]
                            Z { a, b, c } => {
                                a.zeroize();
                                b.zeroize();
                                c.zeroize()
                            }
                            _ => {}
                        }
                    }
                }
            },
        )
    }

    #[test]
    fn zeroize_with_drop() {
        test_derive(
            derive_zeroize_impl,
            quote! {
                #[zeroize(drop)]
                struct Z {
                    a: String,
                    b: Vec<u8>,
                    c: [u8; 3],
                }
            },
            quote! {
                impl ::zeroize::Zeroize for Z {
                    fn zeroize(&mut self) {
                        match self {
                            #[allow(unused_variables)]
                            Z { a, b, c } => {
                                a.zeroize();
                                b.zeroize();
                                c.zeroize()
                            }
                            _ => {}
                        }
                    }
                }
                #[doc(hidden)]
                impl Drop for Z {
                    fn drop(&mut self) {
                        self.zeroize()
                    }
                }
            },
        )
    }

    #[test]
    fn zeroize_with_skip() {
        test_derive(
            derive_zeroize_impl,
            quote! {
                struct Z {
                    a: String,
                    b: Vec<u8>,
                    #[zeroize(skip)]
                    c: [u8; 3],
                }
            },
            quote! {
                impl ::zeroize::Zeroize for Z {
                    fn zeroize(&mut self) {
                        match self {
                            #[allow(unused_variables)]
                            Z { a, b, c } => {
                                a.zeroize();
                                b.zeroize()
                            }
                            _ => {}
                        }
                    }
                }
            },
        )
    }

    #[test]
    fn zeroize_with_bound() {
        test_derive(
            derive_zeroize_impl,
            quote! {
                #[zeroize(bound = "T: MyTrait")]
                struct Z<T>(T);
            },
            quote! {
                impl<T> ::zeroize::Zeroize for Z<T> where T: MyTrait {
                    fn zeroize(&mut self) {
                        match self {
                            #[allow(unused_variables)]
                            Z(__zeroize_field_0) => {
                                __zeroize_field_0.zeroize()
                            }
                            _ => {}
                        }
                    }
                }
            },
        )
    }

    #[test]
    fn zeroize_only_drop() {
        test_derive(
            derive_zeroize_on_drop_impl,
            quote! {
                struct Z {
                    a: String,
                    b: Vec<u8>,
                    c: [u8; 3],
                }
            },
            quote! {
                impl Drop for Z {
                    fn drop(&mut self) {
                        use ::zeroize::__internal::AssertZeroize;
                        use ::zeroize::__internal::AssertZeroizeOnDrop;
                        match self {
                            #[allow(unused_variables)]
                            Z { a, b, c } => {
                                a.zeroize_or_on_drop();
                                b.zeroize_or_on_drop();
                                c.zeroize_or_on_drop()
                            }
                            _ => {}
                        }
                    }
                }
                #[doc(hidden)]
                impl ::zeroize::ZeroizeOnDrop for Z {}
            },
        )
    }

    #[test]
    fn zeroize_on_struct() {
        parse_zeroize_test(stringify!(
            #[zeroize(drop)]
            struct Z {
                a: String,
                b: Vec<u8>,
                c: [u8; 3],
            }
        ));
    }

    #[test]
    fn zeroize_on_enum() {
        parse_zeroize_test(stringify!(
            #[zeroize(drop)]
            enum Z {
                Variant1 { a: String, b: Vec<u8>, c: [u8; 3] },
            }
        ));
    }

    #[test]
    #[should_panic(expected = "#[zeroize(drop)] attribute is not allowed on struct fields")]
    fn zeroize_on_struct_field() {
        parse_zeroize_test(stringify!(
            struct Z {
                #[zeroize(drop)]
                a: String,
                b: Vec<u8>,
                c: [u8; 3],
            }
        ));
    }

    #[test]
    #[should_panic(expected = "#[zeroize(drop)] attribute is not allowed on struct fields")]
    fn zeroize_on_tuple_struct_field() {
        parse_zeroize_test(stringify!(
            struct Z(#[zeroize(drop)] String);
        ));
    }

    #[test]
    #[should_panic(expected = "#[zeroize(drop)] attribute is not allowed on struct fields")]
    fn zeroize_on_second_field() {
        parse_zeroize_test(stringify!(
            struct Z {
                a: String,
                #[zeroize(drop)]
                b: Vec<u8>,
                c: [u8; 3],
            }
        ));
    }

    #[test]
    #[should_panic(expected = "#[zeroize(drop)] attribute is not allowed on enum fields")]
    fn zeroize_on_tuple_enum_variant_field() {
        parse_zeroize_test(stringify!(
            enum Z {
                Variant(#[zeroize(drop)] String),
            }
        ));
    }

    #[test]
    #[should_panic(expected = "#[zeroize(drop)] attribute is not allowed on enum fields")]
    fn zeroize_on_enum_variant_field() {
        parse_zeroize_test(stringify!(
            enum Z {
                Variant {
                    #[zeroize(drop)]
                    a: String,
                    b: Vec<u8>,
                    c: [u8; 3],
                },
            }
        ));
    }

    #[test]
    #[should_panic(expected = "#[zeroize(drop)] attribute is not allowed on enum fields")]
    fn zeroize_on_enum_second_variant_field() {
        parse_zeroize_test(stringify!(
            enum Z {
                Variant1 {
                    a: String,
                    b: Vec<u8>,
                    c: [u8; 3],
                },
                Variant2 {
                    #[zeroize(drop)]
                    a: String,
                    b: Vec<u8>,
                    c: [u8; 3],
                },
            }
        ));
    }

    #[test]
    #[should_panic(expected = "#[zeroize(drop)] attribute is not allowed on enum variants")]
    fn zeroize_on_enum_variant() {
        parse_zeroize_test(stringify!(
            enum Z {
                #[zeroize(drop)]
                Variant,
            }
        ));
    }

    #[test]
    #[should_panic(expected = "#[zeroize(drop)] attribute is not allowed on enum variants")]
    fn zeroize_on_enum_second_variant() {
        parse_zeroize_test(stringify!(
            enum Z {
                Variant1,
                #[zeroize(drop)]
                Variant2,
            }
        ));
    }

    #[test]
    #[should_panic(
        expected = "The #[zeroize(skip)] attribute is not allowed on a `struct` or `enum`. Use it on a field or variant instead."
    )]
    fn zeroize_skip_on_struct() {
        parse_zeroize_test(stringify!(
            #[zeroize(skip)]
            struct Z {
                a: String,
                b: Vec<u8>,
                c: [u8; 3],
            }
        ));
    }

    #[test]
    #[should_panic(
        expected = "The #[zeroize(skip)] attribute is not allowed on a `struct` or `enum`. Use it on a field or variant instead."
    )]
    fn zeroize_skip_on_enum() {
        parse_zeroize_test(stringify!(
            #[zeroize(skip)]
            enum Z {
                Variant1,
                Variant2,
            }
        ));
    }

    #[test]
    #[should_panic(expected = "duplicate #[zeroize] skip flags")]
    fn zeroize_duplicate_skip() {
        parse_zeroize_test(stringify!(
            struct Z {
                a: String,
                #[zeroize(skip)]
                #[zeroize(skip)]
                b: Vec<u8>,
                c: [u8; 3],
            }
        ));
    }

    #[test]
    #[should_panic(expected = "duplicate #[zeroize] skip flags")]
    fn zeroize_duplicate_skip_list() {
        parse_zeroize_test(stringify!(
            struct Z {
                a: String,
                #[zeroize(skip, skip)]
                b: Vec<u8>,
                c: [u8; 3],
            }
        ));
    }

    #[test]
    #[should_panic(expected = "duplicate #[zeroize] skip flags")]
    fn zeroize_duplicate_skip_enum() {
        parse_zeroize_test(stringify!(
            enum Z {
                #[zeroize(skip)]
                Variant {
                    a: String,
                    #[zeroize(skip)]
                    b: Vec<u8>,
                    c: [u8; 3],
                },
            }
        ));
    }

    #[test]
    #[should_panic(expected = "duplicate #[zeroize] bound flags")]
    fn zeroize_duplicate_bound() {
        parse_zeroize_test(stringify!(
            #[zeroize(bound = "T: MyTrait")]
            #[zeroize(bound = "")]
            struct Z<T>(T);
        ));
    }

    #[test]
    #[should_panic(expected = "duplicate #[zeroize] bound flags")]
    fn zeroize_duplicate_bound_list() {
        parse_zeroize_test(stringify!(
            #[zeroize(bound = "T: MyTrait", bound = "")]
            struct Z<T>(T);
        ));
    }

    #[test]
    #[should_panic(
        expected = "The #[zeroize(bound)] attribute is not allowed on struct fields. Use it on the containing struct instead."
    )]
    fn zeroize_bound_struct() {
        parse_zeroize_test(stringify!(
            struct Z<T> {
                #[zeroize(bound = "T: MyTrait")]
                a: T,
            }
        ));
    }

    #[test]
    #[should_panic(
        expected = "The #[zeroize(bound)] attribute is not allowed on enum variants. Use it on the containing enum instead."
    )]
    fn zeroize_bound_enum() {
        parse_zeroize_test(stringify!(
            enum Z<T> {
                #[zeroize(bound = "T: MyTrait")]
                A(T),
            }
        ));
    }

    #[test]
    #[should_panic(
        expected = "The #[zeroize(bound)] attribute is not allowed on enum fields. Use it on the containing enum instead."
    )]
    fn zeroize_bound_enum_variant_field() {
        parse_zeroize_test(stringify!(
            enum Z<T> {
                A {
                    #[zeroize(bound = "T: MyTrait")]
                    a: T,
                },
            }
        ));
    }

    #[test]
    #[should_panic(
        expected = "The #[zeroize(bound)] attribute expects a name-value syntax with a string literal value.E.g. #[zeroize(bound = \"T: MyTrait\")]."
    )]
    fn zeroize_bound_no_value() {
        parse_zeroize_test(stringify!(
            #[zeroize(bound)]
            struct Z<T>(T);
        ));
    }

    #[test]
    #[should_panic(expected = "error parsing bounds: LitStr { token: \"T\" } (expected `:`)")]
    fn zeroize_bound_no_where_predicate() {
        parse_zeroize_test(stringify!(
            #[zeroize(bound = "T")]
            struct Z<T>(T);
        ));
    }
}

Messung V0.5 in Prozent
C=96 H=99 G=97

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

*© 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.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik