Explorar o código

pdb: add daemon 'pdb_server' and optimising data compiler 'pdbt'

- add number portability database daemon and optimizing data compiler
- add debian packaging scripts for tool and daemon as well
- TODO:
  - add documentation (run binaries with '-h' for now to get help)
  - fix this redundant declaration of trie (dt* files) and logging (log*).
    There exists already an implementation that uses the sr core parts for
    this, but we import the proven implementation for now.
  - add data helper scripts as well, they need some cleanup first
- initial implementation was done from Hardy Kahl @ 1&1 Internet Ag
- further bug fixes from Timo Reimann, timo dot reimann at 1und1 dot de
Henning Westerholt %!s(int64=16) %!d(string=hai) anos
pai
achega
d32fa103f1

+ 27 - 0
utils/pdbt/Makefile

@@ -0,0 +1,27 @@
+.phony: all clean install
+
+header=common.h carrier.h dt.h dtm.h pdb_server_backend.h log.h
+obj=dt.o dtm.o carrier.o pdb_server_backend.o log.o
+pdb_server_obj=pdb_server_backend.o dtm.o log.o
+cflags=-Wall -O2 -g
+# -march=x86-64
+extdep=Makefile
+
+all: pdbt pdb_server
+
+$(obj): %.o : %.c %.h $(header) $(extdep)
+	gcc $(cflags) -c $<
+
+pdbt: pdbt.c $(obj) $(header) $(extdep)
+	gcc $(cflags) -o $@ $< $(obj)
+
+pdb_server: pdb_server.c $(pdb_server_obj) $(header) $(extdep)
+	gcc $(cflags) -o $@ $< $(pdb_server_obj)
+
+clean:
+	rm -f *~ *.o pdbt pdb_server
+
+install:
+	cp pdbt $(DESTDIR)/usr/bin/
+	cp pdb_server $(DESTDIR)/usr/bin/
+	cp pdb_server.conf $(DESTDIR)/etc/

+ 118 - 0
utils/pdbt/carrier.c

@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2009 1&1 Internet AG
+ *
+ * This file is part of sip-router, a free SIP server.
+ *
+ * sip-router 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 of the License, or
+ * (at your option) any later version
+ *
+ * sip-router 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#define _GNU_SOURCE
+#include "carrier.h"
+#include "log.h"
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+
+
+char *cnames[MAX_CARRIERID+1];
+
+
+
+
+void init_carrier_names() {
+	int i;
+
+	for (i=0; i<=MAX_CARRIERID; i++) cnames[i]=NULL;
+	cnames[OTHER_CARRIERID] = "other carriers merged by this tool";
+}
+
+
+
+
+int load_carrier_names(char *filename) {
+	FILE * fp;
+	char * line = NULL;
+	size_t len = 0;
+	ssize_t read;
+	char *p;
+	int n;
+	char idstr[4];
+	long int id;
+	int ret=0;
+
+	idstr[3]=0;
+
+	fp = fopen(filename, "r");
+	if (fp == NULL) {
+		LERR("cannot open file '%s'\n", filename);
+		return -1;
+	}
+
+	n=1;
+	while ((read = getline(&line, &len, fp)) != -1) {
+		p=line;
+		len=strlen(p);
+
+		if (len<=5) {
+			LWARNING("invalid line %ld\n", (long int)n);
+			ret=-1;
+			goto nextline;
+		}
+
+		idstr[0] = p[1];
+		idstr[1] = p[2];
+		idstr[2] = p[3];
+		p+=5;
+		len-=5;
+		
+		id = strtol(idstr, NULL, 10);
+		if (!IS_VALID_PDB_CARRIERID(id)) {
+			LWARNING("invalid carrier id '%s'\n", idstr);
+			ret=-1;
+			goto nextline;
+		}
+		
+		cnames[id]=malloc(len+1);
+		if (cnames[id]==NULL) {
+			LERR("out of memory (needed %ld bytes)\n", (long int)len);
+			ret=-1;
+			exit(-1);
+		}
+		
+		strncpy(cnames[id], p, len);
+		cnames[id][len]=0;
+
+	nextline:
+		n++;
+	}
+
+	if (line) free(line);
+	fclose(fp);
+
+	return ret;
+}
+
+
+
+
+char *carrierid2name(carrier_t carrier) {
+	char *s;
+	if (!IS_VALID_CARRIERID(carrier)) s="invalid carrier id";
+	else s=cnames[carrier];
+	if (s==NULL) s="unknown carrier";
+	return s;
+}

+ 54 - 0
utils/pdbt/carrier.h

@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2009 1&1 Internet AG
+ *
+ * This file is part of sip-router, a free SIP server.
+ *
+ * sip-router 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 of the License, or
+ * (at your option) any later version
+ *
+ * sip-router 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef _CARRIER_H_
+#define _CARRIER_H_
+
+
+
+
+#include "common.h"
+
+
+
+
+/*
+ Initializes data structures.
+ Must be called before any of the other functions!
+ Returns 0 on success, -1 otherwise.
+*/
+void init_carrier_names();
+
+/*
+ Loads carrier names from a file.
+ Format: "D[0-9][0-9][0-9] <name>".
+*/
+int load_carrier_names(char *filename);
+
+/*
+  Returns a name for the given carrier id.
+  Always returns a string, even if id is invalid or the id is unknown.
+*/
+char *carrierid2name(carrier_t carrier);
+
+
+
+
+#endif

+ 56 - 0
utils/pdbt/common.h

@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2009 1&1 Internet AG
+ *
+ * This file is part of sip-router, a free SIP server.
+ *
+ * sip-router 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 of the License, or
+ * (at your option) any later version
+ *
+ * sip-router 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef _COMMON_H_
+#define _COMMON_H_
+
+
+
+
+#include <stdint.h> 
+
+
+
+
+/*
+ 0 no carrier id defined.
+ 1..999 are regular carrier ids.
+ 1000 is used as fake carrier id when merging carriers we are not interested in.
+ -1000..-1 used in dtm to indicate a carrier id and that no more nodes will follow (leaf node compression).
+ -1001 used in dtm to mark a pointer to a child node as NULL.
+*/
+#define MIN_PDB_CARRIERID 1
+#define MAX_PDB_CARRIERID 999
+#define OTHER_CARRIERID 1000
+#define MAX_CARRIERID 1000
+#define NULL_CARRIERID -1001
+
+#define IS_VALID_PDB_CARRIERID(id) ((id>=MIN_PDB_CARRIERID) && (id<=MAX_PDB_CARRIERID))
+#define IS_VALID_CARRIERID(id) ((id>=MIN_PDB_CARRIERID) && (id<=MAX_CARRIERID))
+
+
+
+
+typedef int16_t carrier_t;
+
+
+
+
+#endif

+ 0 - 0
utils/pdbt/debian/NEWS


+ 5 - 0
utils/pdbt/debian/changelog

@@ -0,0 +1,5 @@
+pdbt (0.0.1) etch; urgency=low
+
+  * initial release
+
+ -- Henning Westerholt <[email protected]> Wed, 09 Sep 2009 18:07:30  -- +0200

+ 1 - 0
utils/pdbt/debian/compat

@@ -0,0 +1 @@
+4

+ 19 - 0
utils/pdbt/debian/control

@@ -0,0 +1,19 @@
+Source: pdbt
+Section: net
+Priority: optional
+Maintainer: Henning Westerholt <[email protected]>
+Build-Depends: debhelper (>= 4.0.0)
+Standards-Version: 3.6.2
+
+Package: pdb-tools
+Architecture: any
+Depends: ui-pdb-tools-config
+Description: Tools for handling the PDB
+ Tools for building a mmap image of the PDB and running queries on this
+ mmap image.
+
+Package: pdb-server
+Architecture: any
+Description: Service for querying the carrier id via UDP.
+ Provides a service for querying the carrier id of any German phone
+ number via UDP. Needs a prebuilt mmap image of the PDB.

+ 18 - 0
utils/pdbt/debian/copyright

@@ -0,0 +1,18 @@
+#
+# (C) Copyright 2009, 1&1 Internet AG
+#
+# This package is part of sip-router, a free SIP server.
+#
+# sip-router 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 of the License, or
+# (at your option) any later version
+#
+# sip-router 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

+ 1 - 0
utils/pdbt/debian/pdb-server.dirs

@@ -0,0 +1 @@
+usr/share/pdb-server

+ 3 - 0
utils/pdbt/debian/pdb-server.install

