Samstag, 26. August 2006

HOWTO_ Asterisk 1.2 als SIP-Proxy für Sipgate, QSC IPfonie und dus.net

Neulich erinnerte ich mich daran, dass ich Monat für Monat ungenutzte Freiminuten bei QSC IPfonie privat, dem kostenlosen VoIP-Anschluss, welcher bei meinem Q-DSL Internetanschluss inklusive ist, verfallen lasse.

Bei VoIP bin ich recht früh eingestiegen, d.h. zu dem Zeitpunkt, als in Deutschland die erste Hardware dafür nicht mehr preislich weit im dreistelligen Bereich lag. Daher nenne ich ein Grandstream Budge-Tone 101 mein Eigen.
Dieses unterstützt aber nur einen Account, und komplett von Sipgate zu QSC möchte ich auch nicht wechseln, dagegen sprechen einfach zu viele Gründe: Rufnummer, Anrufhistorie, Adressbuch, und und und.

Dann erinnerte ich mich an die Software-Telefonanlage Asterisk und einen Einführungsartikel in der c't. Und so fing ich langsam an, mich zu erkundigen, was Asterisk im SIP-Bereich leistet und ob es für meine Zwecke dienlich sein kann.

Offensichtlich ist dies der Fall, was sollte sonst dieser Artikel. Also, folgende Eckdaten beschreiben meine Installation:
  • Läuft auf einem Debian GNU/Linux Server
  • Verwendet ausschließlich SIP
  • Ist bei den drei Providern Sipgate, QSC sowie dus.net registriert
  • Annahme von Anrufen von allen Providern
  • ausgehende Anrufe über alle Provider
  • einfaches LCR (Least Cost Routing), welches ausgehende Anrufe
    • ins Festnetz in der Nebenzeit über QSC,
    • ins Festnetz in der Hauptzeit über dus.net und
    • alle anderen Anrufe über sipgate leitet.
  • Direkte Wahl eines Anbieters über eine führende Ziffer
    • 3 für dus.net
    • 4 für QSC IPfonie
    • 7 für Sipgate
Alles lässt sich über 2 Konfigurationsdateien regeln, welche ich im Folgenden kurz erkläre.

