703 lines
27 KiB
Plaintext
703 lines
27 KiB
Plaintext
Folgender Text stammt von Tobias Becker, dem ich dafür meinen
|
|
herzlichen Dank aussprechen möchte.
|
|
|
|
Der Text zeigt erstens umfassend, wie die Länder- und Nummernerkennung
|
|
in isndlog funktioniert und behebt zweitens einen Bug darin, deshalb
|
|
gebe ich ihn hier ungekürzt wieder. -lt
|
|
|
|
|
|
isdnlog: country[-de].dat dest.cdb
|
|
==================================
|
|
|
|
Problem
|
|
-------
|
|
isdnrate -N <NUMMER>
|
|
scheitert wenn <NUMMER> zwar in country-de.dat definiert ist,
|
|
dieser Eintrag aber ein top-level Eintrag ist, d. h. über
|
|
kein R:-Tag verfügt und <NUMMER> nicht als erstes mit
|
|
einem C:-Tag definiert ist.
|
|
|
|
Beispiel
|
|
--------
|
|
country.dat:
|
|
| N:Marianen (SaipanNord-)
|
|
| A:Nördliche Marianen, Marianen, Marianen (Saipan), Saipan, Salpan
|
|
| A:Marianen (Nördliche)
|
|
| A:Westl. Marianen
|
|
| E:Northern Mariana Islands
|
|
| C:+1670, +1671
|
|
| T:MP
|
|
|
|
isdnrate erkennt nur den ersten Eintrag:
|
|
| isdnrate -N +1670 +1671
|
|
| +1670 => +1670 - (MP) - DTAG T-ISDN
|
|
| +1671 => - (DE) - DTAG T-ISDN
|
|
|
|
Ziel
|
|
----
|
|
Das Problem liesse sich wahrscheinlich umgehen, in dem alle Einträge,
|
|
deren Nummernraum eine Teilmenge des Nummernraumes eines anderen Lands
|
|
darstellt, mit entsprechenden R:-Tags ausgestattet würden. Allerdings
|
|
entspricht diese logische Zuordnung nicht den realen (politischen)
|
|
Gegebenheiten.
|
|
Alternativ könnte man für die gemeinsamen Rufnummernräume
|
|
(+1, +269, +44, +7, u. a.) Pseudo top-level Einträge anlegen,
|
|
müßte dann aber z. B. die USA vollständig definieren und könnte
|
|
nicht mittels einer Art default-Zuordnung für +1, wie sie jetzt
|
|
verwendet wird, arbeiten.
|
|
Daher soll versucht werden, denn Quelltext entsprechend
|
|
abzuändern, um ohne Nebeneffekte und ohne Zuordnung mit R:-Tags
|
|
das Problem zu lösen.
|
|
|
|
Ansatz
|
|
------
|
|
main() von isdnrate liegt in der Datei isdnlog/tools/isdnrate.c.
|
|
hier wird opts() zum Auswerten der Optionen aufgerufen, -N
|
|
veranlasst das Setzen der globalen Variable explain auf 55.
|
|
opts() gibt den Index der ersten Nichtoption, hier der ersten
|
|
Nummer zurueck.
|
|
Es folgen die Funktionen init() und doit().
|
|
init() ruft unter anderen initDest() aus isdnlog/tools/dest.c auf.
|
|
|
|
doit() beginnt mit post_init():
|
|
| clearNum(&srcnum);
|
|
| if (fromarea) {
|
|
| Strncpy(srcnum.area, fromarea, TN_MAX_AREA_LEN);
|
|
| free(fromarea);
|
|
| fromarea = 0;
|
|
| }
|
|
| initNum(&srcnum);
|
|
|
|
|
| if (wanted_day)
|
|
| get_day(wanted_day);
|
|
|
|
srcnum ist global definiert:
|
|
| static TELNUM srcnum, destnum;
|
|
|
|
Der Typ TELNUM stammt aus isdnlog/tools/telnum.h:
|
|
| /* for number 1002 0043 1 2345 or 1002 01 2345 it gives */
|
|
| typedef struct {
|
|
| char vbn[TN_MAX_VBN_LEN]; /* "10" */
|
|
| char provider[TN_MAX_PROVIDER_LEN]; /* "UTA" */
|
|
| int nprovider; /* 2 */
|
|
| char scountry[TN_MAX_SCOUNTRY_LEN]; /* "Austria" */
|
|
| char country[TN_MAX_COUNTRY_LEN]; /* "+43" */
|
|
| char keys[TN_MAX_SCOUNTRY_LEN]; /* "VIA/AT" */
|
|
| char tld[3]; /* "AT" */
|
|
| int ncountry; /* 43 */
|
|
| char area[TN_MAX_AREA_LEN]; /* "1" */
|
|
| int narea; /* 1 */
|
|
| char sarea[TN_MAX_SAREA_LEN]; /* "Wien" */
|
|
| char msn[TN_MAX_MSN_LEN]; /* "2345" */
|
|
| } TELNUM;
|
|
|
|
clearNum() stammt aus isdnlog/tools/telnum.c und wird dort
|
|
wie folgt beschrieben
|
|
| * void clearNum(TELNUM *num)
|
|
| * --------------------------
|
|
| * call this if you have a number on stack or want to reuse it
|
|
| * normalizeNumber calls this for you
|
|
| *
|
|
| * void initNum(TELNUM *num)
|
|
| * inits a number with myarea, mycountry
|
|
| * you may set the area yourself prior to calling this
|
|
|
|
clearNum() kopiert defnum, dies sind Standardwerte abhängig von der Konfiguration
|
|
nach num. defnum wird von initTelNum() aus telnum.c erstellt, das von initRate()
|
|
aus rate.c aufgerufen, welches in isdnrate.c von init() aufgerufen wird.
|
|
|
|
initNum() belegt einige Defaultwerte, falls nicht vorhanden und ruft getDest()
|
|
auf mit +431, sofern im vergleich zu obigen TELNUM-Beispiel nichts anderes
|
|
angegeben ist.
|
|
|
|
getDest("+431", &srcnum) stammt aus isdnlog/tools/dest.c und ruft seinerseits
|
|
get_cache("+431", &srcnum) auf, offenbar eine Art Zwischenspeicher fuer Zugriffe
|
|
auf dest.cdb?
|
|
|
|
|
|
... zunächst weiter mit doit() aus isdnrate.c unter der Annahme, das
|
|
post_init nur srcnum sinnvoll vorbelegt, was hier nicht weiter von
|
|
Interesse sein dürfte. Möglicherweise ist aber das oben erwähnte
|
|
getDest() der gesuchte Ansatzpunkt.
|
|
|
|
In doit werden nun in einer Schleife alle weitere Parameter der Befehlszeile
|
|
abgearbeitet, hier der relevante Teil, da -N explain=55 zur Folge hatte.
|
|
|
|
| num = sub_sp(argv[i]);
|
|
| if (explain == 55) {
|
|
| if (n_providers) {
|
|
| destnum.nprovider = pnum2prefix_variant(providers[0], 0);
|
|
| Strncpy(destnum.provider, getProvider(destnum.nprovider), TN_MAX_PROVIDER_LEN);
|
|
| normalizeNumber(num, &destnum, TN_NO_PROVIDER);
|
|
| }
|
|
| else
|
|
| normalizeNumber(num, &destnum, TN_ALL);
|
|
| printf("%s => %s \n", num, formatNumber("%l - %p", &destnum));
|
|
| i++;
|
|
| continue;
|
|
| }
|
|
|
|
sub_sp ersetzt Unterstriche durch Leerzeichen, sofern num nicht vollständig aus
|
|
Grossbuchstaben, Unterstrichen und Ziffern besteht, was bei Codes der Bauart
|
|
AT oder _DEMD1 der Fall wäre.
|
|
|
|
n_providers ist ein globaler int, der anfänglich 0 ist und die Anzahl der in
|
|
providers gespeicherten und mittels -p Option gewählten Provider angibt.
|
|
Gibt es keine Option -p, bleibt nproviders 0. (?) Da im gewählten Fehlerfall
|
|
kein -p angegeben wurde, reduziert sich die Betrachtung auf die beiden Zeilen
|
|
| normalizeNumber(num, &destnum, TN_ALL);
|
|
| printf("%s => %s \n", num, formatNumber("%l - %p", &destnum));
|
|
|
|
num ist die Zielnummer aus der Befehlszeile, &destnum vom Typ TELNUM und
|
|
bislang uninitialisiert, TN_ALL ist gleich 15 (telnum.h). normalizeNumber
|
|
stammt aus telnum.c und hat die Aufgabe num in einzelne Bestandteile zu
|
|
zerlegen.
|
|
|
|
Rufen wir uns die kritisierte Ausgabe in Erinnerung:
|
|
| isdnrate -N +1670 +1671
|
|
| +1670 => +1670 - (MP) - DTAG T-ISDN
|
|
| +1671 => - (DE) - DTAG T-ISDN
|
|
formatNumber (ebenfalls aus telnum.c) ersetzt %p durch
|
|
den verwendeten Provider, wobei hier die rate.conf offenbar
|
|
nicht berücksichtigt wird. Für %l ist folgende Beispielausgabe
|
|
vermerkt: %l .. long +49 30 12356 - Berlin (DE)
|
|
|
|
%l ist ein Abkürzung für formatNumber("%1c %1a %1m - %1A (%t)", num)
|
|
wobei %c = .country wenn vorhanden und .ncountry>0
|
|
.country ist die Landesvorwahl als String mit + am Anfang,
|
|
.ncountry die Landesvorwahl (country code) als Zahl
|
|
%a = .area ist die inländische Vorwahl (area code) als String,
|
|
.narea ist das numerische Pendant.
|
|
%m = .msn ist die Anschlussnummer im jeweiligen (Orts-) Netz als String.
|
|
%A = .sarea ist die Bezeichnung der Vorwahl, z. B. Ortsname.
|
|
%t = .tld ist die top-level Destination der Rufnummer.
|
|
.tld ist nur 3 Zeichen lang, hier wäre zu klären, ob es bei
|
|
längeren top-level Bezeichnern zu mehr als optischen Problemen
|
|
kommt, Beispiel Inmarsat A = _INA_.
|
|
|
|
Angeben sind jeweils die Komponenten von TELNUM.
|
|
Die 1 nach dem Prozentzeich unterdrückt bei Nichtvorhandensein des
|
|
Elements 1 nachfolgendes Leerzeichen.
|
|
|
|
Nun weiter zu normalizeNumber(num, &destnum, TN_ALL).
|
|
TN_ALL ist ein flagwert aus telnum.h, hier alle:
|
|
| /* flags */
|
|
| #define TN_PROVIDER 1
|
|
| #define TN_COUNTRY 2
|
|
| #define TN_AREA 4
|
|
| #define TN_MSN 8
|
|
| #define TN_ALL 15
|
|
| #define TN_NO_PROVIDER 14
|
|
| #define TN_NOCLEAR 0x80
|
|
|
|
Zunächst clearNum(&destnum) was für konfiguruierte Standardwerte sorgt:
|
|
| if ((flag & TN_NOCLEAR) == 0)
|
|
| clearNum(num);
|
|
|
|
Dann erfolgt die Verarbeitung einer eventuellen VBN, z. B. 01033:
|
|
| if (flag & TN_PROVIDER) {
|
|
| if (!split_vbn(&p, num)) {
|
|
| num->nprovider = pnum2prefix(preselect,0);
|
|
| }
|
|
| Strncpy(num->provider, getProvider(num->nprovider), TN_MAX_PROVIDER_LEN);
|
|
| }
|
|
|
|
Der Reihe nach, zunächst split_vbn:
|
|
static int split_vbn(char **p, TELNUM * num)
|
|
| {
|
|
| int l;
|
|
|
|
|
| if ((l = provider2prefix(*p, &num->nprovider))) {
|
|
| Strncpy(num->provider, *p, l + 1);
|
|
| *p += l;
|
|
| return l;
|
|
| }
|
|
| return 0;
|
|
| }
|
|
Sinn und Zweck ist es offenbar, zu erkennen, ob eine VBN am
|
|
Anfang von num steht, split_vbn gibt die Länge der VBN zurück.
|
|
Ist eine VBN vorhanden, steht die Providernummer (alle P:-Tags
|
|
aus der rate-CC.dat durchnumeriert) dann in num.nprovider,
|
|
andernfalls wird der Standardprovider (PRESELECTED in isdn.conf)
|
|
mit dem Subprovider 0 eingetragen, die rate.conf wird also nicht
|
|
ausgewertet, dies erklärt die Angabe von T-ISDN an Stelle von
|
|
T-ISDN xxl im isdnrate-Beispiel. Die weitere Funktionialität
|
|
in provider2prefix, u. a. das Beobachten der X:-Tags aus der
|
|
rate-CC.dat wird bis auf weiteres nicht betrachtet.
|
|
|
|
In normalizeNumber() geht es wie folgt weiter:
|
|
| if (flag & TN_COUNTRY) {
|
|
| /* subst '00' => '+' */
|
|
| if (p[0] == '0' && p[1] == '0')
|
|
| *++p = '+';
|
|
| if (getSpecial(p)) { /* sondernummer */
|
|
| goto is_sonder;
|
|
| }
|
|
| if (!isdigit(*p)) {
|
|
| res = getDest(p, num);
|
|
| /* isdnrate is coming with +4319430 but this may be a
|
|
| sondernummer */
|
|
| if (atoi(mycountry + 1) == num->ncountry && (*num->area || *num->msn)) {
|
|
|
|
|
|
|
TN_COUNTRY ist durch TN_ALL gesetzt, p zeigt auf den Beginn einer Kopie der
|
|
zu untersuchenden Nummer num. Eine Nummer 0012345 wird zu +12345, an dieser
|
|
Stelle ist die internationale Vorwahl hart kodiert, es gibt (noch) Länder,
|
|
die eine anderen Zugang zum internationalen Telefonnetz verwenden.
|
|
|
|
getSpecial (rate.c) prüft, ob eine (nationale) Sondernummer vorliegt. Die
|
|
Sonderrufnummern werden durch N:-Tags in der rate-CC.dat definiert ist für
|
|
nur national erreichbare Rufnummern verwendet. Dies soll nicht der Fall sein.
|
|
|
|
Liegt eine internationale Rufnummer, d. h. begann die Nummer auf der
|
|
Befehlszeile mit + oder 00 wird getDest() aufgerufen. Dies trifft auch
|
|
auf nationale Rufnummer zu, die mitsamt Landesvorwahl angegeben wurde.
|
|
num kann an dieser Stelle auch ein Name sein.
|
|
Im weiteren wird hier geprüft (letzte Zeile des obigen Ausschnitts),
|
|
ob der eben genannte Fall, Rufnummer im eigenen Land vorliegt, und falls
|
|
ja, ob es eine Sonderrufnummer ist. Ist letzteres nicht der Fall, endet
|
|
normalizeNumber mit dem Rückgabewert von getDest.
|
|
|
|
Liegt keine internationale Nummer vor, wird ebenfalls auf Sonderrufnummern
|
|
geprüft. Ist dies negativ und beginnt die Rufnummer mit einer 0 (ebenfalls
|
|
hard kodiert nationale Vorwahl, AREAPREFIX aus isdn.conf wird nicht verwendet)
|
|
wird die Rufnummer ins internationale Format gebracht und getDest wie oben
|
|
aufgerufen. Liegt andernfalls eine Rufnummer ohne 0 am Anfang vor, wird diese
|
|
nach num.msn kopiert; num.area, num.naera num.sarea wurden zuvor bereits mit
|
|
den Werten des eigenen Ortsnetzes belegt.
|
|
|
|
|
|
Im weiteren zu untersuchen ist also, was getDest mit einer internationalen
|
|
Rufnummer 'number' und einer bis auf .vbn (?), .provider, .nprovider
|
|
undefinierten TELNUM-Struktur 'num' anfängt.
|
|
|
|
Unser Beispiel ist weiterhin:
|
|
| isdnrate -N +1670 +1671
|
|
| +1670 => +1670 - (MP) - DTAG T-ISDN
|
|
| +1671 => - (DE) - DTAG T-ISDN
|
|
|
|
In der dest.cdb finden sich nach cdbdump dazu folgende Einträge:
|
|
| +2,35:MP->Marianen (SaipanNord-);+1670,+1671^@
|
|
| +5,4:+1670->:MP^@
|
|
| +5,4:+1671->:MP^@
|
|
Links vom Doppelpunkt stehen die Längen von Schlüssel und Wert,
|
|
Rechts davon Schlüssel->Wert. ^@ ist das NUL-Zeichen.
|
|
|
|
Eingangs finden wir den Typ datum, definiert in isdnlog/tools/cdb/i4l_cdb.h:
|
|
| typedef struct datum_t {
|
|
| unsigned char *dptr;
|
|
| size_t dsize;
|
|
| } datum;
|
|
|
|
Anschließend begegnet uns erneut get_cache, womit ein lokaler Zwischenspeicher
|
|
für CACHE_SIZE Nummern realisiert wird. Ist hier das Ergebnis eines getDest()-
|
|
Aufrufs in Form einer TELNUM-Variable bereits abgelegt, wird diese ab der
|
|
Komponente scountry einfach nach 'num' kopiert. Die Details dieses cleveren
|
|
Mechanismus sollten nicht von Belang sein, da isdnrate -N +1671 ebenfalls
|
|
fehl schlägt. Gehen wir also im folgenden davon aus, das number noch
|
|
nicht angefragt wurde.
|
|
|
|
Weiter geht es in getDest():
|
|
| len = strlen(number);
|
|
| if (len==2 && isalpha(*number) && isupper(*number))
|
|
| Strncpy(tld,number,3);
|
|
|
|
|
| if (isdigit(*number)) {
|
|
| warning("getDest called with local number '%s'", number);
|
|
| add_cache(number, num);
|
|
| return UNKNOWN;
|
|
| }
|
|
Ist number zwei Zeichen land und das erste ein Großbuchstabe, wird offenbar ein
|
|
Countrycode angenommen und nach num.tld kopiert. Bei unserem Beispiel trifft
|
|
dies nicht zu.
|
|
Beginnt Nummer mit einer Ziffer liegt ein Fehler vor, da internationale Rufnummern
|
|
hier zumindest bei unserer Vorgeschichte mit einem + beginnen.
|
|
|
|
Es folgen Initialisierungen:
|
|
| countrylen = arealen = prefixlen = 0;
|
|
| num->ncountry = 0;
|
|
| num->narea = 0;
|
|
| *num->area = '\0';
|
|
| *num->sarea = '\0';
|
|
| *num->scountry = '\0';
|
|
| *num->country = '\0';
|
|
| *num->msn = '\0';
|
|
| *num->keys = '\0';
|
|
| if (len > 1 && !isdigit(number[1]) && !isKey(number))
|
|
| city = strdup(number);
|
|
Besteht number aus mindestens zwei Zeichen und ist das zweite Zeichen keine Ziffer
|
|
(number damit keine int. Nummer sondern ein Name) und kein Code der Bauart
|
|
/[A-Z_]?[A-Z_0-9]*/ (Perl-Regexp), wie z. B. AT oder _DEMD1, wird number nach city
|
|
kopiert.
|
|
|
|
Nun die (erste(n)) Abfragen der dest.cdb:
|
|
| again:
|
|
| key.dptr = number;
|
|
| key.dsize = len;
|
|
| value = FETCH(db, key);
|
|
| again2:
|
|
| if (value.dptr != 0) {
|
|
| /* we have a value:
|
|
| * it could be
|
|
| * :RKEY ... pointer to a KEY
|
|
| * :city ... pointer to a city
|
|
| * name;code ... top level entry i.e country
|
|
| * name;codes[;:KEY] ... region
|
|
| * [#len];code;:KEY ... city
|
|
| */
|
|
| [...]
|
|
| }/* if value */
|
|
| else if (first && len && *number == '+') { /* try shorter nums */
|
|
| number[--len] = '\0';
|
|
| goto again; /* I like it */
|
|
| }
|
|
Es unter number nachgesehen, das Ergebnis der Länge value.dsize steht unter
|
|
value.dptr. Wird nichts gefunden, wird bei einer Nummer (+123..) am Ende
|
|
Ziffer um Ziffer entfernt, bis ein Fund auftritt oder die Nummer komplett
|
|
gelöscht wurde. Die Anwendung ist offensichtlich, es geht darum, für eine
|
|
beliebige Nummer die genauest mögliche Destination zu bekommen, gemessen
|
|
über die Nummernlänge
|
|
In unserem Beispiel sollte für beide Nummern der Wert ":MP" bestimmt werden,
|
|
gemäß Kommentar ein RKEY.
|
|
|
|
Schauen wir uns den Anfang der obige Auslassung [...] genauer an:
|
|
| while (value.dptr && *value.dptr == ':') {
|
|
| /* check for city, i.e. lowercase chars */
|
|
| if (!isKey(value.dptr + 1)) {
|
|
| city = strdup(value.dptr + 1);
|
|
| }
|
|
| else {
|
|
| append(num->keys, value.dptr + 1);
|
|
| Strncpy(tld,value.dptr+1,3);
|
|
| }
|
|
| key.dptr = value.dptr + 1;
|
|
| key.dsize = value.dsize - 2; /* w/o : and \x0 */
|
|
| nvalue = FETCH(db, key);
|
|
| if (*dbv == 'G')
|
|
| free(value.dptr);
|
|
| value = nvalue;
|
|
| }
|
|
|
|
|
Ein Ergebnis, das mit : beginnt ist entweder ein Code oder eine
|
|
Stadt. Ersterer (seine ersten 2 Zeichen) werden nach num.tld
|
|
kopiert. Strncpy terminiert im Gegensatz zu strncpy in jedem
|
|
Fall den Zielstring. Der Code beginnt ferner die hierarchiche
|
|
Liste der Codes in num->keys. Haben wir es mit einer Stadt
|
|
zu tun, landet ihr Name in city und würde dort die ursprüngliche
|
|
Anfrage überschreiben, wenn denn eine Abfrage mit einer Stadt
|
|
wiederum eine Stadt liefern würde, was hier nicht der Fall ist.
|
|
|
|
Danach wird ein reverse lookup gemacht, in unserem Fall
|
|
wird nach 'MP' gesucht und dafür als nvalue
|
|
'Marianen (SaipanNord-);+1670,+1671' gefunden.
|
|
dbv ist in isdnlog/tools/zone/common.h definiert und bezeichnet die
|
|
verwendete Datenbank: CDB/GBDM/.. . Somit dienen die Abfragen *dbv == 'G'
|
|
wohl nur der sorgsamen Speicherverwaltung.
|
|
Schließlich wird das erste Ergebnis verworfen und durch das zweite ersetzt.
|
|
number ist weiterhin +1671,
|
|
value.dptr zeigt auf 'Marianen (SaipanNord-);+1670,+1671'
|
|
|
|
Sollte der neue Wert wiederum mit : beginnen, würde das obige Procedere
|
|
wiederholt. Ein Beispiel hierfür ist nicht bekannt:
|
|
+49170 liefert :_DEMD1,
|
|
es folgt num.keys = _DEMD1,
|
|
_DEMD1 liefert Deutschland Mobilfunk D1;+491511,+49160,+49170,+49171,+49175;:_DEMF
|
|
Zumindest zu dieser Phase wird die while-Schleife nur einmal genutzt. Ein Code müßte
|
|
direkt den nächsten Code liefern.
|
|
|
|
Betrachten wir noch einen Durchlauf der mit einem Namen (Stadt) als
|
|
number beginnt:
|
|
"Grünau, Almtal" liefert ;+437616;:AT
|
|
dies löst keinen reverse-lookup aus.
|
|
|
|
Eine Flughafenstadt:
|
|
+4969 führt zu :_FRA,
|
|
_FRA zu ;Frankfurt;+4969;:DE
|
|
('Frankfurt' würde zu :_FRA führen.)
|
|
|
|
Zusammenfassung hieraus: value sollte nun die Form [city];nummer[,nummer][;:code]
|
|
haben. Ist city nicht vorhanden, ist die gleichnamige Variable aber belegt.
|
|
|
|
Es geht weiter:
|
|
| /* do we have something valid */
|
|
| if (value.dptr == 0 && first) {
|
|
| strcpy(num->scountry, unknown);
|
|
| strcpy(num->sarea, unknown);
|
|
| Strncpy(num->msn, number, TN_MAX_MSN_LEN);
|
|
| free(number);
|
|
| if (city)
|
|
| free(city);
|
|
| return 0;
|
|
| }
|
|
Ist beim ersten Durchlauf (first unterhalb dieses Abschnitts
|
|
unabänderlich false) nichts gefunden worden, wird die gesamte
|
|
number num.msn zugeschlagen und getDest() beendet.
|
|
|
|
Es geht weiter:
|
|
| first = false;
|
|
| s = value.dptr;
|
|
| p = strsep(&s, ";");
|
|
| /* name or #len or empty */
|
|
| name = 0;
|
|
| if (p && *p) {
|
|
| if (*p == '#')
|
|
| prefixlen = atoi(p + 1);
|
|
| else
|
|
| name = strdup(p);
|
|
| }
|
|
Ein evtl. vorhandener Name gelangt nach name, bspwl. "Marianen (SaipanNord-)".
|
|
|
|
Bei #num wurde die Laengenangabe nach prefixlen gelangen, das ansonsten 0 bleibt.
|
|
Den #-Fall gibt es hier genau einmal:
|
|
Shared Cost 0180-1->#3;+491801;:DE
|
|
Ursprung ist isdnlog/tools/zone/de/code,
|
|
die restlichen 0180-Einträge sind offenbar falsch kodiert:
|
|
Shared Cost 0180-4 3->;+491804;:DE
|
|
Konkret stehen vor der abschließenden Prefixlänge Leerzeichen statt
|
|
Tabulatoren. Andere Baustelle (TODO).
|
|
|
|
s Zeigt auf den Beginn der Nummernliste, bsplw. +1670,+1671.
|
|
|
|
weiter gehts in getDest() aus der Datei dest.c:
|
|
| p = strsep(&s, ";");
|
|
| /* codes or empty */
|
|
| if (p && *p) {
|
|
| q = strtok(p, ","); /* we could have multiple codes */
|
|
| if(*number!='+') {
|
|
| if (arealen == 0) { /* first one must be city */
|
|
| arealen = strlen(q);
|
|
| Strncpy(num->area, q, TN_MAX_AREA_LEN);
|
|
| }
|
|
| }
|
|
| else {
|
|
| if (arealen == 0) { /* we take the orig number */
|
|
| arealen = strlen(number);
|
|
| Strncpy(num->area, number, TN_MAX_AREA_LEN);
|
|
| }
|
|
| }
|
|
| if (strstr(num->area, q)) /* only if new number has same prefix */
|
|
| countrylen = strlen(q); /* last one must be country */
|
|
| }
|
|
p zeigt auf die Nummernliste. q auf den Eintrag davon.
|
|
Ist die angefragte number ein Name, wird die erste Nummer nach num.area kopiert und
|
|
arealen entsprechend gesetzt. Andernfalls (arealen beim ersten Erreichen zwingend
|
|
0) wird die angefragte number als num.area eingesetzt und arealen gesetzt.
|
|
Im Beispiel ist num.aera somit (zunächst) +1670 und +1671, arealen wird 5.
|
|
Kommt der die erste Nummer in der angefragten vor wird countrylen auf die erste
|
|
Länge gesetzt.
|
|
Bei +1670 ist dies der Fall: countrylen = 5,
|
|
bei +1671 nicht: countrylen = 0.
|
|
|
|
Weiter gehts, die Ursache könnte naheliegen, daher erstmal das Anfangsbeispiel
|
|
betrachten:
|
|
| p = strsep(&s, ";");
|
|
| /* :KEY or empty */
|
|
| /* we should be at toplevel i.e country */
|
|
| if (!p) {
|
|
So, hinter dem 2. ; steht nichts, kein höhere/allgemeinerer Eintrag vorhanden.
|
|
Daher num fertigmachen:
|
|
| append(num->scountry, name);
|
|
name ist in beiden Fällen Marianen ..., passender _s_tring für country.
|
|
|
|
| if (countrylen && (arealen || prefixlen)) {
|
|
das trifft nur für den ersten Code zu, die +1670, nicht für +1671.
|
|
|
|
| append(num->sarea, city);
|
|
city wäre Name für Stadt oder auch Ortsnetz, haben wir hier nicht.
|
|
num.sarea bleibt leer.
|
|
| Strncpy(num->country, num->area, countrylen+1);
|
|
| num->ncountry = atoi(num->country+1);
|
|
num.area ist +1670: num.country wird +1670\0\0
|
|
| strcpy(num->tld,tld);
|
|
num.tld wird MP, ansonsten wurde num.tld bislang in getDest() nicht angefasst.
|
|
num ist physisch destnum in doit() aus isdnrate.c und wurde an normalizeNumber()
|
|
übergeben, das den hier untersuchten getDest() Aufruf veranlasste, vorher aber
|
|
mittels Clearnum u. a. .tld für die eigene Rufnummer setzt (über defnum und _init()
|
|
in telnum.c). Dies erklärt das (DE) in der Ausgabe für +1671.
|
|
| p = num->area + countrylen;
|
|
p zeigt auf \0
|
|
| arealen -= countrylen;
|
|
arealen = 5 - 5 = 0
|
|
| if(prefixlen)
|
|
| arealen=prefixlen;
|
|
haben wir hier nicht, nur bei #len vor ersten ;
|
|
| Strncpy(num->area, p, 1 + arealen);
|
|
num.area wird geleert, richtig, da Angabe komplett country.
|
|
| num->narea = atoi(num->area);
|
|
num.narea = 0
|
|
| if (*onumber == '+' && strlen(onumber) > arealen + countrylen)
|
|
| Strncpy(num->msn, onumber + arealen + countrylen, TN_MAX_MSN_LEN);
|
|
Was nicht in der dest.cdb gefunden wurde, ist die lokale Nummer.
|
|
area werden in dest.cdb Nummernbestandteile mit Ausnahme des letzten Fundes,
|
|
der country darstellt.
|
|
| add_cache(onumber, num);
|
|
| }
|
|
| }
|
|
Abschließend cache-Verwaltung.
|
|
bei +1671 fehlen die ganzen obigen Zuweisungen!
|
|
|
|
| else if (p && *p == ':') {
|
|
| /* do we have a code */
|
|
| append(num->sarea, name);
|
|
| append(num->keys, p + 1);
|
|
| Strncpy(tld,p+1,3);
|
|
| key.dptr = p + 1;
|
|
| key.dsize = strlen(p + 1);
|
|
| nvalue = FETCH(db, key);
|
|
| value = nvalue;
|
|
| goto again2;
|
|
| }
|
|
Obiger Mechanismus durchläuft Abfrage erneut, wenn nach dem zweiten ; :CODE angegeben war,
|
|
um einen möglichst allgemeinen Code zu finden.
|
|
|
|
| free(number);
|
|
| if (city)
|
|
| free(city);
|
|
| return 0;
|
|
| } /* if value */
|
|
Wurde einmal etwas in der dest.cdb gefunden (mit ggf. verkürzter Nummer, das dadurch
|
|
entfallene sollte in num.msn landen) endet getDest hier und führt im zweiten Testfall
|
|
(+1671) zu dn unvollständigen Daten in num und damit der unvollständigen Ausgabe.
|
|
|
|
|
|
Fazit der Fehlersuche
|
|
---------------------
|
|
Eine Nummer wird zunächst soweit verkürzt, bis sie in der dest.cdb auftaucht.
|
|
Nach dem erst Fund wird ihr Code oder Cityname bestimmt, der ggf. zu übergeordneten
|
|
Codes führt. Dieser Pfad wird bis zu einem Code verfolgt, zu dem kein übergeordneter
|
|
mehr angegeben ist. Während im allgemeinen mehrere Nummer zum selben Code führen können,
|
|
und für einen Code mehrere Nummern(anfänge) angegeben sind, wird für den letzten Code
|
|
erwartet, dass er nur über eine Nummer verfügt, die Landesvorwahl. Dies ist im
|
|
gewählten Beispiel nicht der Fall.
|
|
|
|
|
|
Korrekturansatz
|
|
---------------
|
|
(Teilweise spekulativ). Der letzte Code wird am Fehlen eines übergeordneten
|
|
erkannt. countrylen>0 ist dann erforderlich, damit die Struktur num richtig
|
|
belegt wird. Warum sollte daher bei gescheiterter Prüfung strstr(number, code),
|
|
wobei code der erste ist, nicht der nächste Code versucht werden?
|
|
|
|
Gewissermaßen könnte dies zum Beispiel die folgende Notation in der
|
|
country-de.dat erklären, wahrscheinlicher liegt hier aber einfach ein
|
|
unentdeckter Widerspruch infolge einer automatischen Datenübernahme vor:
|
|
| N:Kasachstan
|
|
| E:Kazakhstan
|
|
| C:+7336, +7330, +7329, +7328, +7327, +7326, +7325, +7324, +7323, +7322, +7321, +7318, +7317, +7316, +7315, +7314, +7313, +7312, +7311, +7310, +73
|
|
| T:KZ
|
|
(Man beachte die +73 am Ende).
|
|
|
|
Eine derartige Erweiterung deckt möglicherweise bislang unerkannte Probleme im
|
|
Umfeld country-de.dat / dest.cdb auf oder aber schafft neue.
|
|
|
|
Historische Analyse
|
|
-------------------
|
|
Für die Datei isdnlog/tools/dest.c und hierin die Funktion getDest():
|
|
aktuell ist Version 1.8, zuletzt geändert 2000-08-01 durch Andreas Kool
|
|
oder Leopold Tötsch (CVS Benutzer, Name im Changelog).
|
|
getDest() wurde in Version 1.6 in Bezug auf die Behandlung prefixlen
|
|
(Hinweis auf 'dutch virtual area codes').
|
|
Version 1.4 machte tld zu einem char-Feld der Länge 4 und brachte
|
|
vermutlich die Erkennung von Codes als zu bearbeitende Nummer in
|
|
getDest().
|
|
Version 1.2 ließ eine Hilfsvariable für abschließende Bestimmung
|
|
von num.ncountry entfallen.
|
|
Die auslösende Stelle (bzw. die als solches angesehene) ist somit
|
|
von Anfang an enthalten.
|
|
|
|
Doppelte Nummern bei Ländercodes in der country-de.dat finden sich
|
|
ab der ersten CVS Version 1.1 von 1999-06-27.
|
|
R: und T: Tags erscheinen zuerst in Version 1.15 von 1999-09-26.
|
|
|
|
1. Versuch
|
|
----------
|
|
$ cvs diff -u dest.c
|
|
Index: dest.c
|
|
===================================================================
|
|
RCS file: /i4ldev/isdn4k-utils/isdnlog/tools/dest.c,v
|
|
retrieving revision 1.8
|
|
diff -u -r1.8 dest.c
|
|
--- dest.c 2000/08/01 20:31:31 1.8
|
|
+++ dest.c 2002/08/25 02:00:30
|
|
@@ -338,6 +338,12 @@
|
|
}
|
|
if (strstr(num->area, q)) /* only if new number has same prefix */
|
|
countrylen = strlen(q); /* last one must be country */
|
|
+ else /* test other (country(?)) codes */
|
|
+ while ( (q = strtok(NULL, ",")) )
|
|
+ if (strstr(num->area, q)) {
|
|
+ countrylen = strlen(q);
|
|
+ break;
|
|
+ }
|
|
}
|
|
|
|
p = strsep(&s, ";");
|
|
|
|
|
|
kleiner Erfolg:
|
|
| $ ./bin/isdnrate -N +1670 +1671
|
|
| +1670 => +1670 - (MP) - DTAG T-ISDN
|
|
| +1671 => +1671 - (MP) - DTAG T-ISDN
|
|
|
|
etwas größerer Erfolg: geänderte Programme überleben
|
|
einen (halben) Telefonsonntag, bis die Frage nach der
|
|
Performance auftaucht, da jetzt unter Umständen gegen
|
|
zahlreiche Nummern mit strstr() geprüft wird, siehe
|
|
etwa _DEMF:
|
|
|_DEMF->Deutschland Mobilfunk;+491610,+491611,+491612,+491613,+491614,+491615,
|
|
| +491616,+491617,+491619,+491618,+491511,+49160,+49170,+49171,+49175,+491520,
|
|
| +49162,+49172,+49173,+49174,+49163,+49177,+49178,+49176,+49179,+491505,
|
|
| +491566;:DE^
|
|
|
|
|
|
2. Versuch
|
|
----------
|
|
$ cvs diff -u dest.c
|
|
Index: dest.c
|
|
===================================================================
|
|
RCS file: /i4ldev/isdn4k-utils/isdnlog/tools/dest.c,v
|
|
retrieving revision 1.8
|
|
diff -u -r1.8 dest.c
|
|
--- dest.c 2000/08/01 20:31:31 1.8
|
|
+++ dest.c 2002/08/25 12:34:24
|
|
@@ -219,7 +219,7 @@
|
|
size_t len;
|
|
datum key, value, nvalue;
|
|
static char unknown[] = "Unknown";
|
|
- char *p, *q, *city = 0, *s, *name;
|
|
+ char *p, *q, *r = 0, *city = 0, *s, *name;
|
|
int arealen, countrylen, prefixlen;
|
|
char *number = strdup(onumber);
|
|
char dummy[100]; /* V2.7.2.3 kills stack */
|
|
@@ -338,6 +338,8 @@
|
|
}
|
|
if (strstr(num->area, q)) /* only if new number has same prefix */
|
|
countrylen = strlen(q); /* last one must be country */
|
|
+ else
|
|
+ r = strtok(NULL, ";"); /* save remaining number codes if any */
|
|
}
|
|
|
|
p = strsep(&s, ";");
|
|
@@ -349,6 +351,14 @@
|
|
if (!p) {
|
|
|
|
append(num->scountry, name);
|
|
+ if (!countrylen) /* countrylen missing */
|
|
+ while (r) { /* test remaining number codes */
|
|
+ q = strsep(&r, ",");
|
|
+ if (strstr(num->area, q)) {
|
|
+ countrylen = strlen(q);
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
if (countrylen && (arealen || prefixlen)) {
|
|
append(num->sarea, city);
|
|
Strncpy(num->country, num->area, countrylen+1);
|
|
|
|
|
|
So sollte sich der Aufwand deutlich reduzieren, anstelle über
|
|
strtok() dürfte r sich ebenfalls mittels Pointerarithmetik bestimmen
|
|
lassen.
|
|
|
|
Bislang funktioniert es.
|