@@ -0,0 +1,3 @@
+debian/pdbt/usr/bin/pdb_server usr/bin/
+debian/pdbt/etc/pdb_server.conf etc/
+debian/copyright usr/share/doc/pdb-server/

+ 24 - 0
utils/pdbt/debian/pdb-server.postinst

@@ -0,0 +1,24 @@
+#! /bin/sh
+
+set -e
+
+case "$1" in
+    configure)
+        adduser --quiet --system --group --disabled-password \
+                --shell /bin/false --gecos "PDBServer" \
+                --home /home/pdb pdb || true
+
+        ;;
+
+    abort-upgrade|abort-remove|abort-deconfigure)
+
+        ;;
+
+    *)
+        echo "postinst called with unknown argument \`$1'" >&2
+        exit 1
+        ;;
+esac
+
+#DEBHELPER#
+

+ 3 - 0
utils/pdbt/debian/pdb-tools.install

@@ -0,0 +1,3 @@
+debian/pdbt/usr/bin/pdbt usr/bin/
+debian/copyright usr/share/doc/pdb-tools/
+

+ 98 - 0
utils/pdbt/debian/rules

@@ -0,0 +1,98 @@
+#!/usr/bin/make -f
+# -*- makefile -*-
+# Sample debian/rules that uses debhelper.
+# This file was originally written by Joey Hess and Craig Small.
+# As a special exception, when this file is copied by dh-make into a
+# dh-make output file, you may use that output file without restriction.
+# This special exception was added by Craig Small in version 0.37 of dh-make.
+
+# Uncomment this to turn on verbose mode.
+#export DH_VERBOSE=1
+
+
+
+
+CFLAGS = -Wall -g
+
+ifneq (,$(findstring noopt,$(DEB_BUILD_OPTIONS)))
+	CFLAGS += -O0
+else
+	CFLAGS += -O2
+endif
+
+configure: configure-stamp
+configure-stamp:
+	dh_testdir
+	# Add here commands to configure the package.
+
+	touch configure-stamp
+
+
+build: build-stamp
+
+build-stamp: configure-stamp 
+	dh_testdir
+
+	# Add here commands to compile the package.
+	$(MAKE)
+	#docbook-to-man debian/ui-pdbt.sgml > ui-pdbt.1
+
+	touch build-stamp
+
+clean:
+	dh_testdir
+	dh_testroot
+	rm -f build-stamp configure-stamp
+
+	# Add here commands to clean up after the build process.
+	-$(MAKE) clean
+
+	dh_clean 
+
+install: build
+	dh_testdir
+	dh_testroot
+	dh_clean -k 
+	dh_installdirs
+
+	# Add here commands to install the package into debian/ui-pdbt.
+	$(MAKE) install DESTDIR=$(CURDIR)/debian/ui-pdbt
+
+
+# Build architecture-independent files here.
+binary-indep: build install
+# We have nothing to do by default.
+
+# Build architecture-dependent files here.
+binary-arch: build install
+	dh_testdir
+	dh_testroot
+	dh_installchangelogs 
+#	dh_installdocs
+	dh_installexamples
+	dh_install
+#	dh_installmenu
+#	dh_installdebconf	
+	dh_installlogrotate
+#	dh_installemacsen
+#	dh_installpam
+#	dh_installmime
+	dh_installinit
+	dh_installcron
+#	dh_installinfo
+	dh_installman
+	dh_link
+	dh_strip
+	dh_compress
+	dh_fixperms
+#	dh_perl
+#	dh_python
+#	dh_makeshlibs
+	dh_installdeb
+	dh_shlibdeps
+	dh_gencontrol
+	dh_md5sums
+	dh_builddeb
+
+binary: binary-indep binary-arch
+.PHONY: build clean binary-indep binary-arch binary install configure

+ 360 - 0
utils/pdbt/dt.c

@@ -0,0 +1,360 @@
+/*
+ * Copyright (C) 2009 1&1 Internet AG
+ *
+ * This file is part of sip-router, a free SIP server.
+ *
+ * sip-router 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 of the License, or
+ * (at your option) any later version
+ *
+ * sip-router 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include "dt.h"
+#include "dtm.h"
+#include "carrier.h"
+#include "log.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+
+
+
+struct dt_node_t *dt_init()
+{
+	struct dt_node_t *root;
+
+	root = malloc(sizeof(struct dt_node_t));
+	if (root == NULL) {
+		LERR("dt_init() cannot allocate memory for dt_node_t.\n");
+		return NULL;
+	}
+
+	memset(root, 0, sizeof(struct dt_node_t));
+
+	return root;
+}
+
+
+
+
+void dt_delete(struct dt_node_t *root, struct dt_node_t *node)
+{
+	int i;
+	if (node==NULL) return;
+
+	for (i=0; i<10; i++) {
+		dt_delete(root, node->child[i]);
+		node->child[i] = NULL;
+	}
+
+	if (node != root) free(node);
+}
+
+
+
+
+void dt_destroy(struct dt_node_t **root)
+{
+	if ((root != NULL) && (*root != NULL)) {
+		dt_delete(*root, *root);
+		free(*root);
+		*root = NULL;
+	}
+}
+
+
+
+
+void dt_clear(struct dt_node_t *root)
+{
+	dt_delete(root, root);
+	memset(root, 0, sizeof(struct dt_node_t));
+}
+
+
+
+
+int dt_insert(struct dt_node_t *root, const char *number, int numberlen, carrier_t carrier)
+{
+	struct dt_node_t *node = root;
+	int i=0;
+	unsigned int digit;
+
+	while (i<numberlen) {
+		digit = number[i] - '0';
+		if (digit>9) {
+			LERR("dt_insert() cannot insert non-numerical number\n");
+			return -1;
+		}
+		if (node->child[digit] == NULL) {
+			node->child[digit] = malloc(sizeof(struct dt_node_t));
+#ifdef DT_MEM_ASSERT
+			assert(node->child[digit] != NULL);
+#else
+			if (node->child[digit] == NULL) {
+				LERR("dt_insert() cannot allocate memory\n");
+				return -1;
+			}
+#endif
+			/* non-mapping intermediary nodes carry the special carrier id 0 */
+			memset(node->child[digit], 0, sizeof(struct dt_node_t));
+		}
+		node = node->child[digit];
+
+		i++;
+	}
+
+	node->carrier = carrier;
+	return 0;
+}
+
+
+
+
+int dt_size(struct dt_node_t *root)
+{
+	int i;
+	int sum = 0;
+
+	if (root == NULL) return 0;
+
+	for (i=0; i<10; i++) {
+		sum += dt_size(root->child[i]);
+	}
+	return sum+1;
+}
+
+
+
+
+int dt_loaded_nodes(struct dt_node_t *root) {
+	int i;
+	int sum = 0;
+
+	if (root == NULL) return 0;
+
+	for (i=0; i<10; i++) {
+		sum += dt_loaded_nodes(root->child[i]);
+	}
+
+	if (root->carrier > 0) sum++;
+
+	return sum;
+}
+
+
+
+
+int dt_leaves(struct dt_node_t *root)
+{
+	int i;
+	int sum = 0;
+	int leaf = 1;
+
+	for (i=0; i<10; i++) {
+		if (root->child[i]) {
+			sum += dt_leaves(root->child[i]);
+			leaf = 0;
+		}
+	}
+
+	return sum+leaf;
+}
+
+
+
+
+int dt_longest_match(struct dt_node_t *root, const char *number, int numberlen, carrier_t *carrier)
+{
+	struct dt_node_t *node = root;
+	int nmatch = -1;
+	int i=0;
+	unsigned int digit;
+
+	if (node->carrier > 0) {
+		nmatch=0;
+		*carrier = node->carrier;
+	}
+	while (i<numberlen) {
+		digit = number[i] - '0';
+		if (digit>9) return nmatch;
+		if (node->child[digit] == NULL) return nmatch;
+		node = node->child[digit];
+		i++;
+		if (node->carrier > 0) {
+			nmatch=i;
+			*carrier = node->carrier;
+		}
+	}
+
+	return nmatch;
+}
+
+
+
+
+int dt_contains(struct dt_node_t *root, const char *number, int numberlen, carrier_t *carrier)
+{
+  return (dt_longest_match(root, number, numberlen, carrier) == numberlen);
+}
+
+
+
+
+/*
+ Returns the carrier if all children have the same carrier,
+ 0 otherwise.
+ */
+carrier_t dt_allcce(struct dt_node_t *root) {
+	int i;
+	carrier_t ret = 0;
+
+	/* determine single child carrier */
+	for (i=0; i<10; i++) {
+		if (root->child[i] == NULL)
+			return 0;
+		else if (root->child[i]->carrier > 0) {
+			ret=root->child[i]->carrier;
+			break;
+		}
+	}
+	if (ret==0) return 0;
+
+	/* check if all children share the same carrier */
+	for (i=0; i<10; i++) {
+		if ((root->child[i] == NULL) || (root->child[i]->carrier != ret)) return 0;
+	}
+
+	return ret;
+}
+
+
+
+/*
+ Cuts off tree branches which share a common carrier id with a node located
+ more upwards in the tree. To do so, the subtree starting at root will be
+ traversed recursively and according leaf nodes deleted.
+ Nodes potentially eligible for removal will be marked by the special carrier
+ id `0' (also denoting a [non-mapping] intermediary node). In order to decide
+ whether a node is eligible or not, the lastly observed carrier id will be
+ maintained in lastcarrier and transferred over to each recursion.
+ */
+int dt_optimize_leaf(struct dt_node_t *root, carrier_t lastcarrier)
+{
+	struct dt_node_t *node = root;
+	carrier_t currentcarrier;
+	int deleteret;
+	int delete;
+	carrier_t allcce;
+	int i;
+
+	if (node == NULL) return 0;
+
+	if (node->carrier == lastcarrier) {
+		node->carrier = 0;	    /* this is a node sharing carrier id */
+	}
+
+	if (node->carrier>0) currentcarrier=node->carrier;  /* new common carrier id starts at this node */
+	else currentcarrier=lastcarrier;		    /* carry over common carrier id */
+
+	/* 
+	 Nodes with children sharing the same carrier may be generalized into a common node (prefix). 
+	 Note that the code in the following if-statement is the reason why dt_optimize() calls this function
+	 multiple times.
+	 */
+	if ((allcce=dt_allcce(node))) {
+		/*
+		 generalization requires having an intermediary parent node or a common carrier id between
+		 all children and the current node 
+		 */
+		if ((node->carrier == 0) || (node->carrier < 0 && allcce == currentcarrier)) {
+			currentcarrier=allcce;
+			node->carrier=currentcarrier;
+			for(i=0; i<10; i++) {
+				/* 
+				 Negative carrier ids mark children eligible for generalization into a parent
+				 node. Carrier id 0 cannot be used because it could ambiguously refer to an
+				 intermediary parent node, thereby rendering differentiation of such nodes and
+				 generalized ones impossible and, in turn, overwriting mappings to existing
+				 carrier ids.
+				 When optimization completes, negative carrier ids will be modified to 0 to
+				 make sure other functions operating on the tree do not get confused. (See
+				 dt_clear_negatives() for details.)
+				 */
+				node->child[i]->carrier = -allcce;
+			}
+		}
+	}
+
+	/* preliminarily assume leaf nodes without carrier to be eligible for removal */
+	if (node->carrier <= 0) deleteret=1;
+	else deleteret=0;
+
+	/* optimize children */
+	for (i=0; i<10; i++) {
+		delete=dt_optimize_leaf(node->child[i], currentcarrier);
+		if (delete) {
+			dt_delete(node, node->child[i]);
+			node->child[i] = NULL;
+		}
+		/* this is no leaf node ==> revert removal assumption */
+		if (node->child[i]) deleteret = 0;
+	}
+
+	return deleteret;
+}
+
+
+
+/*
+ Replaces negative carrier ids in the given, root-based
+ subtree by zeros to comply with other functions which may
+ not expect negative carrier ids. (See also dt_optimize_leaf().)
+ */
+void dt_clear_negatives(struct dt_node_t *root)
+{
+    struct dt_node_t *node = root;
+    int i;
+
+    if (node == NULL) return;
+
+    for (i=0; i<10; i++) {
+	    dt_clear_negatives(node->child[i]);
+    }
+
+    if (node->carrier < 0) node->carrier = 0;
+}
+
+
+
+void dt_optimize(struct dt_node_t *root)
+{
+	int size;
+	int oldsize = 0;
+	
+	size=dt_size(root);
+
+	/*
+	 optimization gradually trims leaf nodes in each invocation
+	 of dt_optimize_leaf() ==> keep calling this function until
+	 the size of the tree stabilizes
+	 */
+	while (size!=oldsize) {
+		dt_optimize_leaf(root, 0);
+		oldsize=size;
+		size=dt_size(root);
+	}
+
+	/* turn negative carrier ids into zero's  */
+	dt_clear_negatives(root);
+}