sip.conf:
;
; SIP Configuration for Asterisk
;
; Syntax for specifying a SIP device in extensions.conf is
; SIP/devicename where devicename is defined in a section below.
;
; You may also use
; SIP/username@domain to call any SIP user on the Internet
; (Don't forget to enable DNS SRV records if you want to use this)
;
; If you define a SIP proxy as a peer below, you may call
; SIP/proxyhostname/user or SIP/user@proxyhostname
; where the proxyhostname is defined in a section below
;
; Useful CLI commands to check peers/users:
;   sip show peers              Show all SIP peers (including friends)
;   sip show users              Show all SIP users (including friends)
;   sip show registry           Show status of hosts we register with
;
;   sip debug                   Show all SIP messages
;
;   reload chan_sip.so          Reload configuration file
;                               Active SIP peers will not be reconfigured
;

[general]
context=from-sip                ; Default context for incoming calls

;recordhistory=yes              ; Record SIP history by default (see sip history / sip no history)
;realm=qsc.de                   ; Realm for digest authentication
                                ; defaults to "asterisk"
                                ; Realms MUST be globally unique according to RFC 3261
                                ; Set this to your host name or domain name
port=5060                       ; UDP Port to bind to (SIP standard port is 5060)
bindaddr=0.0.0.0                ; IP address to bind to (0.0.0.0 binds to all)
srvlookup=yes                   ; Enable DNS SRV lookups on outbound calls
                                ; Note: Asterisk only uses the first host
                                ; in SRV records
                                ; Disabling DNS SRV lookups disables the
                                ; ability to place SIP calls based on domain
                                ; names to some other SIP users on the Internet
;pedantic=yes                   ; Enable slow, pedantic checking for Pingtel
                                ; and multiline formatted headers for strict
                                ; SIP compatibility (defaults to "no")
;tos=184                        ; Set IP QoS to either a keyword or numeric val
;tos=lowdelay                   ; lowdelay,throughput,reliability,mincost,none
;maxexpirey=3600                ; Max length of incoming registration we allow
;defaultexpirey=120             ; Default length of incoming/outoing registration
;notifymimetype=text/plain      ; Allow overriding of mime type in MWI NOTIFY
;videosupport=yes               ; Turn on support for SIP video

disallow=all                    ; First disallow all codecs
; use "show translation" for installed codecs
;allow=gsm
;allow=g726
;allow=adpcm
;allow=slin
;allow=lpc10
allow=alaw
allow=ulaw                      ; Allow codecs in order of preference
;allow=ilbc                     ; Note: codec order is respected only in [general]
;musicclass=default             ; Sets the default music on hold class for all SIP calls
                                ; This may also be set for individual users/peers
;language=en                    ; Default language setting for all users/peers
                                ; This may also be set for individual users/peers
;relaxdtmf=yes                  ; Relax dtmf handling
;rtptimeout=60                  ; Terminate call if 60 seconds of no RTP activity
                                ; when we're not on hold
;rtpholdtimeout=300             ; Terminate call if 300 seconds of no RTP activity
                                ; when we're on hold (must be > rtptimeout)
;trustrpid = no                 ; If Remote-Party-ID should be trusted
;progressinband=no              ; If we should generate in-band ringing always
useragent=Asterisk 1.2          ; Allows you to change the user agent string
nat=yes                         ; NAT settings
                                ; yes = Always ignore info and assume NAT
                                ; no = Use NAT mode only according to RFC3581
                                ; never = Never attempt NAT mode or RFC3581 support
                                ; route = Assume NAT, don't send rport (work around more UNIDEN bugs)
;promiscredir = no      ; If yes, allows 302 or REDIR to non-local SIP address
;                       ; Note that promiscredir when redirects are made to the
;                       ; local system will cause loops since SIP is incapable
;                       ; of performing a "hairpin" call.
;
; Examples:
;externip = 83.236.156.2        ; Address that we're going to put in outbound SIP messages
                                ; if we're behind a NAT

                                ; The externip and localnet is used
                                ; when registering and communicating with other proxies
                                ; that we're registered with
                                ; You may add multiple local networks.  A reasonable set of defaults
                                ; are:
;localnet=10.0.0.10
;localnet=192.168.0.0/255.255.0.0; All RFC 1918 addresses are local networks
;localnet=10.0.0.0/255.255.255.0        ; Also RFC1918
;localnet=172.16.0.0/12         ; Another RFC1918 with CIDR notation


register => 030123456789:MYPASSWD@sip.qsc.de/030123456789       ; Register 030123456789 @ QSC
register => SIP-ID:SIP-PWD@sipgate.de/SIP-ID                 ; Register SIP-ID @ sipgate
register => Auth-ID:Kennwort@voip.dus.net/Auth-ID     ; Register AuthID @ dus.net


; ##### SIP PROVIDERS #####
[qsc]
type=peer
context=qsc-incoming
insecure=very
username=030123456789
secret=MYPASSWD
fromuser=030123456789
host=sip.qsc.de
dtmfmode=rfc2833
canredirect=no


[sipgate]
type=peer
context=sip-incoming
insecure=very
username=SIP-ID
secret=SIP-PWD
fromuser=SIP-ID
fromdomain=sipgate.de
host=sipgate.de
qualify=no
dtmfmode=info
canredirect=no


[dus]
type=peer
context=dus-incoming
insecure=very
username=Auth-ID
secret=Kennwort
fromuser=Auth-ID
host=proxy.dus.net
dtmfmode=rfc2833
canredirect=no
language=de


; ##### SIP CLIENTS #####
[client01]
; ~~~ Grandstream Budge-Tone 101 ~~~
type=friend
context=intern-client01
username=client01
secret=password01
host=dynamic
dtmfmode=info


[client02]
type=friend
context=intern-client02
username=client02
secret=password02
host=dynamic
dtmfmode=info
Ja, was bedeutet das jetzt alles? [name] bezeichnet einen sogenannten Kontext, für den die folgenden Zuweisungen der Art Variable=Wert gelten.

Die Kontexte [qsc], [sipgate] und [dus] sind meine drei VoIP-Anbieter. Die Daten sind eigentlich selbsterklärend. Zusätzlich benötigt jeder Anbieter noch eine Registrierung, bei der sich Asterisk als SIP-Client anmeldet. Dies wird durch die Befehle register => AUTH[:PWD]@HOST[:PORT]/AUTH vorgenommen. Für QSC müssen 030123456789 durch die eigene QSC-Rufnummer und MYPASSWD durch das vergebene Passwort, für sipgate SIP-ID durch die Sipgate-ID und SIP-PWD durch das SIP-Passwort, für dus.net Auth-ID und Kennwort durch die entsprechenden Daten ersetzt werden.

[client01] und [client02] konfigurieren zwei locale SIP-Clients, in meinem Falle das Grandstream Budge-Tone 101 sowie ein weiterer, welchen ich mit X-Lite zum Testen verwende.

Wichtig sind hier (eigentlich) nur noch die vergebenen Kontexte context=. Diese werde in der Datei extensions.conf ausgeführt, und bei einem von einem hier festgelegten Kontexte ausgeführten Anruf angesprungen. Kommt also ein Anruf auf die Sipgate-Rufnummer, so wird der Kontext sip-incoming angesprungen.


extensions.conf:
;
; The "General" category is for certain variables.  All other categories
; are interpreted as extension contexts
;
[general]

static=yes
writeprotect=no
autofallthrough=yes
;clearglobalvars=no
;priorityjumping=no

;
[globals]

USERNAME=cypressor                  ; display name for  caller ID in outgoing calls

QSCUSERID=030123456789                          ; caller ID number for QSC calls
QSCPREFIX=4                                     ; 4-GHI (Ipfonie)
DUSUSERID=Auth-ID                               ; caller ID number for dus.net calls
DUSPREFIX=3                                     ; 3-DEF (Dus.net)
SIPUSERID=SIP-ID                                ; caller ID number for sipgate calls
SIPPREFIX=7                                     ; 7-PQRS (Sipgate)

DEFAULTCONTEXT=sipgate-out                      ; default outgoing context for all calls
                                                ; that could not be routed

PHONE1=SIP/client01                             ; identification of phone no. 1
PHONE1NAME=(local) Grandstream BT101            ; display name, will be set if phone does not provide a name itself
PHONE1NUMBER=101                                ; extension number of phone no. 1

PHONE2=SIP/client02
PHONE2NAME=(local) Phone 2
PHONE2NUMBER=102



; #############################################
; #####  CALLS MADE FROM INTERNAL PHONES  #####
[intern-client01]
exten => _X.,1,NoOp(${CALLERID})
exten => _X.,2,GotoIf($["${CALLERIDNAME}" != ""]?3:5)
exten => _X.,3,SetCallerID("${CALLERIDNAME}" <${PHONE1NUMBER}>)
exten => _X.,4,Goto(6)
exten => _X.,5,SetCallerID("${PHONE1NAME}" <${PHONE1NUMBER}>)
exten => _X.,6,Goto(intern,${EXTEN},1)
include => intern

[intern-client02]
exten => _X.,1,NoOp(${CALLERID})
exten => _X.,2,GotoIf($["${CALLERIDNAME}" != ""]?3:5)
exten => _X.,3,SetCallerID("${CALLERIDNAME}" <${PHONE2NUMBER}>)
exten => _X.,4,Goto(6)
exten => _X.,5,SetCallerID("${PHONE2NAME}" <${PHONE2NUMBER}>)
exten => _X.,6,Goto(intern,${EXTEN},1)
include => intern

[intern]
; check with exceptions
include => out-exceptions
; try to route calls with available providers
include => out-route
; unrouteable extension, try default
include => default-out
; no outgoing extension available, try local
include => local
; nothing found yet? playback error
exten => i,1,Playback(invalid)
exten => i,2,Hangup



; #############################################
; ##### CALLS GOING OUT TO SIP-PROVIDERS  #####
[out-exceptions]
; exceptional extensions for outgoing calls
; especially numbers not beginning with 0 to prevent them
; from being routed to another (not-default) provider
include => out-mailbox
exten => 110,1,GoSub(sipgate-out,${EXTEN},1)

[out-mailbox]
; external mailboxes
; sipgate
exten => 50000,1,GoSub(sipgate-out,${EXTEN},1)

[out-route]
; route outgoing (external) calls to provider
; use the first number 2-9 to choose provider
; try numbers beginning with 1 in local context
exten => _1.,1,GoSub(local,${EXTEN},1)
exten => _${DUSPREFIX}.,1,GoSub(dus-out,${EXTEN:1},1)
exten => _${QSCPREFIX}.,1,GoSub(qsc-out,${EXTEN:1},1)
exten => _${SIPPREFIX}.,1,GoSub(sipgate-out,${EXTEN:1},1)
; weekend, always Off-Peak
exten => _0[2-9]X.,1,GoSubIf($["${IFTIME(*|sat-sun|*|*?true)}" != ""]?qsc-out,${EXTEN},1)
 ; mo-fr, Off-Peak is 18-9h
exten => _0[2-9]X.,2,GoSubIf($["${IFTIME(18:00-8:50|mon-fri|*|*?true)}" != ""]?qsc-out,${EXTEN},1)
; On-Peak, use dus.net
exten => _0[2-9]X.,3,GoSub(dus-out,${EXTEN},1)
; 01802/01804 not supported by sipgate
exten => _0180[2,4].,1,GoSub(dus-out,${EXTEN},1)

[default-out]
; include default for outgoing calls
include => ${DEFAULTCONTEXT}

[qsc-out]
exten => _X.,1,SetCallerID("${USERNAME}" <${QSCUSERID}>|a)
exten => _X.,2,Dial(SIP/${EXTEN}@qsc,90,tr)
exten => _X.,3,Hangup
; Return
exten => i,1,Return

[dus-out]
exten => _X.,1,SetCallerID("${USERNAME}" <${DUSUSERID}>)
exten => _X.,2,Dial(SIP/${EXTEN}@dus,60)
exten => _X.,3,Hangup
; Return
exten => i,1,Return

[sipgate-out]
exten => _X.,1,SetCallerID("${USERNAME}" <${SIPUSERID}>)
exten => _X.,2,Dial(SIP/${EXTEN}@sipgate,30,trg)
exten => _X.,3,Hangup
; Return
exten => i,1,Return


; #############################################
; ##### LOCAL CALLS TO OTHER PHONES #####
[local]
; Phone 1
exten => ${PHONE1NUMBER},1,Dial(${PHONE1},60)
exten => ${PHONE1NUMBER},2,Hangup
; Phone 2
exten => ${PHONE2NUMBER},1,Dial(${PHONE2},60)
exten => ${PHONE2NUMBER},2,Hangup
; Return
exten => i,1,Return

; #############################################
; ##### INCOMING CALLS ON ASTERISK #####
[from-sip]
exten => s,1,NoOp(Incoming asterisk call\, no extension)
exten => s,2,NoOp(Setting pseudo extension 00 to call)
exten => s,3,NoOp(${STRFTIME(,,%H)})
exten => s,4,GoTo(00,1)
include => ring-all


; #############################################
; ##### INCOMING CALLS FROM SIP-PROVIDERS #####
[qsc-incoming]
include => ring-all

[sip-incoming]
include => ring-all

[dus-incoming]
include => ring-all


; #############################################
; ##### CALLS TO ALL PHONES #####
[ring-all]
exten => _X.,1,NoOp(Call on line ${EXTEN} from ${CALLERID}.)
exten => _X.,2,DIAL(${PHONE1}&${PHONE2}, 90)
exten => _X.,3,Hangup
Diese Datei ist etwas komplexer. Eine Extension bezeichnet eigentlich nur eine Nummer, welche angerufen wird. Ausgehend von dem Kontext, von dem aus eine Rufnummer gewählt wird, wird nun nach passenden Extensions gesucht und die entsprechenden Kommandos ausgeführt.
exten => <EXTEN>,<PRIO>,<COMMAND>
<EXTEN> war wie gesagt eine Nummer. <PRIO> ist die Priorität. Dabei wird mit 1 angefangen und mit jedem Kommando um eins hochgezählt. <COMMAND> ist ein Kommando, welches mit diversen Parametern versehen irgendeine Aktion ausführt. Zu kompliziert? Ein Beispiel:
[intern-client01]
exten => 100,1,NoOp
exten => 100,2,Answer
exten => 100,3,Wait(5)
exten => 100,4,Playback(invalid)
exten => 100,5,Hangup
Wählt der erste Client ([client01] in sip.conf) die Rufnummer 100, wird die erste definierte Extension mit der Priorität 1 angesprungen und das Kommando NoOp (No Operation) ausgeführt. Das macht - nichts. Anschließend wird die Priorität um eins erhöht, ist also 2. Dazu passt die zweite Extension. So weit dürfte die Sache mit der Priorität also klar sein. Kann ich also kurz die meist verwendeten Kommandos erklären.
NoOp		macht nichts
Hangup		legt auf, beendet also die Verbindung bzw weist einen Anruf ab
Dial		wählt einen anderen Anschluss an
Goto		springt zu einem Ziel
GoSub		geht in eine Unterroutine
Return		kehrt aus einer Unterroutine zurück
GotoIf		springt zu einem Ziel, wenn die Bedingung erfüllt ist
GoSubIf		geht in die Unterroutine, wenn die Bedingung erfüllt ist
SetCallerID	setzt Displayname und Nummer des Anrufers
In Klammern stehen dabei jeweils die benötigten Parameter - oder eben auch keine.

So, was ist nun ${EXTEN} und ähnliches? Ganz offensichtlich Variablen, die von Asterisk automatisch gesetzt werden. Einen Überblick gibt es unter http://www.voip-info.org/wiki/view/Asterisk+variables. Diese werden beim Auswerten der Extensions automatisch ersetzt durch ihren Wert. ${EXTEN} liefert übrigens die aktuelle Extension, ${EXTEN:1} schneidet das erste Zeichen ab, ${EXTEN:0:3} liefert nur die ersten drei Zeichen.

Ah, und warum ist eigentlich bei mir (fast) keine Extension eine richtige Nummer? Nun ja, irgendwie muss man ja doch mit Wildcards arbeiten. Einfache Ausdrücke leitet man dabei mit _ ein. Neben normalen Zahlen gibt es die folgenden Wildcards
  • X Eine Zahl zwischen 0 und 9
  • N Eine Zahl zwischen 2 und 9
  • Z Eine Zahl zwischen 1 und 9
  • . beliebig viele Zeichen und Zahlen
  • [125-8] Eine Zahl aus 1,2,5,6,7,8
So, jetzt versuche ich mal einen Weg durch den ganzen Dschungel zu erklären. Gehen wir also davon aus, dass mein Telefon als [client01] bei Asterisk angemeldet ist. Es sei Mittwoch, 13 Uhr und ich rufe die Festnetznummer 030-18 44 00 1 an.
In sip.conf ist nun als Kontext intern-client01 definiert. Diesen springen wir nun in der extensions.conf an und suchen nach der ersten zur Rufnummer passenden Extension - die Priorität ist eins.
[intern-client01]
exten => _X.,1,NoOp(${CALLERID})
exten => _X.,2,GotoIf($["${CALLERIDNAME}" != ""]?3:5)
exten => _X.,3,SetCallerID("${CALLERIDNAME}" <${PHONE1NUMBER}>)
exten => _X.,4,Goto(6)
exten => _X.,5,SetCallerID("${PHONE1NAME}" <${PHONE1NUMBER}>)
exten => _X.,6,Goto(intern,${EXTEN},1)
include => intern
Ah, _X. passt zu uns, denn die angerufene Nummer ist fängt mit einer Zahl zwischen 0 und 9 an. NoOp macht nichts,
exten => _X.,2,GotoIf($["${CALLERIDNAME}" != ""]?3:5)
setzt die Priorität auf 3, wenn ${CALLERIDNAME}, also der vom Telefon gelieferte Display Name, nicht leer ist, ansonsten auf 5. Gehen wir mal davon aus, dass mein Telefon einen Namen liefert, wir springen also zu
exten => _X.,3,SetCallerID("${CALLERIDNAME}" <${PHONE1NUMBER}>)
und setzen die Anrufer-ID. Die folgende Zeile springt zum Goto, welches uns wiederum in den Kontext intern katapultiert und die Priorität wieder auf eins zurücksetzt.
[intern]
; check with exceptions
include => out-exceptions
; try to route calls with available providers
include => out-route
; unrouteable extension, try default
include => default-out
; no outgoing extension available, try local
include => local
; nothing found yet? playback error
exten => i,1,Playback(invalid)
exten => i,2,Hangup
Oh, aber was macht include? Naja, es inkludiert den benannten Kontext in den aktuellen, tut also so, also ob der Inhalt des inkludierten Kontextes im aktuellen stehen würde.
Ich nehme einfach mal vorne weg, dass die out-exceptions bei unserer Rufnummer nicht zutreffen und wandere in den Routingteil.
[out-route]
; route outgoing (external) calls to provider
; use the first number 2-9 to choose provider
; try numbers beginning with 1 in local context
exten => _1.,1,GoSub(local,${EXTEN},1)
exten => _${DUSPREFIX}.,1,GoSub(dus-out,${EXTEN:1},1)
exten => _${QSCPREFIX}.,1,GoSub(qsc-out,${EXTEN:1},1)
exten => _${SIPPREFIX}.,1,GoSub(sipgate-out,${EXTEN:1},1)
; weekend, always Off-Peak
exten => _0[2-9]X.,1,GoSubIf($["${IFTIME(*|sat-sun|*|*?true)}" != ""]?qsc-out,${EXTEN},1)
 ; mo-fr, Off-Peak is 18-9h
exten => _0[2-9]X.,2,GoSubIf($["${IFTIME(18:00-8:50|mon-fri|*|*?true)}" != ""]?qsc-out,${EXTEN},1)
; On-Peak, use dus.net
exten => _0[2-9]X.,3,GoSub(dus-out,${EXTEN},1)
; 01802/01804 not supported by sipgate
exten => _0180[2,4].,1,GoSub(dus-out,${EXTEN},1)
Da wird's schon besser. Die drei Prefixe checken auf die Verwendung einer Durchwahlnummer, um direkt über den gewählten Anbieter zu telefonieren. Da unsere Nummer aber mit 0 anfängt, treffen diese schon mal nicht zu, und wir wenden uns
; weekend, always Off-Peak
exten => _0[2-9]X.,1,GoSubIf($["${IFTIME(*|sat-sun|*|*?true)}" != ""]?qsc-out,${EXTEN},1)
zu. Die Extension _0[2-9]X. trifft auf alle Festnetzrufnummern zu. GoSubIf überprüft die Bedingung und springt bei Erfüllung in die Subroutine - den Kontext [qsc-out]. Die Bedingung ist ein Ausdruck, diese werden in der Art
$[expr1 operator expr2]
formuliert; siehe auch http://www.voip-info.org/wiki/view/Asterisk+Expressions. Im Ausdruck wird die Funktion IfTime aufgerufen, welche bei Zutreffen der Zeitangabe *|sat-sun|*|* den String true zurückliefert, andernfalls einen Leerstring. Die Zeitangabe trifft für alle Tage zwischen Samstag und Sonntag, jeweils inklusive, zu; also fürs Wochenende. Heute sei jedoch Mittwoch, damit müssen wir also weitermachen. Mehr zur Zeitangabe gibt es unter http://www.voip-info.org/wiki/view/Asterisk+cmd+GotoIfTime.
So viel sei gesagt, auch die nächste Extension trifft nicht zu, erst wieder
; On-Peak, use dus.net
exten => _0[2-9]X.,3,GoSub(dus-out,${EXTEN},1)
und diese geht sofort in den Kontext
[dus-out]
exten => _X.,1,SetCallerID("${USERNAME}" <${DUSUSERID}>)
exten => _X.,2,Dial(SIP/${EXTEN}@dus,60)
exten => _X.,3,Hangup
; Return
exten => i,1,Return
Ich setze die von dus.net erwartete Caller-ID
SetCallerID("${USERNAME}" <${DUSUSERID}>)
und rufe über den Kontext dus in sip.conf die Nummer ${EXTEN}, welche ja die zu Beginn gewählte 030 - 18 44 00 1 ist:
Dial(SIP/${EXTEN}@dus,60)
Dial lässt es hier 60 Sekunden lang klingeln, dann wird automatisch aufgelegt.

Konnte keine passende Extension im aktuellen Kontext gefunden werden, so wird automatisch die Extension auf i für Invalid gesetzt, und mittels Return landen wir wieder im aufrufenden Kontext, in diesem Falle wäre das out-route. Da jedoch kein Fehler auftrat, hatten wir hier unser Telefonat und sind fertig.

Damit sollte jetzt sowohl die Konfiguration im Groben als auch die Vorgehensweise bei einem Anruf verstanden sein. Hoffe ich :)

Reflog

Informationstechnische Howtos, Hinweise und Merkwürdiges

Batchlib v1.0 2008-03-29

Aktuelle Beiträge

HOWTO_ O2 DSL Surf &...
Der O2 DSL Surf & Phone-Router ist für die alleinige...
cypressor - 12. Feb, 19:57
Uptweak Windows XP Home...
There are a lot of annoying limitations in Windows...
cypressor - 9. Okt, 19:30
BATCHLIB_ Batchlib package...
Download Batchlib package v1.0 (5 KB zip file) What...
cypressor - 29. Mär, 19:10
BATCHLIB_ Batchlib library...
The batchlib library string.cmd is part of the batchlib...
cypressor - 29. Mär, 18:10

Homepage Ticker

Links

Status

Online seit 6577 Tagen
Zuletzt aktualisiert: 28. Jun, 11:32
RSS XML 1.0 Button-Get-Firefox

batch
batchlib
howto
tech
video
Profil
Abmelden
Weblog abonnieren