isdn4k-utils/isdnlog/tools/zone.c

733 lines
17 KiB
C

/* $Id: zone.c,v 1.23 2003/09/11 10:58:56 tobiasb Exp $
*
* Zonenberechnung
*
* Copyright 1999 by Leopold Toetsch <lt@toetsch.at>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Log: zone.c,v $
* Revision 1.23 2003/09/11 10:58:56 tobiasb
* Do not compute the zone with empty strings as input.
* Empty areacodes will occur if the national areacodes are not included in
* the destination database. Discovered by Markus Latzel, see article
* <3f61378e.550970@n.avalon.psi.talypso.de> in de.comp.os.unix.linux.isdn.
*
* Revision 1.22 2001/10/15 11:35:46 leo
* fixed cdb zonefiles
*
* Revision 1.21 2001/06/12 14:24:17 paul
* zone.c and mkzonedb.c now understand filename "-" to mean stdin.
*
* Revision 1.20 2001/06/12 13:54:47 paul
* zone files are now created byte-order independent, so:
* - creating the zone files works on sparc
* - CDB files created on e.g. intel can be used without problem om sparc
* Our copy of CDB has also been modified for this.
*
* Revision 1.19 1999/11/07 13:29:29 akool
* isdnlog-3.64
* - new "Sonderrufnummern" handling
*
* Revision 1.18 1999/10/25 18:30:04 akool
* isdnlog-3.57
* WARNING: Experimental version!
* Please use isdnlog-3.56 for production systems!
*
* Revision 1.17 1999/10/22 19:57:59 akool
* isdnlog-3.56 (for Karsten)
*
* Revision 1.16 1999/07/31 09:25:49 akool
* getRate() speedup
*
* Revision 1.15 1999/07/26 16:28:51 akool
* getRate() speedup from Leo
*
* Revision 1.14 1999/07/25 15:58:13 akool
* isdnlog-3.43
* added "telnum" module
*
* Revision 1.13 1999/07/10 21:38:54 akool
* isdnlog-3.41
* rate-de.dat V:1.02-Germany [10-Jul-1999 23:32:27]
* country-de.dat V:1.02-Germany [10-Jul-1999 23:32:36]
* added all "zone-*" files in binary mode
*
* Revision 1.12 1999/07/07 19:44:20 akool
* patches from Michael and Leo
*
* Revision 1.11 1999/07/01 20:44:07 akool
* zone-1.12
*
* Revision 1.10 1999/06/29 20:11:45 akool
* now compiles with ndbm
* (many thanks to Nima <nima_ghasseminejad@public.uni-hamburg.de>)
*
* Revision 1.9 1999/06/26 12:25:54 akool
* isdnlog Version 3.37
* fixed some warnings
*
* Revision 1.8 1999/06/26 10:12:14 akool
* isdnlog Version 3.36
* - EGCS 1.1.2 bug correction from Nima <nima_ghasseminejad@public.uni-hamburg.de>
* - zone-1.11
*
* Revision 1.7 1999/06/22 19:41:28 akool
* zone-1.1 fixes
*
* Revision 1.6 1999/06/22 16:31:15 akool
* zone-1.10
*
* Revision 1.4 1999/06/18 12:41:57 akool
* zone V1.0
*
* Revision 1.3 1999/06/15 20:05:25 akool
* isdnlog Version 3.33
* - big step in using the new zone files
* - *This*is*not*a*production*ready*isdnlog*!!
* - Maybe the last release before the I4L meeting in Nuernberg
*
* Revision 1.2 1999/06/09 20:58:09 akool
* CVS-Tags added
*
*
* Interface:
*
* int initZone(int provider, char *path, char **msg)
* initialize returns -1 on error, 0 if ok
*
* void exitZone(int provider)
* deinitialize
*
* int getZone(int provider, char *from, char *to)
* returns zone for provider, UNKNOWN on not found, -2 on error
*
* Changes:
*
* 1.24 1999.10.06 lt removed getAreacode
*
* 1.23 1999.10... lt switch to getDest
*
* 1.22 1999.07.26 lt bug fix, getZone returned junk, when diff. providers
* used the same zone file
*
* 1.21 1999.07.22 lt fixed bug, were T was overwritten, when an 'A'
* followed versio, occured w. DTAG
*
* 1.20 1999.07.08 lt added support for NL
*
* in NL areacode may be shorter than actual aeracodenumber
* in this case \tLEN is appended to text
* this version reads also datafiles V1.1
*
*/
#define _ZONE_C_
#ifdef STANDALONE
#include <stdlib.h>
#include <stdio.h>
#ifdef __GLIBC__
# define __USE_GNU /* for declaration of basename() */
#endif
#include <string.h>
#include <ctype.h>
#include <stdarg.h>
#include <time.h>
#include <unistd.h>
#include <errno.h>
#if !defined(__GLIBC__) && !defined(basename)
extern const char *basename (const char *name);
#endif
#else
#include "isdnlog.h"
#include "tools.h"
#endif
#include "zone.h"
/* this config (from config.in) could go in global policy */
#include "zone/config.h"
#include "zone/common.h"
#include "zone/upack.h"
#include <dirent.h>
struct sth {
_DB fh;
char *path;
int provider;
int used;
int real;
int *table;
char pack_key, pack_table;
int table_size;
int oz;
int numlen;
int cc;
} ;
#ifdef STANDALONE
#define min(a,b) (a) < (b) ? (a) : (b)
#endif
static struct sth *sthp;
static int count;
static char version[] = "1.25";
#define LINK 127
#define INFO_LEN 80
#define LENGTH 160
#define BUFSIZE 200
#ifdef STANDALONE
#define UNKNOWN -1
#endif
static void warning (char *file, char *fmt, ...)
{
va_list ap;
char msg[BUFSIZ];
va_start (ap, fmt);
vsnprintf (msg, BUFSIZ, fmt, ap);
va_end (ap);
#ifdef STANDALONE
fprintf(stderr, "WARNING: %s %s\n", basename(file), msg);
#else
print_msg(PRT_NORMAL, "WARNING: %s %s\n", basename(file), msg);
#endif
}
static void _exitZone(int provider);
void exitZone(int provider) {
int i;
_exitZone(provider);
for (i=0; i<count; i++)
if(sthp[i].provider>=10000)
_exitZone(sthp[i].provider);
}
static void _exitZone(int provider)
{
int i;
bool any = false;
bool found = false;
for (i=0; i<count; i++)
if (sthp[i].provider == provider) {
found = true;
if (sthp[i].real >= 0)
sthp[sthp[i].real].used--;
else if (sthp[i].path && sthp[i].used == 0) {
free(sthp[i].path);
sthp[i].path = 0;
free(sthp[i].table);
sthp[i].table = 0;
CLOSE(sthp[i].fh);
sthp[i].fh = 0;
if (i == count-1) /* last released ? */
count--;
}
break;
}
if (!found) {
warning(sthp[i].path ? sthp[i].path : sthp[sthp[i].real].path,
"ExitZone for unknown provider %d", provider);
}
for (i=0; i<count; i++)
if (sthp[i].used || sthp[i].path) {
any=true;
break;
}
if (!any) {
free(sthp);
sthp = 0;
}
}
#define ZONES 0
#define AREACODES 1
static int _initZone(int provider, char *path, char **msg, bool area_only);
int initZone(int provider, char *path, char **msg)
{
static char message[LENGTH];
int res;
if (msg)
*(*msg=message)='\0';
res = _initZone(provider, path, msg, ZONES);
return res;
}
static int _initZone(int provider, char *path, char **msg, bool area_only)
{
int i;
struct sth *newsthp;
bool found;
int ocount;
int csize=0, tsize=0, n;
datum key, value;
char *message=0;
if (msg)
message = *msg;
if (!path || !*path) {
if (msg)
snprintf (message, LENGTH,
"Zone V%s: Error: no zone database specified!", version);
return -1;
}
ocount = count;
if (sthp == 0) {
if ((sthp = calloc(++count, sizeof(struct sth))) == 0) {
if (msg)
snprintf (message, LENGTH,
"Zone V%s: Error: Out of mem 0", version);
count--;
return -1;
}
}
else {
/* look if we can reuse some space */
found = false;
for (i=0; i<ocount; i++)
if (sthp[i].fh == 0) {
ocount = i;
found = true;
break;
}
if (!found) {
if ((newsthp = realloc(sthp, sizeof(struct sth) * ++count)) == 0) {
if (msg)
snprintf (message, LENGTH,
"Zone V%s: Error: Out of mem 1", version);
count--;
return -1;
}
sthp = newsthp;
}
}
sthp[ocount].path=0;
sthp[ocount].table=0;
sthp[ocount].used=0;
sthp[ocount].fh=0;
sthp[ocount].real = -1;
sthp[ocount].oz=1;
sthp[ocount].numlen=0;
sthp[ocount].cc=0;
/* now search for same path */
found = false;
for (i=0; i<count-1; i++) {
if (sthp[i].path && strcmp(sthp[i].path, path) == 0) {
sthp[ocount].fh = sthp[i].fh;
sthp[ocount].oz = sthp[i].oz;
sthp[ocount].numlen = sthp[i].numlen;
sthp[ocount].pack_key = sthp[i].pack_key;
sthp[ocount].pack_table = sthp[i].pack_table;
sthp[ocount].table = sthp[i].table;
sthp[ocount].cc = sthp[i].cc;
sthp[i].used++;
sthp[ocount].real = i;
found = true;
break;
}
}
sthp[ocount].provider = provider;
if (!found) {
char vinfo[] = "vErSiO";
char table[] = "_tAbLe";
char dversion[6];
char *p, *q;
*dversion = '\0';
if((sthp[ocount].path = strdup(path)) == 0) {
if (msg)
snprintf (message, LENGTH,
"Zone V%s: Error: Out of mem 2", version);
return -1;
}
if((sthp[ocount].fh = OPEN(path, READ)) == 0) {
if (msg)
snprintf (message, LENGTH,
"Zone V%s: Error: gdbm_open '%s': '%s'",
version, path, GET_ERR);
count--;
return -1;
}
/* read info */
key.dptr = vinfo;
key.dsize = 7;
value = FETCH(sthp[ocount].fh, key);
if (value.dptr == 0) {
if (msg)
snprintf (message, LENGTH,
"Zone V%s: Error: Provider %d File '%s': no Vinfo",
version, provider, path);
exitZone(provider);
return -1;
}
for (p=value.dptr; *p; p++) {
switch (*p) {
case 'V' :
for (p++,n=0,q=dversion; n<6 && *p != ' '; n++)
*q++ = *p++;
*q = '\0';
if (memcmp(dversion, version, 3) > 0) {
if (msg)
snprintf (message, LENGTH,
"Zone V%s: Error: Provider %d File '%s': incompatible Dataversion %s",
version, provider, path, dversion);
exitZone(provider);
return -1;
}
break;
case 'K' :
sthp[ocount].pack_key = *(++p);
break;
case 'C' :
sthp[ocount].pack_table = *(++p);
break;
case 'N' :
p++;
csize = strtol(p, &p, 10);
break;
case 'T' :
p++;
tsize = strtol(p, &p, 10);
break;
case 'O' :
p++;
sthp[ocount].oz = strtol(p, &p, 10);
break;
case 'L' :
p++;
sthp[ocount].numlen = strtol(p, &p, 10);
p--; /* get's incr after, so we miss 0x0*/
break;
}
} /* for */
if (*dbv == 'G')
free (value.dptr);
/* check it */
if (strlen(dversion) == 0 ||
sthp[ocount].pack_key == '\x0' ||
strchr("SL", sthp[ocount].pack_key) == 0 ||
sthp[ocount].pack_table == '\x0' ||
strchr("CSL", sthp[ocount].pack_table) == 0 ||
sthp[ocount].numlen == 0 ||
csize == 0 ||
tsize == 0) {
if (msg)
snprintf (message, LENGTH,
"Zone V%s: Error: Provider %d File '%s' seems to be corrupted:\n%s",
version, provider, path, value.dptr);
exitZone(provider);
return -1;
}
sthp[ocount].pack_table = sthp[ocount].pack_table == 'C' ? 1 :
sthp[ocount].pack_table == 'S' ? 2 : 4;
sthp[ocount].pack_key = sthp[ocount].pack_key == 'S' ? 2 : 4;
if (area_only) {
if (sthp[ocount].cc == 0)
_exitZone(provider); /* discard this one */
return 0;
}
/* alloc & read table */
if ( (sthp[ocount].table = calloc(csize > 256 ? 256 : csize,
sizeof(int)) ) == 0) {
if (msg)
snprintf (message, LENGTH,
"Zone V%s: Error: Out of mem 3", version);
exitZone(provider);
return -1;
}
key.dptr = table;
key.dsize = 7;
value = FETCH(sthp[ocount].fh, key);
if (value.dptr == 0) {
if (msg)
snprintf (message, LENGTH,
"Zone V%s: Error: Provider %d File '%s': no NTable",
version, provider, path);
exitZone(provider);
return -1;
}
for (p = value.dptr, i=0; i< (csize > 256 ? 256 : csize); i++)
/*
sthp[ocount].table[i] =
sthp[ocount].pack_table == 1 ? (int)(UC)*((UC*)p)++ :
sthp[ocount].pack_table == 2 ? (int)(US)*((US*)p)++ :
(int)(UL)*((UL*)p)++;
*/
switch(sthp[ocount].pack_table) {
case 1: sthp[ocount].table[i] = (int)(UC) *p;
p++;
break;
case 2: sthp[ocount].table[i] = (int)tools_unpack16(p);
p += 2;
break;
case 4: sthp[ocount].table[i] = (int)tools_unpack32(p);
p += 4;
break;
}
if (*dbv == 'G')
free(value.dptr);
if (msg) {
snprintf (message, LENGTH,
"Zone V%s: Provider %d File '%s' opened fine - "
"V%s K%d C%d N%d T%d O%d L%d",
version, provider, path,
dversion, sthp[ocount].pack_key, sthp[ocount].pack_table,
csize, tsize, sthp[ocount].oz, sthp[ocount].numlen);
}
}
else {
if (msg)
#if 1
*message = 0;
#else
snprintf (message, LENGTH,
"Zone V%s: Provider %d is open as '%s' for provider %d",
version, provider, path, sthp[sthp[ocount].real].provider);
#endif
}
return 0;
}
static int _getZ(struct sth *sthp, char *from, char *sto) {
_DB fh = sthp->fh;
datum key, value;
static char newfrom[LENGTH];
bool found = false;
char *temp;
int res;
if (!*from || !*sto) /* empty areacodes cannot be interpreted */
return UNKNOWN;
if ((res=strcmp(from, sto)) == 0)
return sthp->oz;
else if (res > 0) {
temp=from;
from=sto;
sto=temp;
}
if (newfrom != from)
memmove(newfrom, from, LENGTH-1);
while (strlen(newfrom)) {
UL lifrom = (UL) atol(newfrom); /* keys could be long */
US ifrom = (US) lifrom;
if (sthp->pack_key == 2) {
key.dptr = (char *) &ifrom;
key.dsize = sizeof(US);
}
else {
key.dptr = (char *) &lifrom;
key.dsize = sizeof(UL);
}
value = FETCH(fh, key);
if (value.dptr) {
char *p = value.dptr;
char to[10];
US count;
int ito;
unsigned char z=0;
if (sthp->cc) /* if areacodes */
/* here is since 1.00 a zero-terminated strring */
while (*p++);
// count = *((US*)p)++;
count = tools_unpack16(p); p+=2;
while (count--) {
bool ind = true;
int len=1;
z = *p++;
if (z >= 128) {
z -= 128;
ind = false;
len = sthp->pack_key;
}
/*
ito = len==1 ? (int)*((UC*)p)++ :
len==2 ? (int)*((US*)p)++ : (int)*((UL*)p)++;
*/
switch(len) {
case 1:
ito = (int)(UC) *p;
p++;
break;
case 2:
ito = (int)tools_unpack16(p);
p += 2;
break;
case 4:
ito = (int)tools_unpack32(p);
p += 4;
break;
}
if (ind)
ito = sthp->table[ito];
if (z == LINK) {
sprintf(newfrom, "%d", ito);
if (*dbv == 'G')
free(value.dptr);
return _getZ(sthp, newfrom, sto);
}
else {
sprintf(to, "%d", ito);
if (memcmp(to, sto, strlen(to))==0) {
found = true;
break;
}
}
}
if (*dbv == 'G')
free(value.dptr);
if (found)
return z;
} /* if dptr */
newfrom[strlen(newfrom)-1] = '\0';
}
return UNKNOWN;
}
int getZone(int provider, char *from, char *to)
{
int i;
for (i=0; i<count; i++)
if (sthp[i].provider == provider) {
if (sthp[i].fh == 0)
return UNKNOWN;
return _getZ(&sthp[i], from, to);
}
return UNKNOWN;
}
#ifdef ZONETEST
static int checkZone(char *zf, char* df,int num1,int num2, bool verbose)
{
char *msg;
int z, ret=0;
char from[10];
char to[10];
int cc;
if (initZone(1, df, &msg)) {
fprintf(stderr,"%s\n", msg);
exit(1);
}
cc = sthp[0].cc;
if(verbose)
printf("%s\n", msg);
if (num1 && num2) {
snprintf(from, 9, "%d",num1);
snprintf(to, 9, "%d",num2);
ret = getZone(1, from, to);
printf("%s %s = %d\n", from, to, ret);
}
else {
FILE *fp;
char line[40];
char *p, *q;
int rz, i, n;
if (strcmp(zf, "-")) { /* use stdin? */
if ((fp = fopen(zf, "r")) == 0) {
fprintf(stderr, "Can't read %s\n", zf);
exitZone(1);
exit(1);
}
}
else {
fp = stdin;
}
n=0;
while (!feof(fp)) {
if((++n % 1000) == 0 && verbose) {
fprintf(stderr,"%d\r",n);
fflush(stderr);
}
if (!fgets(line, 39, fp))
break;
p=line;
q=from;
if (!isdigit(*p))
continue;
i=0;
while (isdigit(*p) && ++i<9) {
*q++ = *p++;
}
*q = '\0';
p++;
q=to;
i=0;
while (isdigit(*p) && ++i<9) {
*q++ = *p++;
}
*q = '\0';
p++;
rz = atoi(p);
if ((z=getZone(1, from, to)) != rz) {
if(verbose)
printf("Err: %s %s = %d not %d\n", from, to, rz, z);
ret = -1;
break;
}
}
if (fp != stdin)
fclose(fp);
if (verbose)
printf("'%s' verified %s.\n", df, ret==0? "Ok": "NoNo");
}
exitZone(1);
return ret;
}
int main (int argc, char *argv[])
{
int verbose=false;
char *df=0;
char *zf=0;
int c;
int num1=0, num2=0;
char snum1[LENGTH];
while ( (c=getopt(argc, argv, "vVd:z:")) != EOF) {
switch (c) {
case 'v' : verbose = true; break;
case 'V' : printf("%s: V%s\n", basename(argv[0]), version); exit(1);
case 'd' : df = strdup(optarg); break;
case 'z' : zf = strdup(optarg); break;
}
}
while (optind < argc) {
if (!num1 && isdigit(*argv[optind])) {
num1 = atoi(argv[optind]);
strncpy(snum1, argv[optind], LENGTH);
optind++;
continue;
}
else if (!num2 && isdigit(*argv[optind])) {
num2 = atoi(argv[optind]);
optind++;
continue;
}
optind++;
}
if (df && (zf || (num1 && num2)))
return checkZone(zf, df, num1, num2, verbose);
fprintf(stderr, "Usage:\n%s -d DBfile -v -V { -z Zonefile | num1 num2 }\n", basename(argv[0]));
return 0;
}
#endif