+ 115 - 0
utils/pdbt/dt.h

@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2009 1&1 Internet AG
+ *
+ * This file is part of sip-router, a free SIP server.
+ *
+ * sip-router 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 of the License, or
+ * (at your option) any later version
+ *
+ * sip-router 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef _DT_H_
+#define _DT_H_
+
+
+
+
+#include "common.h" 
+
+
+
+
+struct dt_node_t {
+	struct dt_node_t *child[10];
+	carrier_t carrier;
+};
+
+
+
+
+/*
+ Allocates memory for the root node and initializes it.
+ Returns a pointer to an initialized root node on success, NULL otherwise.
+*/
+struct dt_node_t *dt_init();
+
+/*
+ Deletes a subtree and frees memory including node.
+ Memory for the root node is not freed.
+*/
+void dt_delete(struct dt_node_t *root, struct dt_node_t *node);
+
+/*
+ Deletes the whole tree and frees the memory including the root node.
+*/
+void dt_destroy(struct dt_node_t **root);
+
+/*
+ Deletes everything but the root node.
+ The root node is initialized.
+ This will make an empty tree like after dt_init.
+*/
+void dt_clear(struct dt_node_t *root);
+
+/*
+ Inserts a number with a corresponding carrier id.
+ Nodes are created if necessary and the node after the last
+ digit is marked with the given carrier id.
+ Returns 0 on success, -1 otherwise.
+*/
+int dt_insert(struct dt_node_t *root, const char *number, int numberlen, carrier_t carrier);
+
+/*
+ Returns the number of nodes in the given tree.
+*/
+int dt_size(struct dt_node_t *root);
+
+/*
+ Returns the number of nodes in the given tree that are marked
+ with a carrier id (carrier>0).
+*/
+int dt_loaded_nodes(struct dt_node_t *root);
+
+/*
+ Returns the number of leaf nodes in the given tree.
+ On leaf nodes the leaf flag is set, on other nodes it is cleared.
+*/
+int dt_leaves(struct dt_node_t *root);
+
+/*
+ Finds the longest prefix match of number in root.
+ Sets *carrier according to value in dtree.
+ Return the number of matched digits.
+ In case no (partial) match is found, return -1 (i.e,
+ not even the very first digit could be prefixed).
+*/
+int dt_longest_match(struct dt_node_t *root, const char *number, int numberlen, carrier_t *carrier);
+
+/*
+ Returns 1 if number is found in root and  set *carrier
+ according to value in dtree.
+ Returns 0 if the number is not found.
+*/
+int dt_contains(struct dt_node_t *root, const char *number, int numberlen, carrier_t *carrier);
+
+/*
+ Optimizes the tree by means of compression. Effectively,
+ this reduces the tree to a set of nodes representing shortest
+ prefixes (as opposed to [likely complete] phone numbers).
+*/
+void dt_optimize(struct dt_node_t *root);
+
+
+
+
+#endif

+ 97 - 0
utils/pdbt/dtm.c

@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2009 1&1 Internet AG
+ *
+ * This file is part of sip-router, a free SIP server.
+ *
+ * sip-router 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 of the License, or
+ * (at your option) any later version
+ *
+ * sip-router 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include "dtm.h"
+#include "carrier.h"
+#include "log.h"
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+
+
+
+
+struct dtm_node_t *dtm_load(char *filename) {
+	struct dtm_node_t *mroot;
+	int fd;
+	int len;
+	int nodes;
+
+	fd = open(filename, O_RDONLY);
+	if (fd < 0) {
+		LERR("cannot open file '%s'\n", filename);
+		return NULL;
+	}
+
+	len=lseek(fd, 0, SEEK_END);
+	lseek(fd, 0, SEEK_SET);
+
+	nodes=len/sizeof(struct dtm_node_t);
+	LINFO("file contains %ld nodes (size=%ld, rest=%ld)\n", (long int)nodes, (long int)len, (long int)len%sizeof(struct dtm_node_t));
+
+	mroot=mmap(NULL, len, PROT_READ, MAP_PRIVATE, fd, 0);
+	if (mroot==MAP_FAILED) {
+		LERR("cannot mmap file '%s', error=%d (%s)\n", filename, errno, strerror(errno));
+		close(fd);
+		return NULL;
+	}
+
+	return mroot;
+}
+
+
+
+
+int dtm_longest_match(struct dtm_node_t *mroot, const char *number, int numberlen, carrier_t *carrier)
+{
+	dtm_node_index_t node = 0;
+
+  int nmatch = -1;
+	int i=0;
+	unsigned int digit;
+
+	if (mroot[node].carrier > 0) {
+		nmatch=0;
+		*carrier = mroot[node].carrier;
+	}
+	while (i<numberlen) {
+		digit = number[i] - '0';
+		if (digit>9) return nmatch;
+		node = mroot[node].child[digit];
+		if (node == NULL_CARRIERID) return nmatch;
+		i++;
+		if (node<0) {
+			nmatch=i;
+			*carrier = -node;
+			return nmatch;
+		}
+		if (mroot[node].carrier > 0) {
+			nmatch=i;
+			*carrier = mroot[node].carrier;
+		}
+	}
+
+	return nmatch;
+}

+ 61 - 0
utils/pdbt/dtm.h

@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2009 1&1 Internet AG
+ *
+ * This file is part of sip-router, a free SIP server.
+ *
+ * sip-router 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 of the License, or
+ * (at your option) any later version
+ *
+ * sip-router 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef _DTM_H_
+#define _DTM_H_
+
+
+
+
+#include "common.h"
+
+
+
+
+typedef int32_t dtm_node_index_t;
+
+
+
+
+struct dtm_node_t {
+	dtm_node_index_t child[10];
+	carrier_t carrier;
+} __attribute__ ((packed));
+
+
+
+
+/*
+ The PDB data in the given file is loaded into memory via mmap.
+*/
+struct dtm_node_t *dtm_load(char *filename);
+
+/*
+ Find the longest prefix match of number in mroot.
+ Set *carrier according to value in dtree.
+ Return the number of matched digits.
+ In case no match is found, return -1.
+*/
+int dtm_longest_match(struct dtm_node_t *mroot, const char *number, int numberlen, carrier_t *carrier);
+
+
+
+
+#endif

+ 76 - 0
utils/pdbt/log.c

@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2009 1&1 Internet AG
+ *
+ * This file is part of sip-router, a free SIP server.
+ *
+ * sip-router 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 of the License, or
+ * (at your option) any later version
+ *
+ * sip-router 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include "log.h"
+#include <stdio.h>
+#include <stdarg.h>
+
+
+
+
+static int use_syslog = 0;
+static int log_level = LOG_WARNING;
+
+
+
+
+void init_log(char *_prgname, int _use_syslog) {
+	use_syslog = _use_syslog;
+	if (use_syslog) {
+		openlog(_prgname, LOG_PID, LOG_DAEMON);
+	}
+}
+
+
+
+
+void set_log_level(int level) {
+	log_level = level;
+}
+
+
+
+
+void destroy_log(void) {
+	if (use_syslog) closelog();
+}
+
+
+
+
+void log_stdout(char * format, va_list ap)
+{
+	vfprintf(stdout, format, ap);
+	fflush(stdout);
+}
+
+
+
+
+void pdb_log(int priority, char * format, ...) {
+	va_list ap;
+
+	if (priority<=log_level) {
+		va_start(ap, format);
+		if (use_syslog) vsyslog(priority, format, ap);
+		else log_stdout(format, ap);
+		va_end(ap);
+	}
+}

+ 49 - 0
utils/pdbt/log.h

@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2009 1&1 Internet AG
+ *
+ * This file is part of sip-router, a free SIP server.
+ *
+ * sip-router 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 of the License, or
+ * (at your option) any later version
+ *
+ * sip-router 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef _LOG_H_
+#define _LOG_H_
+
+#include <syslog.h>
+
+
+
+
+void init_log(char *_prgname, int _use_syslog);
+
+void set_log_level(int level);
+
+void destroy_log(void);
+
+void pdb_log(int priority, char * format, ...);
+
+#define LEMERG(fmt, args...) pdb_log(LOG_EMERG, fmt, ## args)
+#define LALERT(fmt, args...) pdb_log(LOG_ALERT, fmt, ## args)
+#define LCRIT(fmt, args...) pdb_log(LOG_CRIT, fmt, ## args)
+#define LERR(fmt, args...) pdb_log(LOG_ERR, fmt, ## args)
+#define LWARNING(fmt, args...) pdb_log(LOG_WARNING, fmt, ## args)
+#define LNOTICE(fmt, args...) pdb_log(LOG_NOTICE, fmt, ## args)
+#define LINFO(fmt, args...) pdb_log(LOG_INFO, fmt, ## args)
+#define LDEBUG(fmt, args...) pdb_log(LOG_DEBUG, fmt, ## args)
+
+
+
+
+#endif

+ 232 - 0
utils/pdbt/pdb_server.c

@@ -0,0 +1,232 @@
+/*
+ * Copyright (C) 2009 1&1 Internet AG
+ *
+ * This file is part of sip-router, a free SIP server.
+ *
+ * sip-router 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 of the License, or
+ * (at your option) any later version
+ *
+ * sip-router 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include "pdb_server_backend.h"
+#include "log.h"
+
+
+
+#define NETBUFSIZE 200
+#define DEFAULT_BINDADDR "0.0.0.0"
+#define DEFAULT_PORT 5574
+
+
+
+
+void print_usage(char *program) {
+	set_log_level(LOG_INFO);
+	LINFO("Listens on a UDP port for queries and sends answer UDP\n");
+	LINFO("packets back.\n");
+	LINFO("\n");
+	LINFO("Usage: %s [<option>...]\n", program);
+	LINFO("  %s -m <data file> [-i <bind addr>] [-p <port>] [-d <log level>]\n", program);
+	LINFO("\n");
+	LINFO("  Options:\n");
+	LINFO("    -m <file>: Specifies the file containing the backend data.\n");
+	LINFO("    -i <bind addr>: Specifies the address to bind the UDP socket to.\n");
+	LINFO("                    Default is '%s'.\n", DEFAULT_BINDADDR);
+	LINFO("    -p <port>: Specifies the port to listen at in udp_server mode.\n");
+	LINFO("               Default is %ld.\n", (long int)DEFAULT_PORT);
+	LINFO("    -d <debug level>: %ld for debug level.\n", LOG_DEBUG);
+	LINFO("                      %ld for info level.\n", LOG_INFO);
+	LINFO("                      %ld for notice level.\n", LOG_NOTICE);
+	LINFO("                      %ld for warning level.\n", LOG_WARNING);
+	LINFO("                      %ld for error level.\n", LOG_ERR);
+	LINFO("                      %ld for critical level.\n", LOG_CRIT);
+	LINFO("                      %ld for alert level.\n", LOG_ALERT);
+	LINFO("                      %ld for emergency level.\n", LOG_EMERG);
+	LINFO("                      %ld to disable all messages.\n", LOG_EMERG-1);
+	LINFO("                      Default is warning level.\n");
+	LINFO("    -h: Print this help.\n");
+}
+
+
+
+
+/*
+ Receive query request and send answer via UDP.
+ UDP Payload of request must contain the phone number (only digits allowed).
+ The Answer contains the number of the request followed by '\0' and the carrier id.
+ Loops until a receive or send error occurs and returns -1.
+ However, the following errors are ignored: EAGAIN, EINTR, EWOULDBLOCK.
+*/
+int udp_server(int so)
+{
+	struct sockaddr fromaddr;
+	socklen_t fromaddrlen;
+	size_t answerlen;
+	ssize_t bytes_received;
+	ssize_t bytes_sent;
+	carrier_t carrierid;
+	char buf[NETBUFSIZE+1+sizeof(carrierid)]; /* additional space for '\0' termination and carrier */
+	int i;
+	int try;
+
+	for (;;) {
+		fromaddrlen = sizeof(fromaddr);
+		bytes_received = recvfrom(so, buf, NETBUFSIZE, 0, &fromaddr, &fromaddrlen);
+		if (bytes_received<0) {
+      LERR("recvfrom() failed with errno=%d (%s)\n", errno, strerror(errno));
+			if ((errno==EAGAIN)||(errno==EINTR)||(errno==EWOULDBLOCK)) continue;
+			return -1;
+		}
+		
+		/* take only digits */
+		i=0;
+		while ((i<bytes_received) && (buf[i]>='0') && (buf[i]<='9')) i++;
+		buf[i]=0; /* terminate string */
+		i++;
+
+		carrierid=lookup_number(buf);
+		
+		/* convert to network byte order*/
+		carrierid=htons(carrierid);
+
+		/* append carrier id to answer */
+		memcpy(&(buf[i]), &carrierid, sizeof(carrierid));
+		answerlen=i+sizeof(carrierid);
+		
+		try=0;
+	again:
+		bytes_sent = sendto(so, buf, answerlen, 0, &fromaddr, fromaddrlen);
+		if (bytes_sent < 3) {
+			if ((errno==EINTR) && (try<3)) {
+				try++;
+				LERR("sendto() failed - trying again. errno=%d (%s)\n", errno, strerror(errno));
+				goto again;
+			}
+			LERR("sendto() failed with errno=%d (%s)\n", errno, strerror(errno));
+			if ((errno==EAGAIN)||(errno==EINTR)||(errno==EWOULDBLOCK)) continue;
+			return -1;
+		}
+		if (bytes_sent != answerlen) {
+			LERR("cannot send the whole answer (%ld/%ld).\n", (long int)bytes_sent, (long int)answerlen);
+			continue;
+		}
+	}
+
+	return 0;
+}
+
+
+
+
+int main(int argc, char *argv[]) {
+	int opt;
+	char *backend_data_filename = NULL;
+	char *bind_addr = DEFAULT_BINDADDR;
+	unsigned short bind_port = DEFAULT_PORT;
+	int use_syslog = 0;
+	int log_level=LOG_WARNING;
+
+	long int ret;
+
+	int so;
+	struct sockaddr_in sa;
+		
+	while ((opt = getopt(argc, argv, "m:i:p:hdl:")) != -1) {
+		switch (opt) {
+		case 'm':
+			backend_data_filename = optarg;
+			break;
+		case 'i':
+			bind_addr=optarg;
+			break;
+		case 'p':
+			ret=strtol(optarg, NULL, 10);
+			if ((ret<0) || (ret>65535)) {
+				init_log("pdb_server", use_syslog);
+				LERR("invalid port '%s' specified.\n", optarg);
+				return -1;
+			}
+			bind_port=ret;
+			break;
+		case 'h':
+			init_log("pdb_server", use_syslog);
+			print_usage(argv[0]);
+			return 0;
+			break;
+		case 'd':
+			use_syslog=1;
+			break;
+		case 'l':
+			ret=strtol(optarg, NULL, 10);
+			if ((ret<LOG_EMERG-1) || (ret>LOG_DEBUG)) {
+				init_log("pdb_server", use_syslog);
+				LERR("invalid log level '%s' specified.\n", optarg);
+				return -1;
+			}
+			log_level=ret;
+			break;
+		default:
+			init_log("pdb_server", use_syslog);
+			LERR("invalid option '%c'.\n", opt);
+			print_usage(argv[0]);
+			return 1;
+		}
+	}
+
+	init_log("pdb_server", use_syslog);
+	set_log_level(log_level);
+
+	if (backend_data_filename==NULL) {
+		LERR("no data file specified.\n");
+		return 1;
+	}
+
+	if (init_backend(backend_data_filename)<0) {
+		LERR("cannot initialize backend.\n");
+		return -1;
+	}
+	
+	so = socket(AF_INET, SOCK_DGRAM, 0);
+	if (so<0) {
+		LERR("socket() failed with errno=%d (%s)\n", errno, strerror(errno));
+		return -1;
+	}
+	
+	memset(&sa, 0, sizeof(sa));
+	sa.sin_family = AF_INET;
+	sa.sin_port = htons(bind_port);
+	if (inet_aton(bind_addr, &(sa.sin_addr))==0) {
+		LERR("invalid address '%s'.\n", bind_addr);
+		close(so);
+		return -1;
+	}
+	
+	if (bind(so, (struct sockaddr *) &sa, sizeof(sa))<0) {
+		LERR("bind() failed with errno=%d (%s)\n", errno, strerror(errno));
+		close(so);
+		return -1;
+	}
+	
+	udp_server(so);
+	close(so);
+
+	return 0;
+}

+ 7 - 0
utils/pdbt/pdb_server.conf

@@ -0,0 +1,7 @@
+# Configuration file for pdb_server
+
+PDBUSER=pdb
+PDBGROUP=pdb
+MMAPFILE=/home/pdb/pdb.mmap
+BINDADDR=0.0.0.0
+BINDPORT=8871

+ 58 - 0
utils/pdbt/pdb_server_backend.c

@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2009 1&1 Internet AG
+ *
+ * This file is part of sip-router, a free SIP server.
+ *
+ * sip-router 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 of the License, or
+ * (at your option) any later version
+ *
+ * sip-router 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include "pdb_server_backend.h"
+#include "dtm.h"
+#include "log.h"
+#include <stdio.h>
+#include <string.h>
+
+
+
+
+struct dtm_node_t *mroot;
+
+
+
+
+int init_backend(char *filename)
+{
+	mroot=dtm_load(filename);
+	if (mroot == NULL) {
+		LERR("cannot load '%s'.\n", filename);
+		return -1;
+	}
+	return 0;
+}
+
+
+
+
+carrier_t lookup_number(char *number)
+{
+	carrier_t carrierid;
+	int nmatch=dtm_longest_match(mroot, number, strlen(number), &carrierid);
+	if (nmatch<=0) {
+		/* nothing found - return id 0 */
+		carrierid=0;
+	}
+	LINFO("request='%s', nmatch=%ld, carrier=%ld\n", number, (long int)nmatch, (long int)carrierid);
+	return carrierid;
+}

+ 47 - 0
utils/pdbt/pdb_server_backend.h

@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2009 1&1 Internet AG
+ *
+ * This file is part of sip-router, a free SIP server.
+ *
+ * sip-router 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 of the License, or
+ * (at your option) any later version
+ *
+ * sip-router 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef _PDB_SERVER_BACKEND_H_
+#define _PDB_SERVER_BACKEND_H_
+
+
+
+
+#include "common.h"
+
+
+
+
+/*
+ Initializes the backend and loads the required data from the given file.
+ Returns 0 on success, -1 otherwise.
+*/
+int init_backend(char *filename);
+
+/*
+ Finds the carrier id for the given number and returns it.
+ Returns 0 if not found.
+*/
+carrier_t lookup_number(char *number);
+
+
+
+
+#endif

+ 823 - 0
utils/pdbt/pdbt.c

@@ -0,0 +1,823 @@
+/*
+ * Copyright (C) 2009 1&1 Internet AG
+ *
+ * This file is part of sip-router, a free SIP server.
+ *
+ * sip-router 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 of the License, or
+ * (at your option) any later version
+ *
+ * sip-router 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/time.h>
+#include <poll.h>
+#include <ctype.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <errno.h>
+#include <limits.h>
+#include "dt.h"
+#include "dtm.h"
+#include "carrier.h"
+#include "log.h"
+
+
+
+
+#define NETBUFSIZE 200
+
+
+
+
+typedef void (*query_func_t)(char *number, char *comment, void *data);
+
+
+
+
+void print_usage(char *program) {
+	set_log_level(LOG_INFO);
+	LINFO("Usage: %s [<option>...] <command> [<param>...]\n", program);
+	LINFO("  %s -s <csv file> -m <mmap file> [-k <ids>] [-o] [-u <tree file>] [-l <log level>] build\n", program);
+	LINFO("  %s (-m <mmap file>|-r <host>:<port>) [-q <timeout>] [-f <query file>] [-t <carrier text file>] [-l <log level>] query <number>...\n", program);
+	LINFO("\n");
+	LINFO("  Commands:\n");
+	LINFO("    build: Build a mmap image from a csv list.\n");
+	LINFO("    query: Query a mmap image or pdb_server.\n");
+	LINFO("           Uses Numbers given on commandline or in a file (-f).\n");
+	LINFO("\n");
+	LINFO("  Options:\n");
+	LINFO("    -s <file>: Specifies the csv list.\n");
+	LINFO("               Use '-' as filename to read from stdin.\n");
+	LINFO("               Line format: <number prefix>;<carrier id>\n");
+	LINFO("               Format of carrier id: D[0-9][0-9][0-9]\n");
+	LINFO("    -m <file>: Specifies the mmap image.\n");
+	LINFO("    -f <file>: Specifies the query file.\n");
+	LINFO("               Use '-' as filename to read from stdin.\n");
+	LINFO("               Each number must be in a separate line.\n");
+	LINFO("               Numbers on the command line will not be processed.\n");
+	LINFO("    -t <file>: Specifies the file containing carrier names.\n");
+	LINFO("               In addition to the carrier code these names will be shown\n");
+	LINFO("               when querying numbers.\n");
+	LINFO("               Each carrier id and name must be in a separate line.\n");
+	LINFO("               Format: D[0-9][0-9][0-9] <name>\n");
+	LINFO("    -k <ids>: Keep these carrier ids.\n");
+	LINFO("              Merge all other carrier ids into a new id.\n");
+	LINFO("              This will save some memory.\n");
+	LINFO("              Format: <id>[,<id>...]\n");
+	LINFO("    -r <host>:<port>: Host and port to be used for remote server queries.\n");
+	LINFO("    -q <timeout>: Timeout for remote server queries in milliseconds.\n");
+	LINFO("                  Default is 500 ms.\n");
+	LINFO("    -o: Try to optimize the data structure when building a mmap image.\n");
+	LINFO("    -u: Write (possibly optimized) tree structure in human-readable format to the given file.\n");
+	LINFO("    -l <debug level>: %ld for debug level.\n", LOG_DEBUG);
+	LINFO("                      %ld for info level.\n", LOG_INFO);
+	LINFO("                      %ld for notice level.\n", LOG_NOTICE);
+	LINFO("                      %ld for warning level.\n", LOG_WARNING);
+	LINFO("                      %ld for error level.\n", LOG_ERR);
+	LINFO("                      %ld for critical level.\n", LOG_CRIT);
+	LINFO("                      %ld for alert level.\n", LOG_ALERT);
+	LINFO("                      %ld for emergency level.\n", LOG_EMERG);
+	LINFO("                      %ld to disable all messages.\n", LOG_EMERG-1);
+	LINFO("                      Default is info level.\n");
+	LINFO("    -h: Print this help.\n");
+}
+
+
+
+
+void print_stats(struct dt_node_t *root) {
+	int s;
+	int l;
+	int c;
+
+	LINFO("+----------------------------------------\n");
+	s = dt_size(root);
+	LINFO("| %ld nodes in tree (%ld bytes, %ld KB, %ld MB)\n", (long int)s, (long int)s*sizeof(struct dt_node_t), (long int)s*sizeof(struct dt_node_t)/1024, (long int)s*sizeof(struct dt_node_t)/1024/1024);
+	l = dt_leaves(root);
+	LINFO("| %ld nodes are leaves (%ld bytes, %ld KB, %ld MB)\n", (long int)l, (long int)l*sizeof(struct dt_node_t), (long int)l*sizeof(struct dt_node_t)/1024, (long int)l*sizeof(struct dt_node_t)/1024/1024);
+	c = dt_loaded_nodes(root);
+	LINFO("| %ld carrier nodes in tree\n", (long int)c);
+	LINFO("| \n");
+	LINFO("| After saving with leaf node compression:\n");
+	LINFO("| %ld nodes in tree (%ld bytes, %ld KB, %ld MB)\n", (long int)s-l, (long int)(s-l)*sizeof(struct dtm_node_t), (long int)(s-l)*sizeof(struct dtm_node_t)/1024, (long int)(s-l)*sizeof(struct dtm_node_t)/1024/1024);
+	LINFO("+----------------------------------------\n");
+}
+
+
+
+
+int file_query(char *filename, query_func_t query_func, void *data) {
+	char * p;
+	char * comment;
+	FILE * fp;
+	char * line = NULL;
+	size_t len = 0;
+	ssize_t read;
+
+	LINFO("\nprocessing query file '%s'...\n", filename);
+  if (strcmp(filename, "-")==0) fp=stdin;
+	else fp = fopen(filename, "r");
+	if (fp == NULL) {
+		LERR("cannot open file '%s'\n", filename);
+		return -1;
+	}
+	while ((read = getline(&line, &len, fp)) != -1) {
+		p=line;
+		while ((*p >= '0') && (*p <= '9') && (p < line+len)) p++;
+		*p='\0';
+		p++;
+		comment=p;
+		while ((*p >= 32) && (p < line+len)) p++;
+		*p='\0';
+		query_func(line, comment, data);
+	}
+	if (line) free(line);
+	fclose(fp);
+	return 0;
+}
+
+
+
+
+/*
+ Read a csv list from the given file and build a dtree structure.
+ Format of lines in csv file: "<number prefix>;<carrier id>".
+ Format of carrier id: "D[0-9][0-9][0-9]".
+ Returns the number of lines imported or -1 on error.
+*/
+int import_csv(struct dt_node_t *root, char *filename) {
+	char *prefix;
+	char *carrier_str;
+	carrier_t carrier;
+	long int ret;
+
+	FILE * fp;
+	char * line = NULL;
+	size_t len = 0;
+	ssize_t read;
+	int i=0;
+	int n=1;
+
+  if (strcmp(filename, "-")==0) fp=stdin;
+	else fp = fopen(filename, "r");
+	if (fp == NULL) {
+		LERR("cannot open file '%s'\n", filename);
+		return -1;
+	}
+	while ((read = getline(&line, &len, fp)) != -1) {
+		carrier_str=line;
+		prefix=strsep(&carrier_str, ";");
+		ret=strtol(carrier_str, NULL, 10);
+		if (!IS_VALID_PDB_CARRIERID(ret)) {
+			LWARNING("invalid carrier '%s' in line %ld.\n", carrier_str, (long int)n);
+			if (line) free(line);
+			fclose(fp);
+			return -1;
+		}
+		else {
+			carrier=ret;
+			i++;
+			dt_insert(root, prefix, strlen(prefix), carrier);
+		}
+		n++;
+	}
+	if (line) free(line);
+	fclose(fp);
+	return i;
+}
+
+
+
+
+/*
+ Returns 1 if the given node is a leaf node, 0 otherwise.
+*/
+inline int dt_is_leaf(struct dt_node_t *root)
+{
+	int i;
+
+	for (i=0; i<10; i++) {
+		if (root->child[i]) return 0;
+	}
+
+	return 1;
+}
+
+
+
+/*
+ Recursively writes sequences of digits (i.e., telephone numbers/prefixes) and mapped
+ carrier ids to the given file descriptor for the entire subtree starting at the
+ given node. Each written line matches one such sequence and its mapped carried id.
+ Returns 1 on success, -1 otherwise.
+ */
+int dt_write_tree_recursor(const struct dt_node_t *node, const int fd, char* number)
+{
+	int i;
+	int ret;
+	int slen;
+	char *buf;
+	char *p;
+	int bufsize;
+
+	if (node == NULL) return 0;
+
+	slen = strlen(number);
+	if (slen > 0) {
+		
+		bufsize = slen + 1 + 1 + 3 + 1 + 1;		    // line buffer (telephone number + colon + white space + carrier ID + newline + \0)
+		buf = (char *)malloc(bufsize);	    
+		if (buf == NULL) {
+			LERR("could not allocate line output buffer of size %d\n", bufsize);
+			return -1;
+		}
+
+		/* construct outline line */
+		p = strncpy(buf, number, slen);
+		p += slen;
+		strncpy(p, ": ", 2);
+		p += 2;
+		ret = snprintf(p, 5, "%d\n", node->carrier);
+		if (ret < 1 || ret > 4) {
+			LERR("snprintf failed to write correct number of characters\n");
+			return -1;
+		}
+
+		/* write line to file */
+		ret = write(fd, (void *)buf, strlen(buf));
+		if (ret != strlen(buf)) {
+			LERR("could not write (complete) line output '%s' to file\n", number);
+			return -1;
+		}
+		free(buf);
+	}
+
+	for (i=0;i<10;i++) {
+		/* extend number by single digit and adjust terminating null byte */
+		number[slen] = i + '0';
+		number[slen+1] = '\0';	    /* must always be done because other recursive invocations operate on `number' too */
+		ret = dt_write_tree_recursor(node->child[i], fd, number);
+		if (ret < 0) {
+			LERR("could not write node\n");
+			return -1;
+		}
+	}
+
+	return 1;
+}
+
+
+
+/*
+ Writes tree to a file in human-readable format, i.e., ASCII.
+ Returns 1 on success, -1 otherwise.
+ */
+int dt_write_tree(const struct dt_node_t *root, const char* filename)
+{
+	int fd;
+	char number[25];
+	number[0] = '\0';
+
+	fd = creat(filename, S_IRWXU);
+	if (fd < 0) {
+		LERR("cannot create file '%s'\n", filename);
+		return -1;
+	}
+	
+	if (dt_write_tree_recursor(root, fd, (char *)&number) < 0) {
+		LERR("writing tree to file '%s' failed\n", filename);
+		return -1;
+	}
+
+	close(fd);
+	return 1;
+}
+
+
+
+/*
+  Saves the given node and all sub-nodes to an mmappable file recursively.
+  Pointers from parent to child nodes will be represented by positive numbers indexing
+  file offsets. The "pointee" address within the file will be linearly proportional to
+  this number.
+  Pointers to leaf nodes will be replaced by leaf node carrier ids encoded in negative
+  numbers, thereby discarding the leaves layer in the tree. Note that this optimization
+  accounts for the majority of space saving.
+  Returns the index of the next free node, -1 on error.
+ */
+dtm_node_index_t save_recursor(struct dt_node_t *root, int fd, dtm_node_index_t n) {
+	dtm_node_index_t i;
+	dtm_node_index_t nn=n+1; /* next free node */
+	struct dtm_node_t node;
+	int offset;
+
+	node.carrier=root->carrier;
+	for (i=0; i<10; i++) {
+		if (root->child[i]) {
+			if (dt_is_leaf(root->child[i])) {
+				node.child[i]=-root->child[i]->carrier;
+			}
+			else {
+				node.child[i]=nn;
+				nn=save_recursor(root->child[i], fd, nn);
+			}
+		}
+		else {
+			node.child[i]=NULL_CARRIERID;
+		}
+	}
+
+	offset=lseek(fd, n*sizeof(struct dtm_node_t), SEEK_SET);
+	if (offset < 0) {
+		LERR("could not position file offset to address %d: errno=%d (%s)\n", n*sizeof(struct dtm_node_t), errno, strerror(errno));
+		return -1;
+	}
+	if (write(fd, &node, sizeof(struct dtm_node_t)) != sizeof(struct dtm_node_t)) {
+		LERR("could not write %d bytes of node data at file address %d: errno=%d (%s)\n", sizeof(struct dtm_node_t), offset, errno, strerror(errno));
+		return -1;
+	}
+
+	return nn;
+}
+
+
+
+
+/*
+	Saves the given tree in a mmappable file.
+	Returns the number of nodes saved or -1 on error.
+*/
+int save_mmap(struct dt_node_t *root, char *filename) {
+	int fd;
+	int n;
+
+	fd = open(filename, O_RDWR|O_CREAT|O_TRUNC, S_IRWXU);
+	if (fd < 0) {
+		LERR("cannot create file '%s'\n", filename);
+		return -1;
+	}
+
+	n=save_recursor(root, fd, 0);
+
+	close(fd);
+
+	return n;
+}
+
+
+
+
+/*
+ Returns 1 if carrier is found in keep_carriers, 0 otherwise.
+*/
+int keep_carrier_func(carrier_t carrier, int keep_carriers_num, carrier_t keep_carriers[])
+{
+	int i;
+
+	for (i=0; i<keep_carriers_num; i++) {
+		if (keep_carriers[i]==carrier) return 1;
+	}
+
+	return 0;
+}
+
+
+
+
+int merge_carrier_recursor(struct dt_node_t *node, int keep_carriers_num, carrier_t keep_carriers[], carrier_t lastcarrier)
+{
+  carrier_t currentcarrier;
+	int i;
+	int sum=0;
+
+	if (node==NULL) return 0;
+
+	if (node->carrier>0) {
+		if (!keep_carrier_func(node->carrier, keep_carriers_num, keep_carriers)) {
+			sum++;
+			if (lastcarrier==0) node->carrier=0; /* first carrier we encountered. we can remove it since we are not interested in it. */
+			else {
+				node->carrier=OTHER_CARRIERID; /* we already have a carrier we are interested in. this is an exception, set it to a special carrier id. */
+			}
+		}
+	}
+
+	if (node->carrier>0) currentcarrier=node->carrier;
+	else currentcarrier=lastcarrier;
+
+	/* merge children carriers */
+	for (i=0; i<10; i++) {
+		sum+=merge_carrier_recursor(node->child[i], keep_carriers_num, keep_carriers, currentcarrier);
+	}
+
+	return sum;
+}
+
+
+
+
+/*
+ Merge all carriers not in keep_carriers into one new carrier id.
+ This will save some memory.
+ Returns the number of nodes modified.
+*/
+int merge_carrier(struct dt_node_t *root, int keep_carriers_num, carrier_t keep_carriers[])
+{
+	return merge_carrier_recursor(root, keep_carriers_num, keep_carriers, 0);
+}
+
+
+
+
+/**
+ * return the corresponding carrier id, -1 on error
+ */
+int query_udp(char *number, int timeout, struct pollfd *pfds, struct sockaddr_in *dstaddr, socklen_t dstaddrlen)
+{
+	struct timeval tstart, tnow;
+	short int carrierid;
+	char buf[NETBUFSIZE+1+sizeof(carrierid)];
+	size_t reqlen;
+	int ret, nflush;
+	long int td;
+
+	if (gettimeofday(&tstart, NULL) != 0) {
+		LERR("gettimeofday() failed with errno=%d (%s)\n", errno, strerror(errno));
+		return -1;
+	}
+
+	/* clear recv buffer */
+	nflush = 0;
+	while (recv(pfds->fd, buf, NETBUFSIZE, MSG_DONTWAIT) > 0) {
+		nflush++;
+		if (gettimeofday(&tnow, NULL) != 0) {
+			LERR("gettimeofday() failed with errno=%d (%s)\n", errno, strerror(errno));
+			return -1;
+		}
+		td=(tnow.tv_usec-tstart.tv_usec+(tnow.tv_sec-tstart.tv_sec)*1000000) / 1000;
+		if (td > timeout) {
+			LWARNING("exceeded timeout while flushing recv buffer.\n");
+			return -1;
+		}
+	}
+	
+	/* prepare request */
+	reqlen = strlen(number) + 1; /* include null termination */
+	if (reqlen > NETBUFSIZE) {
+		LERR("number too long '%s'.\n", number);
+		return -1;
+	}
+	strcpy(buf, number);
+
+	/* send request to all servers */
+	ret=sendto(pfds->fd, buf, reqlen, MSG_DONTWAIT, (struct sockaddr *)dstaddr, dstaddrlen);
+	if (ret < 0) {
+		LERR("sendto() failed with errno=%d (%s)\n", errno, strerror(errno));
+		return -1;
+	}
+		
+	/* wait for response */
+	for (;;) {
+		if (gettimeofday(&tnow, NULL) != 0) {
+			LERR("gettimeofday() failed with errno=%d (%s)\n", errno, strerror(errno));
+			return -1;
+		}
+		td=(tnow.tv_usec-tstart.tv_usec+(tnow.tv_sec-tstart.tv_sec)*1000000) / 1000;
+		if (td > timeout) {
+			LWARNING("exceeded timeout while waiting for response.\n");
+			return -1;
+		}
+		
+		ret=poll(pfds, 1, timeout-td);
+		if (pfds->revents & POLLIN) {
+			if (recv(pfds->fd, buf, NETBUFSIZE, MSG_DONTWAIT) > 0) { /* do not block - just in case select/poll was wrong */
+				buf[NETBUFSIZE] = '\0';
+				if (strcmp(buf, number) == 0) {
+					carrierid=ntohs(*((short int *)&(buf[reqlen]))); /* convert to host byte order */
+					goto found;
+				}
+			}
+		}
+		pfds->revents = 0;
+	}
+
+	found:
+	if (gettimeofday(&tnow, NULL) == 0) {
+		LINFO("got an answer in %f ms\n", ((double)(tnow.tv_usec-tstart.tv_usec+(tnow.tv_sec-tstart.tv_sec)*1000000))/1000);
+	}
+	return carrierid;
+}
+
+
+
+
+struct server_query_data_t {
+	int timeout;
+	struct sockaddr_in dstaddr;
+	socklen_t dstaddrlen;
+	struct pollfd pfds;
+};
+
+
+
+
+void query_mmap(char *number, char *comment, void *data) {
+	int nmatch;
+	carrier_t carrierid;
+	struct dtm_node_t *mroot = (struct dtm_node_t *)data;
+
+	nmatch=dtm_longest_match(mroot, number, strlen(number), &carrierid);
+
+	if (nmatch<=0) {
+		LINFO("%s: not_found: nmatch=%ld, comment='%s'\n", number, (long int)nmatch, comment);
+	}
+	else {
+		LINFO("%s: found: carrier_id=%ld, carrier_name='%s', nmatch=%ld, comment='%s'\n", number, (long int)carrierid, carrierid2name(carrierid), (long int)nmatch, comment);
+	}
+}
+
+
+
+
+void query_server(char *number, char *comment, void *data) {
+	carrier_t carrierid;
+	struct server_query_data_t *sdata = (struct server_query_data_t *)data;
+
+	carrierid = query_udp(number, sdata->timeout, &(sdata->pfds), &(sdata->dstaddr), sdata->dstaddrlen);
+
+	if (carrierid<=0) {
+		LINFO("%s: not_found: comment='%s'\n", number, comment);
+	}
+	else {
+		LINFO("%s: found: carrier_id=%ld, carrier_name='%s', comment='%s'\n", number, (long int)carrierid, carrierid2name(carrierid), comment);
+	}
+}
+
+
+
+
+int main(int argc, char *argv[]) {
+	int n;
+	struct dt_node_t root;
+	memset(&root, 0, sizeof(root));
+	struct dtm_node_t *mroot;
+
+	int opt;
+	char *csv_file = NULL;
+	char *mmap_file = NULL;
+	char *query_file = NULL;
+	char *tree_file = NULL;
+	int optimize = 0;
+	int keep_carriers_num = 0;
+	carrier_t keep_carriers[MAX_PDB_CARRIERID+1];
+	char *host_str = NULL;
+	char *port_str = NULL;
+	unsigned short int port = 0;
+	char *tmp;
+	int log_level = LOG_INFO;
+
+	struct hostent *hp;
+	int sockfd;
+
+	struct server_query_data_t sdata;
+
+	char *id_str;
+	long int ret;
+
+	sdata.timeout=500;
+
+	init_carrier_names();
+
+	init_log("pdbt", 0);
+
+	while ((opt = getopt(argc, argv, "s:m:f:u:t:r:q:k:ol:h")) != -1) {
+		switch (opt) {
+		case 's':
+			csv_file = optarg;
+			break;
+		case 'm':
+			mmap_file = optarg;
+			break;
+		case 'f':
+			query_file = optarg;
+			break;
+		case 'u':
+			tree_file = optarg;
+			break;
+		case 'k':
+			while ((id_str=strsep(&optarg, ","))) {
+				ret=strtol(id_str, NULL, 10);
+				if (!IS_VALID_PDB_CARRIERID(ret)) {
+					LERR("invalid carrier id '%s' specified.\n", id_str);
+					return -1;
+				}
+				if (keep_carriers_num>MAX_PDB_CARRIERID) {
+					LERR("too many carrier ids specified.\n");
+					return -1;
+				}
+				keep_carriers[keep_carriers_num]=ret;
+				keep_carriers_num++;
+			}
+			break;
+		case 't':
+			if (load_carrier_names(optarg)<0) {
+				LERR("cannot load carrier names from '%s'.\n", optarg);
+				return -1;
+			}
+			break;
+		case 'r':
+			host_str=optarg;
+
+			tmp = strchr(host_str, ':');
+			if (tmp == NULL) {
+				LERR("syntax error in remote host:port specification '%s'.\n", host_str);
+				return -1;
+			}
+			*tmp = '\0';
+			port_str = tmp + 1;
+
+			ret=strtol(port_str, NULL, 10);
+			if ((ret<0) || (ret==LONG_MAX)) {
+				LERR("invalid timeout '%s'\n", optarg);
+				return -1;
+			}
+			port = ret;
+
+			break;
+		case 'q':
+			ret=strtol(optarg, NULL, 10);
+			if ((ret<0) || (ret>65535)) {
+				LERR("invalid port '%s'\n", port_str);
+				return -1;
+			}
+			sdata.timeout = ret;
+
+			break;
+		case 'o':
+			optimize=1;
+			break;
+		case 'l':
+			ret=strtol(optarg, NULL, 10);
+			if ((ret<LOG_EMERG-1) || (ret>LOG_DEBUG)) {
+				LERR("invalid log level '%s' specified.\n", optarg);
+				return -1;
+			}
+			log_level=ret;
+			break;
+		case 'h':
+			print_usage(argv[0]);
+			return 0;
+			break;
+		default:
+			LERR("invalid option '%c'.\n", opt);
+			print_usage(argv[0]);
+			return 1;
+		}
+	}
+
+	set_log_level(log_level);
+
+	if (optind>=argc) {
+		LERR("no command specified.\n");
+		return 1;
+	}
+
+	if (strcmp(argv[optind], "build")==0) {
+		if (csv_file==NULL) {
+			LERR("no csv file specified.\n");
+			return 1;
+		}
+
+		if (mmap_file==NULL) {
+			LERR("no mmap file specified.\n");
+			return 1;
+		}
+
+		LINFO("loading '%s'...\n", csv_file);
+		n = import_csv(&root, csv_file);
+		if (n < 0) {
+			LERR("cannot import '%s'\n", csv_file);
+			return -1;
+		}
+		LINFO("done.\n");
+		LINFO("%ld lines imported\n", (long int)n);
+
+		LINFO("Node size is %ld bytes (%ld for dtm)\n", (long int)sizeof(struct dt_node_t), (long int)sizeof(struct dtm_node_t));
+		print_stats(&root);
+
+		if (keep_carriers_num) {
+			LINFO("merging carriers...\n");
+			n=merge_carrier(&root, keep_carriers_num, keep_carriers);
+			LINFO("done (modified %ld nodes).\n", (long int)n);
+		}
+
+		if (optimize) {
+			LINFO("optimizing...\n");
+			dt_optimize(&root);
+			LINFO("done.\n");
+			print_stats(&root);
+		}
+
+		if (tree_file != NULL) {
+			LINFO("writing human-readable tree...\n");
+			if (dt_write_tree(&root, tree_file) < 0) {
+				LERR("cannot write tree\n");
+				return -1;
+		    }
+			LINFO("done.\n");
+		}
+
+		LINFO("saving to '%s'...\n", mmap_file);
+		n = save_mmap(&root, mmap_file);
+		if (n < 0) {
+			LERR("cannot save '%s'\n", mmap_file);
+			return -1;
+		}
+		LINFO("done.\n");
+		LINFO("%ld nodes saved\n", (long int)n);
+	}
+	else if (strcmp(argv[optind], "query")==0) {
+		if ((mmap_file!=NULL) && (host_str!=NULL)) {
+			LERR("you cannot query a pdb_server and a mmap file at the same time.\n");
+			return 1;
+		}
+
+		if ((mmap_file==NULL) && (host_str==NULL)) {
+			LERR("neither a mmap file nor a remote host specified.\n");
+			return 1;
+		}
+
+		if (mmap_file==NULL) {
+			sockfd = socket(AF_INET, SOCK_DGRAM, 0);
+			if (sockfd<0) {
+				LERR("socket() failed with errno=%d (%s).\n", errno, strerror(errno));
+				return -1;
+			}
+
+			memset(&sdata.dstaddr, 0, sizeof(sdata.dstaddr));
+			sdata.dstaddr.sin_family = AF_INET;
+			sdata.dstaddr.sin_port = htons(port);
+			hp = gethostbyname(host_str);
+			if (hp == NULL) {
+				LERR("gethostbyname(%s) failed with h_errno=%d.\n", host_str, h_errno);
+				close(sockfd);
+				return -1;
+			}
+			memcpy(&sdata.dstaddr.sin_addr.s_addr, hp->h_addr, hp->h_length);
+			sdata.dstaddrlen=sizeof(sdata.dstaddr);
+
+			sdata.pfds.fd=sockfd;
+			sdata.pfds.events=POLLIN;
+
+			if (query_file==NULL) {
+				LINFO("\nprocessing command line parameters...\n");
+				for (n=optind+1; n<argc; n++) {
+					query_server(argv[n], "", &sdata);
+				}
+			}
+			else {
+				file_query(query_file, query_server, &sdata);
+			}
+		}
+		else {
+			mroot=dtm_load(mmap_file);
+			if (mroot == NULL) {
+				LERR("cannot load '%s'.\n", mmap_file);
+				return -1;
+			}
+			
+			if (query_file==NULL) {
+				LINFO("\nprocessing command line parameters...\n");
+				for (n=optind+1; n<argc; n++) {
+					query_mmap(argv[n], "", mroot);
+				}
+			}
+			else {
+				file_query(query_file, query_mmap, mroot);
+			}
+		}
+	}
+	else {
+		LERR("invalid command '%s'.\n", argv[optind]);
+		return 1;
+	}
+
+	return 0;
+}