merged in commercial subscriberRegistry

This commit is contained in:
Michael Iedema 2014-03-25 00:30:12 +01:00
parent 226fc1c28a
commit 25e81ce18e
57 changed files with 2961 additions and 3072 deletions

4
.gitmodules vendored
View File

@ -6,3 +6,7 @@
path = sqlite3
url = git@github.com:RangeNetworks/sqlite3.git
branch = master
[submodule "NodeManager"]
path = NodeManager
url = git@github.com:RangeNetworks/NodeManager.git
branch = 4.0

0
AUTHORS Normal file
View File

10
COPYING
View File

@ -693,14 +693,6 @@ Interaction; Use with the GNU General Public License"). This exemption of
interfaces other than the GSM air interface from the requirements of Section 13
is an additional permission granted to you.
2. GSM "A5" cipher stream generation libraries
Notwithstanding any other provision of this License, you have
permission to link the Program with GSM "A5" cipher-stream generation
libraries provided under any license that allows redistribution of
those libraries in binary form, provided that the function of any such
library is limited to the generation of cipher-steam bits.
Non-Permissive Terms Supplementing The License
@ -712,9 +704,11 @@ license does not include the right to use the OpenBTS trademark in commerce.
This additional non-permissive term is consistent with Section 7 of the AGPLv3
license.
END OF ADDITIONAL TERMS
How to comply with Section 13 of the AGPLv3 license.
The recommended method for compliance with Section 13 of the AGPLv3 license is

0
ChangeLog Normal file
View File

@ -1 +1 @@
Subproject commit bd94d0a9883002daa8310b601f28eed3b0d87d2f
Subproject commit abec8dabb69f8f2bdefdb9bcca340b89f43ce419

25
Globals/Globals.cpp Normal file
View File

@ -0,0 +1,25 @@
/**@file Global system parameters. */
/*
* Copyright 2013 Range Networks, Inc.
*
* This software is distributed under multiple licenses;
* see the COPYING file in the main directory for licensing
* information for this specific distribuion.
*
* This use of this software may be subject to additional restrictions.
* See the LEGAL file in the main directory for details.
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.
*/
/*
This file keeps global system parameters.
*/
#include "config.h"
#include <Globals.h>
const char *gVersionString = "release " VERSION " built " __DATE__ " rev" SVN_REV " ";

36
Globals/Globals.h Normal file
View File

@ -0,0 +1,36 @@
/**@file Global system parameters. */
/*
* Copyright 2013 Range Networks, Inc.
*
* This software is distributed under multiple licenses;
* see the COPYING file in the main directory for licensing
* information for this specific distribuion.
*
* This use of this software may be subject to additional restrictions.
* See the LEGAL file in the main directory for details.
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.
*/
/*
This file keeps global system parameters.
*/
#ifndef GLOBALS_H
#define GLOBALS_H
#include <Configuration.h>
/**
Just about everything goes into the configuration table.
This should be defined in the main body of the top-level application.
*/
extern ConfigurationTable gConfig;
/** The version string. */
extern const char *gVersionString;
#endif

33
Globals/Makefile.am Normal file
View File

@ -0,0 +1,33 @@
#
# Copyright 2008 Free Software Foundation, Inc.
#
# This software is distributed under the terms of the GNU Public License.
# See the COPYING file in the main directory for details.
#
# 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 3 of the License, 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, see <http://www.gnu.org/licenses/>.
#
include $(top_srcdir)/Makefile.common
AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES)
AM_CXXFLAGS = -Wall
noinst_LTLIBRARIES = libglobals.la
libglobals_la_SOURCES = Globals.cpp
noinst_PROGRAMS =
noinst_HEADERS = \
Globals.h

365
INSTALL Normal file
View File

@ -0,0 +1,365 @@
Installation Instructions
*************************
Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005,
2006, 2007, 2008, 2009 Free Software Foundation, Inc.
Copying and distribution of this file, with or without modification,
are permitted in any medium without royalty provided the copyright
notice and this notice are preserved. This file is offered as-is,
without warranty of any kind.
Basic Installation
==================
Briefly, the shell commands `./configure; make; make install' should
configure, build, and install this package. The following
more-detailed instructions are generic; see the `README' file for
instructions specific to this package. Some packages provide this
`INSTALL' file but do not implement all of the features documented
below. The lack of an optional feature in a given package is not
necessarily a bug. More recommendations for GNU packages can be found
in *note Makefile Conventions: (standards)Makefile Conventions.
The `configure' shell script attempts to guess correct values for
various system-dependent variables used during compilation. It uses
those values to create a `Makefile' in each directory of the package.
It may also create one or more `.h' files containing system-dependent
definitions. Finally, it creates a shell script `config.status' that
you can run in the future to recreate the current configuration, and a
file `config.log' containing compiler output (useful mainly for
debugging `configure').
It can also use an optional file (typically called `config.cache'
and enabled with `--cache-file=config.cache' or simply `-C') that saves
the results of its tests to speed up reconfiguring. Caching is
disabled by default to prevent problems with accidental use of stale
cache files.
If you need to do unusual things to compile the package, please try
to figure out how `configure' could check whether to do them, and mail
diffs or instructions to the address given in the `README' so they can
be considered for the next release. If you are using the cache, and at
some point `config.cache' contains results you don't want to keep, you
may remove or edit it.
The file `configure.ac' (or `configure.in') is used to create
`configure' by a program called `autoconf'. You need `configure.ac' if
you want to change it or regenerate `configure' using a newer version
of `autoconf'.
The simplest way to compile this package is:
1. `cd' to the directory containing the package's source code and type
`./configure' to configure the package for your system.
Running `configure' might take a while. While running, it prints
some messages telling which features it is checking for.
2. Type `make' to compile the package.
3. Optionally, type `make check' to run any self-tests that come with
the package, generally using the just-built uninstalled binaries.
4. Type `make install' to install the programs and any data files and
documentation. When installing into a prefix owned by root, it is
recommended that the package be configured and built as a regular
user, and only the `make install' phase executed with root
privileges.
5. Optionally, type `make installcheck' to repeat any self-tests, but
this time using the binaries in their final installed location.
This target does not install anything. Running this target as a
regular user, particularly if the prior `make install' required
root privileges, verifies that the installation completed
correctly.
6. You can remove the program binaries and object files from the
source code directory by typing `make clean'. To also remove the
files that `configure' created (so you can compile the package for
a different kind of computer), type `make distclean'. There is
also a `make maintainer-clean' target, but that is intended mainly
for the package's developers. If you use it, you may have to get
all sorts of other programs in order to regenerate files that came
with the distribution.
7. Often, you can also type `make uninstall' to remove the installed
files again. In practice, not all packages have tested that
uninstallation works correctly, even though it is required by the
GNU Coding Standards.
8. Some packages, particularly those that use Automake, provide `make
distcheck', which can by used by developers to test that all other
targets like `make install' and `make uninstall' work correctly.
This target is generally not run by end users.
Compilers and Options
=====================
Some systems require unusual options for compilation or linking that
the `configure' script does not know about. Run `./configure --help'
for details on some of the pertinent environment variables.
You can give `configure' initial values for configuration parameters
by setting variables in the command line or in the environment. Here
is an example:
./configure CC=c99 CFLAGS=-g LIBS=-lposix
*Note Defining Variables::, for more details.
Compiling For Multiple Architectures
====================================
You can compile the package for more than one kind of computer at the
same time, by placing the object files for each architecture in their
own directory. To do this, you can use GNU `make'. `cd' to the
directory where you want the object files and executables to go and run
the `configure' script. `configure' automatically checks for the
source code in the directory that `configure' is in and in `..'. This
is known as a "VPATH" build.
With a non-GNU `make', it is safer to compile the package for one
architecture at a time in the source code directory. After you have
installed the package for one architecture, use `make distclean' before
reconfiguring for another architecture.
On MacOS X 10.5 and later systems, you can create libraries and
executables that work on multiple system types--known as "fat" or
"universal" binaries--by specifying multiple `-arch' options to the
compiler but only a single `-arch' option to the preprocessor. Like
this:
./configure CC="gcc -arch i386 -arch x86_64 -arch ppc -arch ppc64" \
CXX="g++ -arch i386 -arch x86_64 -arch ppc -arch ppc64" \
CPP="gcc -E" CXXCPP="g++ -E"
This is not guaranteed to produce working output in all cases, you
may have to build one architecture at a time and combine the results
using the `lipo' tool if you have problems.
Installation Names
==================
By default, `make install' installs the package's commands under
`/usr/local/bin', include files under `/usr/local/include', etc. You
can specify an installation prefix other than `/usr/local' by giving
`configure' the option `--prefix=PREFIX', where PREFIX must be an
absolute file name.
You can specify separate installation prefixes for
architecture-specific files and architecture-independent files. If you
pass the option `--exec-prefix=PREFIX' to `configure', the package uses
PREFIX as the prefix for installing programs and libraries.
Documentation and other data files still use the regular prefix.
In addition, if you use an unusual directory layout you can give
options like `--bindir=DIR' to specify different values for particular
kinds of files. Run `configure --help' for a list of the directories
you can set and what kinds of files go in them. In general, the
default for these options is expressed in terms of `${prefix}', so that
specifying just `--prefix' will affect all of the other directory
specifications that were not explicitly provided.
The most portable way to affect installation locations is to pass the
correct locations to `configure'; however, many packages provide one or
both of the following shortcuts of passing variable assignments to the
`make install' command line to change installation locations without
having to reconfigure or recompile.
The first method involves providing an override variable for each
affected directory. For example, `make install
prefix=/alternate/directory' will choose an alternate location for all
directory configuration variables that were expressed in terms of
`${prefix}'. Any directories that were specified during `configure',
but not in terms of `${prefix}', must each be overridden at install
time for the entire installation to be relocated. The approach of
makefile variable overrides for each directory variable is required by
the GNU Coding Standards, and ideally causes no recompilation.
However, some platforms have known limitations with the semantics of
shared libraries that end up requiring recompilation when using this
method, particularly noticeable in packages that use GNU Libtool.
The second method involves providing the `DESTDIR' variable. For
example, `make install DESTDIR=/alternate/directory' will prepend
`/alternate/directory' before all installation names. The approach of
`DESTDIR' overrides is not required by the GNU Coding Standards, and
does not work on platforms that have drive letters. On the other hand,
it does better at avoiding recompilation issues, and works well even
when some directory options were not specified in terms of `${prefix}'
at `configure' time.
Optional Features
=================
If the package supports it, you can cause programs to be installed
with an extra prefix or suffix on their names by giving `configure' the
option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
Some packages pay attention to `--enable-FEATURE' options to
`configure', where FEATURE indicates an optional part of the package.
They may also pay attention to `--with-PACKAGE' options, where PACKAGE
is something like `gnu-as' or `x' (for the X Window System). The
`README' should mention any `--enable-' and `--with-' options that the
package recognizes.
For packages that use the X Window System, `configure' can usually
find the X include and library files automatically, but if it doesn't,
you can use the `configure' options `--x-includes=DIR' and
`--x-libraries=DIR' to specify their locations.
Some packages offer the ability to configure how verbose the
execution of `make' will be. For these packages, running `./configure
--enable-silent-rules' sets the default to minimal output, which can be
overridden with `make V=1'; while running `./configure
--disable-silent-rules' sets the default to verbose, which can be
overridden with `make V=0'.
Particular systems
==================
On HP-UX, the default C compiler is not ANSI C compatible. If GNU
CC is not installed, it is recommended to use the following options in
order to use an ANSI C compiler:
./configure CC="cc -Ae -D_XOPEN_SOURCE=500"
and if that doesn't work, install pre-built binaries of GCC for HP-UX.
On OSF/1 a.k.a. Tru64, some versions of the default C compiler cannot
parse its `<wchar.h>' header file. The option `-nodtk' can be used as
a workaround. If GNU CC is not installed, it is therefore recommended
to try
./configure CC="cc"
and if that doesn't work, try
./configure CC="cc -nodtk"
On Solaris, don't put `/usr/ucb' early in your `PATH'. This
directory contains several dysfunctional programs; working variants of
these programs are available in `/usr/bin'. So, if you need `/usr/ucb'
in your `PATH', put it _after_ `/usr/bin'.
On Haiku, software installed for all users goes in `/boot/common',
not `/usr/local'. It is recommended to use the following options:
./configure --prefix=/boot/common
Specifying the System Type
==========================
There may be some features `configure' cannot figure out
automatically, but needs to determine by the type of machine the package
will run on. Usually, assuming the package is built to be run on the
_same_ architectures, `configure' can figure that out, but if it prints
a message saying it cannot guess the machine type, give it the
`--build=TYPE' option. TYPE can either be a short name for the system
type, such as `sun4', or a canonical name which has the form:
CPU-COMPANY-SYSTEM
where SYSTEM can have one of these forms:
OS
KERNEL-OS
See the file `config.sub' for the possible values of each field. If
`config.sub' isn't included in this package, then this package doesn't
need to know the machine type.
If you are _building_ compiler tools for cross-compiling, you should
use the option `--target=TYPE' to select the type of system they will
produce code for.
If you want to _use_ a cross compiler, that generates code for a
platform different from the build platform, you should specify the
"host" platform (i.e., that on which the generated programs will
eventually be run) with `--host=TYPE'.
Sharing Defaults
================
If you want to set default values for `configure' scripts to share,
you can create a site shell script called `config.site' that gives
default values for variables like `CC', `cache_file', and `prefix'.
`configure' looks for `PREFIX/share/config.site' if it exists, then
`PREFIX/etc/config.site' if it exists. Or, you can set the
`CONFIG_SITE' environment variable to the location of the site script.
A warning: not all `configure' scripts look for a site script.
Defining Variables
==================
Variables not defined in a site shell script can be set in the
environment passed to `configure'. However, some packages may run
configure again during the build, and the customized values of these
variables may be lost. In order to avoid this problem, you should set
them in the `configure' command line, using `VAR=value'. For example:
./configure CC=/usr/local2/bin/gcc
causes the specified `gcc' to be used as the C compiler (unless it is
overridden in the site shell script).
Unfortunately, this technique does not work for `CONFIG_SHELL' due to
an Autoconf bug. Until the bug is fixed you can use this workaround:
CONFIG_SHELL=/bin/bash /bin/bash ./configure CONFIG_SHELL=/bin/bash
`configure' Invocation
======================
`configure' recognizes the following options to control how it
operates.
`--help'
`-h'
Print a summary of all of the options to `configure', and exit.
`--help=short'
`--help=recursive'
Print a summary of the options unique to this package's
`configure', and exit. The `short' variant lists options used
only in the top level, while the `recursive' variant lists options
also present in any nested packages.
`--version'
`-V'
Print the version of Autoconf used to generate the `configure'
script, and exit.
`--cache-file=FILE'
Enable the cache: use and save the results of the tests in FILE,
traditionally `config.cache'. FILE defaults to `/dev/null' to
disable caching.
`--config-cache'
`-C'
Alias for `--cache-file=config.cache'.
`--quiet'
`--silent'
`-q'
Do not print messages saying which checks are being made. To
suppress all normal output, redirect it to `/dev/null' (any error
messages will still be shown).
`--srcdir=DIR'
Look for the package's source code in directory DIR. Usually
`configure' can determine that directory automatically.
`--prefix=DIR'
Use DIR as the installation prefix. *note Installation Names::
for more details, including other options available for fine-tuning
the installation locations.
`--no-create'
`-n'
Run the configure checks, but stop before creating any output
files.
`configure' also accepts some other, not widely useful, options. Run
`configure --help' for more details.

View File

@ -1,61 +0,0 @@
COM=CommonLibs
SQL=sqlite3
SR=.
LOCALLIBS=$(COM)/Logger.cpp $(COM)/Timeval.cpp $(COM)/Threads.cpp $(COM)/Sockets.cpp $(COM)/Configuration.cpp $(COM)/sqlite3util.cpp $(SR)/SubscriberRegistry.cpp $(COM)/Utils.cpp servershare.cpp
LIBS= -L$(SQL) $(LOCALLIBS) -losipparser2 -losip2 -lc -lpthread -lsqlite3
INCLUDES=-I$(COM) -I$(SQL) -I$(SR)
CPPFLAGS=-g -Wall -Wno-deprecated
DESTDIR :=
all: comp128 srmanager.cgi subscriberserver.cgi sipauthserve
comp128: comp128.c
g++ -o comp128 comp128.c
subscriberserver.cgi: subscriberserver.cpp $(LOCALLIBS)
g++ -o subscriberserver.cgi $(CPPFLAGS) $(INCLUDES) subscriberserver.cpp $(LIBS)
srmanager.cgi: srmanager.cpp $(LOCALLIBS)
g++ -o srmanager.cgi $(CPPFLAGS) $(INCLUDES) srmanager.cpp $(LIBS)
sipauthserve: sipauthserve.cpp $(LOCALLIBS)
g++ -o sipauthserve $(CPPFLAGS) $(INCLUDES) sipauthserve.cpp $(LIBS)
clean:
rm -f comp128 subscriberserver.cgi srmanager.cgi sipauthserve test.SubscriberRegistry/test
rm -r -f *.dSYM
# this needs "local7.debug<at least one tab>/var/log/openbts.log" in /etc/syslog.conf
test: all
cd test.SubscriberRegistry; ./runtest
cd test.sipauthserve; ./runtest
cd test.srmanager; ./runtest
cd test.subscriberserver; ./runtest
just3: all
cd test.SubscriberRegistry; ./runtest
cd test.srmanager; ./runtest
cd test.subscriberserver; ./runtest
IPATH=$(DESTDIR)/usr/local/bin/
CGIPATH=$(DESTDIR)/var/cgi-bin/
OPATH=$(DESTDIR)/OpenBTS/
CONFIGPATH=$(DESTDIR)/etc/OpenBTS/
# specifically for boa web server
install: all
mkdir -p $(IPATH)
install comp128 $(IPATH)
install hexmapper $(IPATH)
install syslogextractor $(IPATH)
mkdir -p $(CGIPATH)
install -m +s -o root srmanager.cgi $(CGIPATH)
install -m +s -o root subscriberserver.cgi $(CGIPATH)
mkdir -p $(OPATH)
install comp128 $(OPATH)
install sipauthserve $(OPATH)
install runloop.sipauthserve.sh $(OPATH)
mkdir -p $(CONFIGPATH)
install subscriberRegistry.example.sql $(CONFIGPATH)

View File

@ -1,6 +1,5 @@
#
# Copyright 2009 Free Software Foundation, Inc.
# Copyright 2010 Kestrel Signal Processing, Inc.
# Copyright 2008 Free Software Foundation, Inc.
#
# This software is distributed under the terms of the GNU Public License.
# See the COPYING file in the main directory for details.
@ -21,8 +20,10 @@
include $(top_srcdir)/Makefile.common
AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES)
AM_CXXFLAGS = $(STD_DEFINES_AND_INCLUDES) -Wall -ldl -lpthread
DESTDIR :=
AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES) $(WITH_INCLUDES)
AM_CXXFLAGS = -Wall -pthread -ldl
noinst_LTLIBRARIES = libSR.la
@ -32,3 +33,19 @@ libSR_la_SOURCES = \
noinst_HEADERS = \
SubscriberRegistry.h
# Order must be preserved
SUBDIRS = \
config \
sqlite3 \
CommonLibs \
Globals \
NodeManager \
apps
install: all
$(MAKE) -C ./apps install
dox: FORCE
doxygen doxconfig
FORCE:

48
Makefile.common Normal file
View File

@ -0,0 +1,48 @@
#
# Copyright 2008 Free Software Foundation, Inc.
#
# This software is distributed under the terms of the GNU Public License.
# See the COPYING file in the main directory for details.
#
# 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 3 of the License, 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, see <http://www.gnu.org/licenses/>.
#
AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES) $(WITH_INCLUDES)
AM_CXXFLAGS = -Wall -pthread -ldl
COMMON_INCLUDEDIR = $(top_srcdir)/CommonLibs
GLOBALS_INCLUDEDIR = $(top_srcdir)/Globals
SQLITE_INCLUDEDIR = $(top_srcdir)/sqlite3
NODEMANAGER_INCLUDEDIR = $(top_srcdir)/NodeManager
JSONBOX_INCLUDEDIR = $(top_srcdir)/NodeManager/JsonBox-0.4.3/include
JSONDB_INCLUDEDIR = $(top_srcdir)/NodeManager/JSONDB
SVNDEV = -D'SVN_REV="$(shell svn info $(top_builddir) | grep "Last Changed Rev:" | cut -d " " -f 4) CommonLibs:rev$(shell svn info $(top_builddir)/CommonLibs | grep "Last Changed Rev:" | cut -d " " -f 4)"'
STD_DEFINES_AND_INCLUDES = \
$(SVNDEV) \
-I$(COMMON_INCLUDEDIR) \
-I$(GLOBALS_INCLUDEDIR) \
-I$(NODEMANAGER_INCLUDEDIR) \
-I$(JSONBOX_INCLUDEDIR) \
-I$(JSONDB_INCLUDEDIR) \
-I$(SQLITE_INCLUDEDIR)
# These macros are referenced in apps/Makefile.am, which must be changed in sync with these.
COMMON_LA = $(top_builddir)/CommonLibs/libcommon.la
GLOBALS_LA = $(top_builddir)/Globals/libglobals.la
NODEMANAGER_LA = $(top_builddir)/NodeManager/libnodemanager.la -lzmq
SQLITE_LA = $(top_builddir)/sqlite3/libsqlite.la -ldl
MOSTLYCLEANFILES = *~

0
NEWS Normal file
View File

1
NodeManager Submodule

@ -0,0 +1 @@
Subproject commit c154368a55d081d7be241a14825e9a009dee093a

0
README Normal file
View File

View File

@ -1,6 +1,6 @@
/*
* Copyright 2011 Kestrel Signal Processing, Inc.
* Copyright 2011, 2012 Range Networks, Inc.
* Copyright 2011, 2012, 2013, 2014 Range Networks, Inc.
*
* This software is distributed under the terms of the GNU Affero Public License.
* See the COPYING file in the main directory for details.
@ -33,6 +33,7 @@
#include <iostream>
#include <sstream>
#include <fstream>
#include <algorithm> // for sort()
#include <Configuration.h>
extern ConfigurationTable gConfig;
@ -157,6 +158,15 @@ static const char* createSBTable = {
")"
};
static const char* createMEMSBTable = {
"CREATE TABLE IF NOT EXISTS memcache.mem_sip_buddies ("
"username varchar(80) primary key, "
"ipaddr varchar(45), "
"port int(5) default 5062, "
"rand varchar(33) default '', "
"sres varchar(33) default ''"
")"
};
int SubscriberRegistry::init()
{
@ -202,10 +212,243 @@ int SubscriberRegistry::init()
if (!sqlite3_command(mDB,enableWAL,mNumSQLTries)) {
LOG(EMERG) << "Cannot enable WAL mode on database at " << ldb << ", error message: " << sqlite3_errmsg(mDB);
}
#ifndef SR_API_ONLY
// memory based sip_buddies table
if (!sqlite3_command(mDB,"attach database ':memory:' as memcache",mNumSQLTries)) {
LOG(EMERG) << "Cannot create memcache database";
return 1;
}
if (!sqlite3_command(mDB,createMEMSBTable,mNumSQLTries)) {
LOG(EMERG) << "Cannot create memcache mem_sip_buddies table";
return 1;
}
if (!sqlite3_command(mDB,"INSERT INTO mem_sip_buddies SELECT username, ipaddr, port, rand, sres FROM sip_buddies",mNumSQLTries)) {
LOG(EMERG) << "Cannot populate mem_sip_buddies table with disk contents";
return 1;
}
if (!sqlite3_command(mDB,"ALTER TABLE mem_sip_buddies ADD dirty INTEGER DEFAULT 0",mNumSQLTries)) {
LOG(EMERG) << "Cannot add dirty column to mem_sip_buddies table";
return 1;
}
// Start the sync thread
mSyncer.start((void*(*)(void*))subscriberRegistrySyncer,NULL);
#endif
return 0;
}
string SubscriberRegistry::getResultsAsString(string query)
{
sqlite3_stmt *stmt;
if (sqlite3_prepare_statement(mDB, &stmt, query.c_str(), 5)) {
std::cout << " - sqlite3_prepare_statement() failed" << std::endl;
return "";
}
int state;
int row = 0;
int col;
int colMax = sqlite3_column_count(stmt);
const char* colName;
const char* colText;
ostringstream colNames;
ostringstream colTexts;
while (1) {
state = sqlite3_step(stmt);
if (state == SQLITE_ROW) {
for (col = 0; col < colMax; col++) {
colName = (const char*)sqlite3_column_name(stmt, col);
colText = (const char*)sqlite3_column_text(stmt, col);
if (row == 0) {
colNames << colName << " ";
}
if (colText == NULL) {
colTexts << "NULL" << " ";
} else {
colTexts << colText << " ";
}
}
} else if (state == SQLITE_DONE) {
break;
// TODO : handle more SQLITE_FAILURECODES here
} else {
return " - failed in while(1) in } else {";
}
colTexts << std::endl;
row++;
}
sqlite3_finalize(stmt);
ostringstream s;
string title = colNames.str();
if (title.length()) {
s << title << std::endl;
s << colTexts << std::endl;
} else {
s << " - none found" << std::endl;
}
return s.str();
}
vector<string> SubscriberRegistry::getTableColumns(string tableName)
{
vector<string> colNames;
stringstream tmp;
tmp << "select * from " << tableName << " limit 1";
sqlite3_stmt *stmt;
if (sqlite3_prepare_statement(mDB, &stmt, tmp.str().c_str(), 5)) {
std::cout << " - getTableColumns() - sqlite3_prepare_statement() failed" << std::endl;
return colNames;
}
tmp.str("");
int state;
int col;
int colMax = sqlite3_column_count(stmt);
while (1) {
state = sqlite3_step(stmt);
if (state == SQLITE_ROW) {
for (col = 0; col < colMax; col++) {
tmp << (const char*)sqlite3_column_name(stmt, col);
colNames.push_back(tmp.str());
tmp.str("");
}
} else if (state == SQLITE_DONE) {
break;
// TODO : handle more SQLITE_FAILURECODES here
} else {
std::cout << " - getTableColumns() - failed in while(1) in } else {" << std::endl;
return colNames;
}
}
sqlite3_finalize(stmt);
return colNames;
}
#ifndef SR_API_ONLY
string SubscriberRegistry::generateSyncToDiskQuery()
{
vector<string> columns = getTableColumns("sip_buddies");
if (!columns.size()) {
LOG(DEBUG) << "sip_buddies is empty";
return "";
}
sort(columns.begin(), columns.end());
std::stringstream ss;
unsigned i;
ss << "replace into sip_buddies (ipaddr, port, rand, sres, ";
for (i = 0; i < columns.size(); i++) {
if (columns[i].compare("ipaddr") == 0 || columns[i].compare("port") == 0 ||
columns[i].compare("rand") == 0 || columns[i].compare("sres") == 0) {
continue;
}
if (i != 0) {
ss << ", ";
}
if (columns[i].find("-") != std::string::npos) {
ss << "'" << columns[i] << "'";
} else {
ss << columns[i];
}
}
ss << ") select src.ipaddr, src.port, src.rand, src.sres, ";
for (i = 0; i < columns.size(); i++) {
if (columns[i].compare("ipaddr") == 0 || columns[i].compare("port") == 0 ||
columns[i].compare("rand") == 0 || columns[i].compare("sres") == 0) {
continue;
}
if (i != 0) {
ss << ", ";
}
ss << "dest.";
if (columns[i].find("-") != std::string::npos) {
ss << "'" << columns[i] << "'";
} else {
ss << columns[i];
}
}
ss << " from mem_sip_buddies src ";
ss << "inner join sip_buddies dest on src.username = dest.username ";
ss << "where src.dirty = \"1\"";
return ss.str();
}
bool SubscriberRegistry::syncMemoryDB()
{
bool ret = true;
string syncToDisk = generateSyncToDiskQuery();
string cleanDirty = "update mem_sip_buddies set dirty = \"0\"";
string syncFromDiskAddNewEntries = "INSERT INTO mem_sip_buddies "
"(username, ipaddr, port, rand, sres, dirty) "
"SELECT sip_buddies.username, sip_buddies.ipaddr, sip_buddies.port, sip_buddies.rand, sip_buddies.sres, '0' FROM sip_buddies "
"LEFT OUTER JOIN mem_sip_buddies ON (mem_sip_buddies.username=sip_buddies.username) "
"WHERE mem_sip_buddies.username IS NULL";
string syncFromDiskDeleteOldEntries = "DELETE FROM mem_sip_buddies "
"WHERE username IN "
"(SELECT mem_sip_buddies.username FROM mem_sip_buddies"
" LEFT JOIN sip_buddies ON mem_sip_buddies.username=sip_buddies.username"
" WHERE sip_buddies.username IS NULL"
")";
Timeval timer;
mLock.lock();
// this string is only non-empty if there are entries in sip_buddies
if (syncToDisk.length()) {
// sync dirty entries to disk
if (sqlUpdate(syncToDisk.c_str()) == FAILURE) {
LOG(ERR) << "syncToDisk failed";
} else {
// if it succeeds, mark these entries as clean again
LOG(INFO) << "syncToDisk succeeded";
if (sqlUpdate(cleanDirty.c_str()) == FAILURE) {
LOG(ERR) << "cleanDirty failed";
} else {
LOG(INFO) << "cleanDirty succeeded";
}
}
// sync new rows from the disk into memory
if (sqlUpdate(syncFromDiskAddNewEntries.c_str()) == FAILURE) {
LOG(ERR) << "syncFromDiskAddNewEntries failed";
} else {
LOG(INFO) << "syncFromDiskAddNewEntries succeeded";
}
}
// delete old entries from memory no longer found on the disk
if (sqlUpdate(syncFromDiskDeleteOldEntries.c_str()) == FAILURE) {
LOG(ERR) << "syncFromDiskDeleteOldEntries failed";
} else {
LOG(INFO) << "syncFromDiskDeleteOldEntries succeeded";
}
mLock.unlock();
LOG(INFO) << "syncMemoryDB() locked the db for " << timer.elapsed() << "ms";
return ret;
}
#endif
SubscriberRegistry::~SubscriberRegistry()
{
@ -214,20 +457,6 @@ SubscriberRegistry::~SubscriberRegistry()
SubscriberRegistry::Status SubscriberRegistry::sqlHttp(const char *stmt, char **resultptr)
{
LOG(INFO) << stmt;
HttpQuery qry("sql");
qry.send("stmts", stmt);
if (!qry.http(false)) return FAILURE;
const char *res = qry.receive("res");
if (!res) return FAILURE;
*resultptr = strdup(res);
return SUCCESS;
}
SubscriberRegistry::Status SubscriberRegistry::sqlLocal(const char *query, char **resultptr)
{
LOG(INFO) << query;
@ -273,19 +502,7 @@ char *SubscriberRegistry::sqlQuery(const char *unknownColumn, const char *table,
LOG(INFO) << "result = " << result;
return result;
}
// didn't find locally, so try over http
st = sqlHttp(os.str().c_str(), &result);
if ((st == SUCCESS) && result) {
// found over http but not locally, so cache locally
ostringstream os2;
os2 << "insert into " << table << "(" << knownColumn << ", " << unknownColumn << ") values (\"" <<
knownValue << "\",\"" << result << "\")";
// ignore whether it succeeds
sqlLocal(os2.str().c_str(), NULL);
LOG(INFO) << "result = " << result;
return result;
}
// didn't find locally or over http
// didn't find locally
LOG(INFO) << "not found: " << os.str();
return NULL;
}
@ -295,32 +512,48 @@ char *SubscriberRegistry::sqlQuery(const char *unknownColumn, const char *table,
SubscriberRegistry::Status SubscriberRegistry::sqlUpdate(const char *stmt)
{
LOG(INFO) << stmt;
SubscriberRegistry::Status st = sqlLocal(stmt, NULL);
sqlHttp(stmt, NULL);
// status of local is only important one because asterisk talks to that db directly
// must update local no matter what
return st;
return sqlLocal(stmt, NULL);
}
string SubscriberRegistry::imsiGet(string imsi, string key)
{
string name = imsi.substr(0,4) == "IMSI" ? imsi : "IMSI" + imsi;
char *st = sqlQuery(key.c_str(), "sip_buddies", "name", imsi.c_str());
char *st = sqlQuery(key.c_str(), "sip_buddies", "username", name.c_str());
if (!st) {
LOG(WARNING) << "cannot get key " << key << " for username " << imsi;
LOG(INFO) << "cannot get key " << key << " for username " << name;
return "";
}
return st;
}
#ifndef SR_API_ONLY
bool SubscriberRegistry::imsiSet(string imsi, string key, string value)
{
string name = imsi.substr(0,4) == "IMSI" ? imsi : "IMSI" + imsi;
ostringstream os;
os << "update sip_buddies set " << key << " = \"" << value << "\" where name = \"" << name << "\"";
return sqlUpdate(os.str().c_str()) == FAILURE;
os << "update mem_sip_buddies set dirty = \"1\", " << key << " = \"" << value << "\" where username = \"" << name << "\"";
mLock.lock();
SubscriberRegistry::Status ret = sqlUpdate(os.str().c_str());
mLock.unlock();
return ret == FAILURE;
}
bool SubscriberRegistry::imsiSet(string imsi, string key1, string value1, string key2, string value2)
{
string name = imsi.substr(0,4) == "IMSI" ? imsi : "IMSI" + imsi;
ostringstream os;
os << "update mem_sip_buddies set dirty = \"1\", " << key1 << " = \"" << value1 << "\"," << key2 << " = \"" << value2 << "\" where username = \"" << name << "\"";
mLock.lock();
SubscriberRegistry::Status ret = sqlUpdate(os.str().c_str());
mLock.unlock();
return ret == FAILURE;
}
#endif
char *SubscriberRegistry::getIMSI(const char *ISDN)
{
if (!ISDN) {
@ -431,34 +664,6 @@ SubscriberRegistry::Status SubscriberRegistry::addUser(const char* IMSI, const c
}
// For handover. Only remove the local cache. BS2 will have updated the global.
SubscriberRegistry::Status SubscriberRegistry::removeUser(const char* IMSI)
{
if (!IMSI) {
LOG(WARNING) << "SubscriberRegistry::addUser attempting add of NULL IMSI";
return FAILURE;
}
LOG(INFO) << "removeUser(" << IMSI << ")";
string server = gConfig.getStr("SubscriberRegistry.UpstreamServer");
if (server.length() == 0) {
LOG(INFO) << "not removing user if no upstream server";
return FAILURE;
}
ostringstream os;
os << "delete from sip_buddies where name = ";
os << "\"" << IMSI << "\"";
os << ";";
LOG(INFO) << os.str();
SubscriberRegistry::Status st = sqlLocal(os.str().c_str(), NULL);
ostringstream os2;
os2 << "delete from dialdata_table where dial = ";
os2 << "\"" << IMSI << "\"";
LOG(INFO) << os2.str();
SubscriberRegistry::Status st2 = sqlLocal(os2.str().c_str(), NULL);
return st == SUCCESS && st2 == SUCCESS ? SUCCESS : FAILURE;
}
char *SubscriberRegistry::mapCLIDGlobal(const char *local)
{
@ -487,307 +692,17 @@ SubscriberRegistry::Status SubscriberRegistry::RRLPUpdate(string name, string la
return sqlUpdate(os.str().c_str());
}
string SubscriberRegistry::getRandForAuthentication(bool sip, string IMSI)
#ifndef SR_API_ONLY
extern SubscriberRegistry gSubscriberRegistry;
void* subscriberRegistrySyncer(void*)
{
if (IMSI.length() == 0) {
LOG(WARNING) << "SubscriberRegistry::getRandForAuthentication attempting lookup of NULL IMSI";
return "";
}
LOG(INFO) << "getRandForAuthentication(" << IMSI << ")";
// get rand from SR server
HttpQuery qry("rand");
qry.send("imsi", IMSI);
qry.log();
if (!qry.http(sip)) return "";
return qry.receive("rand");
}
bool SubscriberRegistry::getRandForAuthentication(bool sip, string IMSI, uint64_t *hRAND, uint64_t *lRAND)
{
string strRAND = getRandForAuthentication(sip, IMSI);
if (strRAND.length() == 0) {
*hRAND = 0;
*lRAND = 0;
return false;
}
stringToUint(strRAND, hRAND, lRAND);
return true;
}
void SubscriberRegistry::stringToUint(string strRAND, uint64_t *hRAND, uint64_t *lRAND)
{
assert(strRAND.size() == 32);
string strhRAND = strRAND.substr(0, 16);
string strlRAND = strRAND.substr(16, 16);
stringstream ssh;
ssh << hex << strhRAND;
ssh >> *hRAND;
stringstream ssl;
ssl << hex << strlRAND;
ssl >> *lRAND;
}
string SubscriberRegistry::uintToString(uint64_t h, uint64_t l)
{
ostringstream os1;
os1.width(16);
os1.fill('0');
os1 << hex << h;
ostringstream os2;
os2.width(16);
os2.fill('0');
os2 << hex << l;
ostringstream os3;
os3 << os1.str() << os2.str();
return os3.str();
}
string SubscriberRegistry::uintToString(uint32_t x)
{
ostringstream os;
os.width(8);
os.fill('0');
os << hex << x;
return os.str();
}
SubscriberRegistry::Status SubscriberRegistry::authenticate(bool sip, string IMSI, uint64_t hRAND, uint64_t lRAND, uint32_t SRES)
{
string strRAND = uintToString(hRAND, lRAND);
string strSRES = uintToString(SRES);
return authenticate(sip, IMSI, strRAND, strSRES);
}
SubscriberRegistry::Status SubscriberRegistry::authenticate(bool sip, string IMSI, string rand, string sres)
{
if (IMSI.length() == 0) {
LOG(WARNING) << "SubscriberRegistry::authenticate attempting lookup of NULL IMSI";
return FAILURE;
}
LOG(INFO) << "authenticate(" << IMSI << "," << rand << "," << sres << ")";
HttpQuery qry("auth");
qry.send("imsi", IMSI);
qry.send("rand", rand);
qry.send("sres", sres);
qry.log();
if (!qry.http(sip)) return FAILURE;
const char *status = qry.receive("status");
if (strcmp(status, "SUCCESS") == 0) return SUCCESS;
if (strcmp(status, "FAILURE") == 0) return FAILURE;
// status is Kc
return SUCCESS;
}
bool SubscriberRegistry::useGateway(const char* ISDN)
{
// FIXME -- Do something more general in Asterisk.
// This is a hack for Burning Man.
int cmp = strncmp(ISDN,"88351000125",11);
return cmp!=0;
}
SubscriberRegistry::Status SubscriberRegistry::setPrepaid(const char *IMSI, bool yes)
{
ostringstream os;
os << "update sip_buddies set prepaid = " << (yes ? 1 : 0) << " where username = " << '"' << IMSI << '"';
return sqlUpdate(os.str().c_str());
}
SubscriberRegistry::Status SubscriberRegistry::isPrepaid(const char *IMSI, bool &yes)
{
char *st = sqlQuery("prepaid", "sip_buddies", "username", IMSI);
if (!st) {
LOG(NOTICE) << "cannot get prepaid status for username " << IMSI;
return FAILURE;
}
yes = *st == '1';
free(st);
return SUCCESS;
}
SubscriberRegistry::Status SubscriberRegistry::balanceRemaining(const char *IMSI, int &balance)
{
char *st = sqlQuery("account_balance", "sip_buddies", "username", IMSI);
if (!st) {
LOG(NOTICE) << "cannot get balance for " << IMSI;
return FAILURE;
}
balance = (int)strtol(st, (char **)NULL, 10);
free(st);
return SUCCESS;
}
SubscriberRegistry::Status SubscriberRegistry::addMoney(const char *IMSI, int moneyToAdd)
{
ostringstream os;
os << "update sip_buddies set account_balance = account_balance + " << moneyToAdd << " where username = " << '"' << IMSI << '"';
if (sqlUpdate(os.str().c_str()) == FAILURE) {
LOG(NOTICE) << "cannot update rate for username " << IMSI;
return FAILURE;
}
return SUCCESS;
}
int SubscriberRegistry::serviceCost(const char* service)
{
char *rateSt = sqlQuery("rate", "rates", "service", service);
if (!rateSt) {
LOG(ALERT) << "cannot get rate for service " << service;
return -1;
}
int rate = (int)strtol(rateSt, (char **)NULL, 10);
free(rateSt);
return rate;
}
SubscriberRegistry::Status SubscriberRegistry::serviceUnits(const char *IMSI, const char* service, int &units)
{
int balance;
Status stat = balanceRemaining(IMSI,balance);
if (stat == FAILURE) return FAILURE;
int rate = serviceCost(service);
if (rate<0) return FAILURE;
units = balance / rate;
return SUCCESS;
}
HttpQuery::HttpQuery(const char *req)
{
sends = map<string,string>();
sends["req"] = req;
receives = map<string,string>();
}
void HttpQuery::send(const char *label, string value)
{
sends[label] = value;
}
void HttpQuery::log()
{
ostringstream os;
bool first = true;
for (map<string,string>::iterator it = sends.begin(); it != sends.end(); it++) {
if (first) {
first = false;
} else {
os << "&";
}
os << it->first << "=" << it->second;
}
LOG(INFO) << os.str();
}
bool HttpQuery::http(bool sip)
{
// unique temporary file names
ostringstream os1;
ostringstream os2;
os1 << "/tmp/subscriberregistry.1." << getpid();
os2 << "/tmp/subscriberregistry.2." << getpid();
string tmpFile1 = os1.str();
string tmpFile2 = os2.str();
// write the request and params to temp file
ofstream file1(tmpFile1.c_str());
if (file1.fail()) {
LOG(ERR) << "HttpQuery::http: can't write " << tmpFile1.c_str();
return false;
}
bool first = true;
for (map<string,string>::iterator it = sends.begin(); it != sends.end(); it++) {
if (first) {
first = false;
} else {
file1 << "&";
}
file1 << it->first << "=" << it->second;
}
file1.close();
// call the server
string server = sip ?
gConfig.getStr("SIP.Proxy.Registration"):
gConfig.getStr("SubscriberRegistry.UpstreamServer");
if (server.length() == 0 && !sip) return false;
ostringstream os;
os << "curl -s --data-binary @" << tmpFile1.c_str() << " " << server << " > " << tmpFile2.c_str();
LOG(INFO) << os.str();
if (server == "testing") {
return false;
} else {
int st = system(os.str().c_str());
if (st != 0) {
LOG(ERR) << "curl call returned " << st;
return false;
}
while (true) {
sleep(15);
gSubscriberRegistry.syncMemoryDB();
}
// read the http return from another temp file
ifstream file2(tmpFile2.c_str());
if (file2.fail()) {
LOG(ERR) << "HTTPQuery::http: can't read " << tmpFile2.c_str();
return false;
}
string tmp;
while (getline(file2, tmp)) {
size_t pos = tmp.find('=');
if (pos != string::npos) {
string key = tmp.substr(0, pos);
string value = tmp.substr(pos+1);
if (key == "error") {
LOG(ERR) << "HTTPQuery::http error: " << value;
file2.close();
return false;
}
receives[key] = value;
} else {
file2.close();
LOG(ERR) << "HTTPQuery::http: bad server return:";
ifstream file22(tmpFile2.c_str());
while (getline(file22, tmp)) {
LOG(ERR) << tmp;
}
file22.close();
return false;
}
}
file2.close();
return true;
return NULL;
}
const char *HttpQuery::receive(const char *label)
{
map<string,string>::iterator it = receives.find(label);
return it == receives.end() ? NULL : it->second.c_str();
}
#endif
// vim: ts=4 sw=4

View File

@ -29,8 +29,8 @@
#include <map>
#include <stdlib.h>
#include <Logger.h>
// #include <Timeval.h>
// #include <Threads.h>
#include <Timeval.h>
#include <Threads.h>
#include <map>
#include <string>
#include "sqlite3.h"
@ -44,6 +44,11 @@ class SubscriberRegistry {
sqlite3 *mDB; ///< database connection
unsigned mNumSQLTries; ///< Number of times to try an sqlite command before giving up.
#ifndef SR_API_ONLY
mutable Mutex mLock; ///< control for multithreaded read/write access to the memory based sip_buddies table
Thread mSyncer; ///< thread responsible for synchronizing the memory and disk based sip_buddies tables
#endif
public:
~SubscriberRegistry();
@ -68,6 +73,28 @@ class SubscriberRegistry {
}
/**
Grab the memory based sqlite db as a table string
*/
string getResultsAsString(string query);
/**
Grab the columns from a table
*/
vector<string> getTableColumns(string tableName);
#ifndef SR_API_ONLY
/**
Generate the SQL query which is used to sync the in-memory database to disk
to account for differences in database schemas.
*/
string generateSyncToDiskQuery();
/**
Sync the in-memory sip_buddies table to disk.
*/
bool syncMemoryDB();
#endif
/**
Resolve an ISDN or other numeric address to an IMSI.
@ -102,20 +129,32 @@ class SubscriberRegistry {
char* getRegistrationIP(const char* IMSI);
/**
Set a specific variable indexed by imsi from sip_buddies
@param imsi The user's IMSI or SIP username.
@param key to index into table
Get a subscriber's property.
@param imsi imsi of the subscriber
@param key name of the property
*/
string imsiGet(string imsi, string key);
#ifndef SR_API_ONLY
/**
Set a specific variable indexed by imsi_from sip_buddies
@param imsi The user's IMSI or SIP username.
@param key to index into table
@param value to set indexed by the key
Set a subscriber's property.
@param imsi imsi of the subscriber
@param key name of the property
@param value value of the property
*/
bool imsiSet(string imsi, string key, string value);
/**
Set a subscriber's property.
@param imsi imsi of the subscriber
@param key1 name of the property
@param value1 value of the property
@param key2 name of the property
@param value2 value of the property
*/
bool imsiSet(string imsi, string key1, string value1, string key2, string value2);
#endif
/**
Add a new user to the SubscriberRegistry.
@param IMSI The user's IMSI or SIP username.
@ -141,99 +180,6 @@ class SubscriberRegistry {
char *mapCLIDGlobal(const char *local);
/**
Get a 128-bit number for authentication.
@param sip sip server (true) or http
@param IMSI The user's IMSI or SIP username.
@return the 128-bit number in hex
*/
string getRandForAuthentication(bool sip, string IMSI);
/**
Get a 128-bit number for authentication.
@param sip sip server (true) or http
@param IMSI The user's IMSI or SIP username;
@param hRAND upper 64 bits
@param lRAND lower 64 bits
*/
bool getRandForAuthentication(bool sip, string IMSI, uint64_t *hRAND, uint64_t *lRAND);
void stringToUint(string strRAND, uint64_t *hRAND, uint64_t *lRAND);
string uintToString(uint64_t h, uint64_t l);
string uintToString(uint32_t x);
SubscriberRegistry::Status authenticate(bool sip, string IMSI, uint64_t hRAND, uint64_t lRAND, uint32_t SRES);
/**
Authenticate a handset.
@param sip sip server (true) or http
@param IMSI The user's IMSI or SIP username.
@param rand RAND.
@param sres SRES
@return ok or fail
*/
SubscriberRegistry::Status authenticate(bool sip, string IMSI, string rand, string sres);
bool useGateway(const char* ISDN);
/**
Set whether a subscriber is prepaid.
@param IMSI Subscriber's IMSI
@param yes true for prepaid, false for postpaid
@return SUCCESS or FAILURE
*/
Status setPrepaid(const char *IMSI, bool yes);
/**
Is a subscriber postpaid?
@param IMSI Subscriber's IMSI
@param yes set to true if subscriber is postpaid, false if prepaid
@return SUCCESS or FAILURE
*/
Status isPrepaid(const char *IMSI, bool &yes);
/**
Get the balance remaining in a subscriber's account.
@param IMSI Subscriber's IMSI
@param balance current account balance
@return SUCCESS or FAILURE
*/
Status balanceRemaining(const char *IMSI, int &balance);
/**
Atomic operation to add to the account balance
@param IMSI subscriber's IMSI
@param moneyToAdd money to add (negative to subtract)
@return SUCCESS or FAILURE
*/
Status addMoney(const char *IMSI, int moneyToAdd);
/**
Return the number of "units" of a particular transcation type available in the user's account.
@param IMSI subscriber's IMSI
@param service the service type
@param units number of units of this service type remaining
@return SUCCESS or FAILURE
*/
Status serviceUnits(const char *IMSI, const char* service, int &units);
/**
Return the cost per unit of a specific service type.
*/
int serviceCost(const char* service);
/**
Update the RRLP location for user
@param name IMSI to be updated
@ -257,15 +203,6 @@ class SubscriberRegistry {
/**
Run sql statments over http.
@param stmt The sql statements.
@param resultptr Set this to point to the result of executing the statements.
*/
Status sqlHttp(const char *stmt, char **resultptr);
/**
Run an sql query (select unknownColumn from table where knownColumn = knownValue).
@param unknownColumn The column whose value you want.
@ -284,82 +221,10 @@ class SubscriberRegistry {
Status sqlUpdate(const char *stmt);
};
/** Class that SubscriberRegistry uses to setup an http query, run it, and get the results. */
class HttpQuery {
public:
/**
Constructor.
@param req The type of http query (sql, (get a)rand(om number) auth(enticate), etc).
*/
HttpQuery(const char *req);
/**
Specify a parameter to send in the http query.
@param label The label or name for the parameter.
@param value The value of the parameter.
*/
void send(const char *label, string value);
/**
Log the query.
*/
void log();
/**
This runs the http query.
@param sip Whether to call the sip server as opposed to the http server.
*/
bool http(bool sip);
/**
Get result from the http query.
@param label The label or name of the parameter whose value you want.
*/
const char *receive(const char *label);
private:
/** stores the parameters to send. */
map<string,string> sends;
/** stores the return parameters. */
map<string,string> receives;
};
/** Periodically triggers SubscriberRegistry::syncMemoryDB(). */
void* subscriberRegistrySyncer(void*);
#endif

58
apps/Makefile.am Normal file
View File

@ -0,0 +1,58 @@
#
# Copyright 2013 Range Networks, Inc.
#
# This software is distributed under the terms of the GNU Public License.
# See the COPYING file in the main directory for details.
#
# 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 3 of the License, 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, see <http://www.gnu.org/licenses/>.
#
include $(top_srcdir)/Makefile.common
DESTDIR :=
ourlibs = \
$(GLOBALS_LA) \
$(COMMON_LA) \
$(SQLITE_LA) \
$(NODEMANAGER_LA)
bin_PROGRAMS = sipauthserve comp128
sipauthserve_SOURCES = sipauthserve.cpp ../servershare.cpp ../SubscriberRegistry.cpp \
../NodeManager/JSONDB/JSONDB.cpp
sipauthserve_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES)
sipauthserve_LDADD = $(ourlibs) -losipparser2 -losip2
comp128_SOURCES = comp128.c
comp128_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES)
comp128_LDADD = $(ourlibs)
EXTRA_DIST = \
subscriberRegistry.example.sql \
sipauthserve.conf
# Populating comp128 in both locations for backwards compat
install: sipauthserve comp128
mkdir -p "$(DESTDIR)/OpenBTS/"
install comp128 "$(DESTDIR)/OpenBTS/"
install sipauthserve "$(DESTDIR)/OpenBTS/"
mkdir -p "$(DESTDIR)/usr/local/bin/"
install comp128 "$(DESTDIR)/usr/local/bin/"
install syslogextractor "$(DESTDIR)/usr/local/bin/"
install hexmapper "$(DESTDIR)/usr/local/bin/"
mkdir -p "$(DESTDIR)/etc/init/"
install sipauthserve.conf "$(DESTDIR)/etc/init/"
mkdir -p "$(DESTDIR)/etc/OpenBTS/"
install subscriberRegistry.example.sql "$(DESTDIR)/etc/OpenBTS/"

10
apps/sipauthserve.conf Normal file
View File

@ -0,0 +1,10 @@
# sipauthserve - Range Networks SIP Authorization Server
#
# This service runs sipauthserve from the point the system is
# started until it is shut down again.
start on stopped rc RUNLEVEL=[2345]
stop on runlevel [!2345]
respawn
exec /OpenBTS/sipauthserve

View File

@ -1,6 +1,6 @@
/*
* Copyright 2011 Kestrel Signal Processing, Inc.
* Copyright 2011 Range Networks, Inc.
* Copyright 2011, 2014 Range Networks, Inc.
*
* This software is distributed under the terms of the GNU Affero Public License.
* See the COPYING file in the main directory for details.
@ -44,7 +44,9 @@
#include <unistd.h>
#include <Logger.h>
#include <Configuration.h>
#include <Globals.h>
#include <NodeManager.h>
#include <JSONDB.h>
#include "servershare.h"
#include "SubscriberRegistry.h"
@ -58,6 +60,29 @@ int my_udp_port;
// just using this for the database access
SubscriberRegistry gSubscriberRegistry;
/** The remote node manager. */
NodeManager gNodeManager;
/** The JSON<->DB interface. */
JSONDB gJSONDB;
/** Application specific NodeManager logic for handling requests. */
JsonBox::Object nmHandler(JsonBox::Object& request)
{
JsonBox::Object response;
std::string command = request["command"].getString();
std::string action = request["action"].getString();
if (command.compare("subscribers") == 0) {
request["table"] = JsonBox::Value("subscribers");
response = gJSONDB.query(request);
} else {
response["code"] = JsonBox::Value(501);
}
return response;
}
void prettyPrint(const char *label, osip_message_t *sip)
{
char *dest=NULL;
@ -73,14 +98,13 @@ void prettyPrint(const char *label, osip_message_t *sip)
string imsiFromSip(osip_message_t *sip)
{
int i;
char *dest;
osip_uri_t *fromUri = osip_from_get_url(sip->from);
if (!fromUri) {
LOG(ERR) << "osip_from_get_url problem";
return "";
}
i = osip_uri_to_str(fromUri, &dest);
osip_uri_to_str(fromUri, &dest);
string imsi = dest;
osip_free(dest);
return imsi;
@ -88,14 +112,13 @@ string imsiFromSip(osip_message_t *sip)
string imsiToSip(osip_message_t *sip)
{
int i;
char *dest;
osip_uri_t *toUri = osip_to_get_url(sip->to);
if (!toUri) {
LOG(ERR) << "osip_to_get_url problem";
return "";
}
i = osip_uri_to_str(toUri, &dest);
osip_uri_to_str(toUri, &dest);
string imsi = dest;
osip_free(dest);
return imsi;
@ -104,7 +127,7 @@ string imsiToSip(osip_message_t *sip)
// is imsi in the database?
bool imsiFound(string imsi)
{
string x = imsiGet(imsi, "id");
string x = gSubscriberRegistry.imsiGet(imsi, "id");
return x.length() != 0;
}
@ -180,7 +203,7 @@ char *processBuffer(char *buffer)
osip_message_set_status_code (response, 200);
osip_message_set_reason_phrase (response, osip_strdup("OK"));
LOG(INFO) << "success, registering for IP address " << remote_host;
imsiSet(imsi,"ipaddr", remote_host, "port", remote_port);
gSubscriberRegistry.imsiSet(imsi,"ipaddr", remote_host, "port", remote_port);
} else {
// look for rand and sres in Authorization header (assume imsi same as in from)
string randx;
@ -238,9 +261,19 @@ char *processBuffer(char *buffer)
i = osip_list_add (&response->authentication_infos, auth, -1);
if (i < 0) LOG(ERR) << "problem adding authentication_infos";
}
// (pat 9-2013) Add the caller id.
static string calleridstr("callerid");
string callid = gSubscriberRegistry.imsiGet(imsi,calleridstr);
if (callid.size()) {
char buf[120];
// Per RFC3966 the telephone numbers should begin with "+" only if it is globally unique throughout the world.
// We should not add the "+" here, it should be in the database if appropriate.
snprintf(buf,120,"<tel:%s>",callid.c_str());
osip_message_set_header(response,"P-Associated-URI",buf);
}
// And register it.
LOG(INFO) << "success, registering for IP address " << remote_host;
imsiSet(imsi,"ipaddr", remote_host, "port", remote_port);
gSubscriberRegistry.imsiSet(imsi,"ipaddr", remote_host, "port", remote_port);
} else {
// sres does not match rand => 401 Unauthorized
osip_message_set_status_code (response, 401);
@ -269,6 +302,24 @@ char *processBuffer(char *buffer)
int
main(int argc, char **argv)
{
// TODO: Properly parse and handle any arguments
if (argc > 1) {
for (int argi = 0; argi < argc; argi++) {
if (!strcmp(argv[argi], "--version") ||
!strcmp(argv[argi], "-v")) {
cout << gVersionString << endl;
}
if (!strcmp(argv[argi], "--gensql")) {
cout << gConfig.getDefaultSQL(string(argv[0]), gVersionString) << endl;
}
if (!strcmp(argv[argi], "--gentex")) {
cout << gConfig.getTeX(string(argv[0]), gVersionString) << endl;
}
}
return 0;
}
sockaddr_in si_me;
sockaddr_in si_other;
int aSocket;
@ -278,13 +329,15 @@ main(int argc, char **argv)
srand ( time(NULL) + (int)getpid() );
my_udp_port = gConfig.getNum("SubscriberRegistry.Port");
gSubscriberRegistry.init();
gNodeManager.setAppLogicHandler(&nmHandler);
gNodeManager.start(45064);
// init osip lib
osip_t *osip;
int i=osip_init(&osip);
if (i!=0) {
LOG(ALERT) << "cannot init sip lib";
return NULL;
exit(1);
}
if ((aSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) {

View File

@ -0,0 +1,20 @@
--
-- This file was generated using: ./sipauthserve --gensql
-- binary version: release 4.0TRUNK built Nov 20 2013 rev6847M
--
-- Future changes should not be put in this file directly but
-- rather in the program's ConfigurationKey schema.
--
PRAGMA foreign_keys=OFF;
BEGIN TRANSACTION;
CREATE TABLE IF NOT EXISTS CONFIG ( KEYSTRING TEXT UNIQUE NOT NULL, VALUESTRING TEXT, STATIC INTEGER DEFAULT 0, OPTIONAL INTEGER DEFAULT 0, COMMENTS TEXT DEFAULT '');
INSERT OR IGNORE INTO "CONFIG" VALUES('Control.NumSQLTries','3',0,0,'Number of times to retry SQL queries before declaring a database access failure.');
INSERT OR IGNORE INTO "CONFIG" VALUES('Log.Alarms.Max','20',0,0,'Maximum number of alarms to remember inside the application.');
INSERT OR IGNORE INTO "CONFIG" VALUES('Log.File','',0,0,'Path to use for textfile based logging. By default, this feature is disabled. To enable, specify an absolute path to the file you wish to use, eg: /tmp/my-debug.log. To disable again, execute "unconfig Log.File".');
INSERT OR IGNORE INTO "CONFIG" VALUES('Log.Level','NOTICE',0,0,'Default logging level when no other level is defined for a file.');
INSERT OR IGNORE INTO "CONFIG" VALUES('SubscriberRegistry.A3A8','/OpenBTS/comp128',0,0,'Path to the program that implements the A3/A8 algorithm.');
INSERT OR IGNORE INTO "CONFIG" VALUES('SubscriberRegistry.Port','5064',0,0,'Port used by the SIP Authentication Server. NOTE: In some older releases (pre-2.8.1) this is called SIP.myPort.');
INSERT OR IGNORE INTO "CONFIG" VALUES('SubscriberRegistry.db','/var/lib/asterisk/sqlite3dir/sqlite3.db',0,0,'The location of the sqlite3 database holding the subscriber registry.');
COMMIT;

1578
autogen.sh Executable file

File diff suppressed because it is too large Load Diff

105
config.h
View File

@ -1,105 +0,0 @@
//
// Sorry, SubscriberRegistry didn't need fancy make setup before the library sync, and I wasn't in the mood to create it for the sync, so I just copied the openbts config.h here.
//
/* config.h. Generated from config.h.in by configure. */
/* config.h.in. Generated from configure.ac by autoheader. */
/* Define if building universal (internal helper macro) */
/* #undef AC_APPLE_UNIVERSAL_BUILD */
/* Define to 1 if you have the <byteswap.h> header file. */
#define HAVE_BYTESWAP_H 1
/* Define to 1 if you have the <dlfcn.h> header file. */
#define HAVE_DLFCN_H 1
/* Define if libc implements gethostbyname2_r */
#define HAVE_GETHOSTBYNAME2_R 1
/* Define if libc implements gethostbyname_r */
#define HAVE_GETHOSTBYNAME_R 1
/* Define to 1 if you have the <inttypes.h> header file. */
#define HAVE_INTTYPES_H 1
/* Define to 1 if you have the <memory.h> header file. */
#define HAVE_MEMORY_H 1
/* Define to 1 if you have the <stdint.h> header file. */
#define HAVE_STDINT_H 1
/* Define to 1 if you have the <stdlib.h> header file. */
#define HAVE_STDLIB_H 1
/* Define to 1 if you have the <strings.h> header file. */
#define HAVE_STRINGS_H 1
/* Define to 1 if you have the <string.h> header file. */
#define HAVE_STRING_H 1
/* Define to 1 if you have the <sys/stat.h> header file. */
#define HAVE_SYS_STAT_H 1
/* Define to 1 if you have the <sys/types.h> header file. */
#define HAVE_SYS_TYPES_H 1
/* Define to 1 if you have the <unistd.h> header file. */
#define HAVE_UNISTD_H 1
/* Define to the sub-directory in which libtool stores uninstalled libraries.
*/
#define LT_OBJDIR ".libs/"
/* Name of package */
#define PACKAGE "openbts"
/* Define to the address where bug reports for this package should be sent. */
#define PACKAGE_BUGREPORT ""
/* Define to the full name of this package. */
#define PACKAGE_NAME "openbts"
/* Define to the full name and version of this package. */
#define PACKAGE_STRING "openbts 3.0TRUNK"
/* Define to the one symbol short name of this package. */
#define PACKAGE_TARNAME "openbts"
/* Define to the home page for this package. */
#define PACKAGE_URL ""
/* Define to the version of this package. */
#define PACKAGE_VERSION "3.0TRUNK"
/* Define to 1 if you have the ANSI C header files. */
#define STDC_HEADERS 1
/* Define to 1 if you can safely include both <sys/time.h> and <time.h>. */
#define TIME_WITH_SYS_TIME 1
/* Version number of package */
#define VERSION "3.0TRUNK"
/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most
significant byte first (like Motorola and SPARC, unlike Intel). */
#if defined AC_APPLE_UNIVERSAL_BUILD
# if defined __BIG_ENDIAN__
# define WORDS_BIGENDIAN 1
# endif
#else
# ifndef WORDS_BIGENDIAN
/* # undef WORDS_BIGENDIAN */
# endif
#endif
/* Define to empty if `const' does not conform to ANSI C. */
/* #undef const */
/* Define to `__inline__' or `__inline' if that's what the C compiler
calls it, or to nothing if 'inline' is not supported under any name. */
#ifndef __cplusplus
/* #undef inline */
#endif
/* Define to `unsigned int' if <sys/types.h> does not define. */
/* #undef size_t */

30
config/Makefile.am Normal file
View File

@ -0,0 +1,30 @@
#
# Copyright 2008 Free Software Foundation, Inc.
#
# This software is distributed under the terms of the GNU Public License.
# See the COPYING file in the main directory for details.
#
# 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 3 of the License, 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, see <http://www.gnu.org/licenses/>.
#
include $(top_srcdir)/Makefile.common
# Install m4 macros in this directory
m4datadir = $(datadir)/aclocal
# List your m4 macros here
m4macros = \
pkg.m4
EXTRA_DIST = $(m4macros)

188
config/pkg.m4 Normal file
View File

@ -0,0 +1,188 @@
# pkg.m4 - Macros to locate and utilise pkg-config. -*- Autoconf -*-
#
# Copyright © 2004 Scott James Remnant <scott@netsplit.com>.
# Copyright © 2008 Free Software Foundation, Inc.
#
# 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 of the License, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
# As a special exception to the GNU General Public License, if you
# distribute this file as part of a program that contains a
# configuration script generated by Autoconf, you may include it under
# the same distribution terms that you use for the rest of that program.
# PKG_PROG_PKG_CONFIG([MIN-VERSION])
# ----------------------------------
AC_DEFUN([PKG_PROG_PKG_CONFIG],
[m4_pattern_forbid([^_?PKG_[A-Z_]+$])
m4_pattern_allow([^PKG_CONFIG(_PATH)?$])
AC_ARG_VAR([PKG_CONFIG], [path to pkg-config utility])dnl
if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then
AC_PATH_TOOL([PKG_CONFIG], [pkg-config])
fi
if test -n "$PKG_CONFIG"; then
_pkg_min_version=m4_default([$1], [0.18])
AC_MSG_CHECKING([pkg-config is at least version $_pkg_min_version])
if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then
AC_MSG_RESULT([yes])
else
AC_MSG_RESULT([no])
PKG_CONFIG=""
fi
fi[]dnl
])# PKG_PROG_PKG_CONFIG
# PKG_CHECK_EXISTS(MODULES, [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
#
# Check to see whether a particular set of modules exists. Similar
# to PKG_CHECK_MODULES(), but does not set variables or print errors.
#
#
# Similar to PKG_CHECK_MODULES, make sure that the first instance of
# this or PKG_CHECK_MODULES is called, or make sure to call
# PKG_CHECK_EXISTS manually
# --------------------------------------------------------------
AC_DEFUN([PKG_CHECK_EXISTS],
[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl
if test -n "$PKG_CONFIG" && \
AC_RUN_LOG([$PKG_CONFIG --exists --print-errors "$1"]); then
m4_ifval([$2], [$2], [:])
m4_ifvaln([$3], [else
$3])dnl
fi])
# _PKG_CONFIG([VARIABLE], [COMMAND], [MODULES])
# ---------------------------------------------
m4_define([_PKG_CONFIG],
[if test -n "$PKG_CONFIG"; then
if test -n "$$1"; then
pkg_cv_[]$1="$$1"
else
PKG_CHECK_EXISTS([$3],
[pkg_cv_[]$1=`$PKG_CONFIG --[]$2 "$3" 2>/dev/null`],
[pkg_failed=yes])
fi
else
pkg_failed=untried
fi[]dnl
])# _PKG_CONFIG
# _PKG_SHORT_ERRORS_SUPPORTED
# -----------------------------
AC_DEFUN([_PKG_SHORT_ERRORS_SUPPORTED],
[AC_REQUIRE([PKG_PROG_PKG_CONFIG])
if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
_pkg_short_errors_supported=yes
else
_pkg_short_errors_supported=no
fi[]dnl
])# _PKG_SHORT_ERRORS_SUPPORTED
# PKG_CHECK_MODULES(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND],
# [ACTION-IF-NOT-FOUND])
#
# E.g.,
# PKG_CHECK_MODULES(GSTUFF, gtk+-2.0 >= 1.3 glib = 1.3.4, action-if, action-not)
# defines:
#
# GSTUFF_LIBS
# GSTUFF_CFLAGS
# GSTUFF_INCLUDEDIR
# GSTUFF_CPPFLAGS # the -I, -D and -U's out of CFLAGS
#
# see pkg-config man page also defines GSTUFF_PKG_ERRORS on error
#
# Note that if there is a possibility the first call to
# PKG_CHECK_MODULES might not happen, you should be sure to include an
# explicit call to PKG_PROG_PKG_CONFIG in your configure.ac
#
# --------------------------------------------------------------
AC_DEFUN([PKG_CHECK_MODULES],
[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl
AC_ARG_VAR([$1][_CFLAGS], [C compiler flags for $1, overriding pkg-config])dnl
AC_ARG_VAR([$1][_LIBS], [linker flags for $1, overriding pkg-config])dnl
AC_ARG_VAR([$1][_INCLUDEDIR], [includedir for $1, overriding pkg-config])dnl
pkg_failed=no
AC_MSG_CHECKING([for $1])
_PKG_CONFIG([$1][_CFLAGS], [cflags], [$2])
if test x$cross_compiling = xyes
then
_PKG_CONFIG([$1][_LIBS], [libs-only-l --static], [$2])
else
_PKG_CONFIG([$1][_LIBS], [libs --static], [$2])
fi
_PKG_CONFIG([$1][_INCLUDEDIR], [variable=includedir], [$2])
m4_define([_PKG_TEXT], [Alternatively, you may set the environment variables $1[]_CFLAGS
and $1[]_LIBS to avoid the need to call pkg-config.
See the pkg-config man page for more details.])
if test $pkg_failed = yes; then
_PKG_SHORT_ERRORS_SUPPORTED
if test $_pkg_short_errors_supported = yes; then
$1[]_PKG_ERRORS=`$PKG_CONFIG --short-errors --errors-to-stdout --print-errors "$2"`
else
$1[]_PKG_ERRORS=`$PKG_CONFIG --errors-to-stdout --print-errors "$2"`
fi
# Put the nasty error message in config.log where it belongs
echo "$$1[]_PKG_ERRORS" >&AS_MESSAGE_LOG_FD
ifelse([$4], , [AC_MSG_ERROR(dnl
[Package requirements ($2) were not met:
$$1_PKG_ERRORS
Consider adjusting the PKG_CONFIG_PATH environment variable if you
installed software in a non-standard prefix.
_PKG_TEXT
])],
[AC_MSG_RESULT([no])
$4])
elif test $pkg_failed = untried; then
ifelse([$4], , [AC_MSG_FAILURE(dnl
[The pkg-config script could not be found or is too old. Make sure it
is in your PATH or set the PKG_CONFIG environment variable to the full
path to pkg-config.
_PKG_TEXT
To get pkg-config, see <http://pkg-config.freedesktop.org/>.])],
[$4])
else
$1[]_CFLAGS=$pkg_cv_[]$1[]_CFLAGS
$1[]_LIBS=$pkg_cv_[]$1[]_LIBS
$1[]_INCLUDEDIR=$pkg_cv_[]$1[]_INCLUDEDIR
$1[]_CPPFLAGS=""
for flag in $$1[]_CFLAGS; do
case $flag in
-I* | -D* | -U*) $1[]_CPPFLAGS="$$1[]_CPPFLAGS $flag" ;;
esac
done
pkg_cv_[]$1[]_CPPFLAGS=$$1[]_CPPFLAGS
AC_SUBST($1[]_CPPFLAGS)
AC_MSG_RESULT([yes])
ifelse([$3], , :, [$3])
fi[]dnl
])# PKG_CHECK_MODULES

87
configure.ac Normal file
View File

@ -0,0 +1,87 @@
dnl
dnl Copyright 2008, 2009, 2010 Free Software Foundation, Inc.
dnl
dnl This software is distributed under the terms of the GNU Public License.
dnl See the COPYING file in the main directory for details.
dnl
dnl This program is free software: you can redistribute it and/or modify
dnl it under the terms of the GNU General Public License as published by
dnl the Free Software Foundation, either version 3 of the License, or
dnl (at your option) any later version.
dnl
dnl This program is distributed in the hope that it will be useful,
dnl but WITHOUT ANY WARRANTY; without even the implied warranty of
dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
dnl GNU General Public License for more details.
dnl
dnl You should have received a copy of the GNU General Public License
dnl along with this program. If not, see <http://www.gnu.org/licenses/>.
dnl
AC_INIT(subscriberregistry,4.0TRUNK)
AC_PREREQ(2.57)
AC_CONFIG_SRCDIR([config/Makefile.am])
AC_CONFIG_AUX_DIR([.])
AM_CONFIG_HEADER(config.h)
AC_CANONICAL_BUILD
AC_CANONICAL_HOST
AC_CANONICAL_TARGET
AM_INIT_AUTOMAKE
AM_PROG_AS
AC_PROG_CXX
AC_PROG_LN_S
AC_PROG_MAKE_SET
AC_PROG_INSTALL
AC_PATH_PROG([RM_PROG], [rm])
AC_LIBTOOL_WIN32_DLL
AC_ENABLE_SHARED dnl do build shared libraries
AC_DISABLE_STATIC dnl don't build static libraries
AC_PROG_LIBTOOL
dnl Checks for header files.
AC_HEADER_STDC
dnl This is required for GnuRadio includes to understand endianess correctly:
AC_CHECK_HEADERS([byteswap.h])
dnl Checks for typedefs, structures, and compiler characteristics.
AC_C_CONST
AC_C_INLINE
AC_TYPE_SIZE_T
AC_HEADER_TIME
AC_C_BIGENDIAN
dnl Check for libzmq
if test ! -r "/usr/include/zmq.h" -a ! -r "/usr/local/include/zmq.h"; then
AC_MSG_ERROR([/usr/local/include/zmq.h not found. Install the range-libzmq package or manually build and install with $ sudo ./NodeManager/install_libzmq.sh])
fi
if test ! -r "/usr/include/zmq.hpp" -a ! -r "/usr/local/include/zmq.hpp"; then
AC_MSG_ERROR([/usr/local/include/zmq.hpp not found. Install the range-libzmq package or manually build and install with $ sudo ./NodeManager/install_libzmq.sh])
fi
AC_CHECK_LIB(zmq, zmq_init, ,[AC_MSG_ERROR([Cannot link with -lzmq. Install the range-libzmq package or manually build and install with $ sudo ./NodeManager/install_libzmq.sh])])
AC_MSG_CHECKING([whether libzmq installation works])
AC_LINK_IFELSE([AC_LANG_PROGRAM([#include <zmq.h>],
[zmq_init(1);])
],
[AC_MSG_RESULT([yes])],
[AC_MSG_ERROR([no. Install the range-libzmq package or manually build and install with $ sudo ./NodeManager/install_libzmq.sh])])
# Check for glibc-specific network functions
AC_CHECK_FUNC(gethostbyname_r, [AC_DEFINE(HAVE_GETHOSTBYNAME_R, 1, Define if libc implements gethostbyname_r)])
AC_CHECK_FUNC(gethostbyname2_r, [AC_DEFINE(HAVE_GETHOSTBYNAME2_R, 1, Define if libc implements gethostbyname2_r)])
dnl Output files
AC_CONFIG_FILES([\
Makefile \
apps/Makefile \
config/Makefile \
CommonLibs/Makefile \
Globals/Makefile \
NodeManager/Makefile \
sqlite3/Makefile \
])
AC_OUTPUT

11
debian/changelog vendored
View File

@ -1,12 +1,5 @@
sipauthserve-public (3.2) UNRELEASED; urgency=low
* Non-maintainer upload.
* Public release
-- Kurtis <kheimerl@rangenetworks.com> Tue, 30 Jul 2013 23:01:28 -0700
sipauthserve-public (3.2) UNRELEASED; urgency=low
sipauthserve (4.0) unstable; urgency=low
* Test
-- Donald C. Kirker <donald.kirker@rangenetworks.com> Mon, 22 Apr 2013 00:11:00 -0700
-- Donald C. Kirker <donald.kirker@rangenetworks.com> Mon, 22 Apr 2013 00:11:00 -0700

1
debian/compat vendored
View File

@ -1 +0,0 @@
1

15
debian/control vendored
View File

@ -1,19 +1,16 @@
Source: sipauthserve-public
Provides: sipauthserve
Source: sipauthserve
Section: comm
Priority: optional
Maintainer: Range Networks, Inc. <info@rangenetworks.com>
Homepage: http://www.rangenetworks.com/
Build-Depends: build-essential, debhelper (>= 7), libosip2-dev, pkg-config, autoconf, libsqlite3-dev (>= 3.7)
Build-Depends: build-essential, debhelper (>= 7), libosip2-dev, pkg-config, autoconf, libtool
Standards-Version: 3.7.3
Package: sipauthserve-public
Provides: sipauthserve
Version: TRUNK
Package: sipauthserve
Section: comm
Priority: optional
Architecture: any
Architecture: i386
Essential: no
Depends: sqlite, sqlite3 (>= 3.7), libosip2-4, libglib2.0-0, libgl1-mesa-glx, libc6, libasound2, pkg-config, libpcre3, gawk, screen
Description: OpenBTS Public software.
Depends: sqlite3, libosip2-4, libc6-i686, pkg-config, range-libzmq
Description: Range Networks - SIP Authorization Server

36
debian/postinst vendored
View File

@ -19,37 +19,37 @@ set -e
configure()
{
DB_LOC=/etc/OpenBTS/sipauthserve.db
DATE=$(date +'%Y-%m-%d.%H:%M:%S')
CONFIG_BACKUP=$DB_LOC.dump-$DATE
DATE=$(date --rfc-3339='date')
CONFIG_BACKUP=/etc/OpenBTS/OpenBTS.dump-$DATE
if [ ! -e $CONFIG_BACKUP ]; then
sqlite3 $DB_LOC ".dump" > $CONFIG_BACKUP
sqlite3 /etc/OpenBTS/OpenBTS.db ".dump" > $CONFIG_BACKUP
fi
sqlite3 $DB_LOC ".read /etc/OpenBTS/subscriberRegistry.example.sql" > /dev/null 2>&1
sqlite3 /etc/OpenBTS/OpenBTS.db ".read /etc/OpenBTS/subscriberRegistry.example.sql" > /dev/null 2>&1
SR_DIR=/var/lib/asterisk/sqlite3dir
SRPATH=$SR_DIR/sqlite3.db
if [ ! -d /var/lib/asterisk/sqlite3dir ]; then
mkdir -p /var/lib/asterisk/sqlite3dir
fi
SRPATH=/var/lib/asterisk/sqlite3dir/sqlite3.db
DATE=$(date --rfc-3339='date')
SR_CONFIG_BACKUP=$SRPATH.dump-$DATE
if [ ! -d $SR_DIR ]; then
mkdir -p $SR_DIR
fi
chown -R root:www-data $SR_DIR
chmod 775 $SR_DIR
if [[ -e $SRPATH && "`sqlite3 $SRPATH "PRAGMA table_info(sip_buddies)"`" != "" ]]; then
if [[ -e $SRPATH && "`sqlite3 $SRPATH "PRAGMA table_info(sip_buddies)" || true`" != "" ]]; then
if [ ! -e $SR_CONFIG_BACKUP ]; then
sqlite3 $SRPATH ".dump" > $SR_CONFIG_BACKUP
fi
HASPREPAID=`sqlite3 $SRPATH "PRAGMA table_info(sip_buddies)" | grep prepaid`
HASPREPAID=`sqlite3 $SRPATH "PRAGMA table_info(sip_buddies)" | grep prepaid || true`
if [ "$HASPREPAID" = "" ]; then
sqlite3 $SRPATH "alter table sip_buddies add prepaid int(1) DEFAULT 0 not null"
fi
HASBALANCE=`sqlite3 $SRPATH "PRAGMA table_info(sip_buddies)" | grep account_balance`
HASBALANCE=`sqlite3 $SRPATH "PRAGMA table_info(sip_buddies)" | grep account_balance || true`
if [ "$HASBALANCE" = "" ]; then
sqlite3 $SRPATH "alter table sip_buddies add column account_balance int(9) default 0"
fi
@ -64,8 +64,10 @@ fi
# set up permissions for webui, temporary fix until ui is replumbed to use json
# directory permissions
if [ -e $SRPATH ]; then
chmod 664 $SR_DIR/sqlite3*
chown -R root:www-data /var/lib/asterisk/sqlite3dir
chmod 775 /var/lib/asterisk/sqlite3dir/
if [ -e /var/lib/asterisk/sqlite3dir/sqlite3.db ]; then
chmod 664 /var/lib/asterisk/sqlite3dir/sqlite3*
fi
}

12
debian/prerm vendored
View File

@ -16,16 +16,14 @@ set -e
# for details, see http://www.debian.org/doc/debian-policy/ or
# the debian-policy package
APP=sipauthserve
remove()
{
killall $APP &>/dev/null
}
# remove()
# {
# killall runloop.sipauthserve.sh &>/dev/null
# }
case "$1" in
remove|upgrade|deconfigure)
remove
# remove
;;
failed-upgrade)

2
debian/rules vendored
View File

@ -20,11 +20,13 @@ export DH_ALWAYS_EXCLUDE=.svn:.git
export DH_OPTIONS
configure:
./autogen.sh
config: configure-stamp
configure-stamp: configure
dh_testdir
# Add here commands to configure the package.
./configure $(confflags)
touch configure-stamp
#Architecture

View File

@ -1,4 +0,0 @@
#!/bin/sh
# A script to restart and just keep sipauthserve running.
while true; do killall sipauthserve; sleep 2; ./sipauthserve; done

View File

@ -29,6 +29,7 @@
#include <fstream>
#include <cstdlib>
#include <Configuration.h>
#include <Utils.h>
#include <string.h>
#include "servershare.h"
@ -51,18 +52,6 @@ ConfigurationKeyMap getConfigurationKeys()
ConfigurationKeyMap map;
ConfigurationKey *tmp;
tmp = new ConfigurationKey("SIP.Proxy.Registration","127.0.0.1:5064",
"",
ConfigurationKey::CUSTOMERWARN,
ConfigurationKey::IPANDPORT,
"",
false,
"The IP host and port of the proxy to be used for registration and authentication. "
"This should normally be the subscriber registry SIP interface, not Asterisk."
);
map[tmp->getName()] = *tmp;
delete tmp;
tmp = new ConfigurationKey("SubscriberRegistry.A3A8","/OpenBTS/comp128",
"",
ConfigurationKey::CUSTOMERWARN,
@ -85,28 +74,6 @@ ConfigurationKeyMap getConfigurationKeys()
map[tmp->getName()] = *tmp;
delete tmp;
tmp = new ConfigurationKey("SubscriberRegistry.Manager.Title","Subscriber Registry",
"",
ConfigurationKey::CUSTOMER,
ConfigurationKey::STRING,
"^[[:print:]]+$",
false,
"Title text to be displayed on the subscriber registry manager."
);
map[tmp->getName()] = *tmp;
delete tmp;
tmp = new ConfigurationKey("SubscriberRegistry.Manager.VisibleColumns","name username type context host",
"",
ConfigurationKey::CUSTOMERTUNE,
ConfigurationKey::STRING,
"^(name){0,1} (username){0,1} (type){0,1} (context){0,1} (host){0,1}$",
false,
"A space separated list of columns to display in the subscriber registry manager."
);
map[tmp->getName()] = *tmp;
delete tmp;
tmp = new ConfigurationKey("SubscriberRegistry.Port","5064",
"",
ConfigurationKey::CUSTOMERWARN,
@ -118,58 +85,9 @@ ConfigurationKeyMap getConfigurationKeys()
map[tmp->getName()] = *tmp;
delete tmp;
tmp = new ConfigurationKey("SubscriberRegistry.UpstreamServer","",
"",
ConfigurationKey::CUSTOMERWARN,
ConfigurationKey::STRING_OPT,// audited
"",
false,
"URL of the subscriber registry HTTP interface on the upstream server. "
"By default, this feature is disabled. "
"To enable, specify a server URL eg: http://localhost/cgi/subreg.cgi. "
"To disable again, execute \"unconfig SubscriberRegistry.UpstreamServer\"."
);
map[tmp->getName()] = *tmp;
delete tmp;
return map;
}
string imsiGet(string imsi, string key)
{
string name = imsi.substr(0,4) == "IMSI" ? imsi : "IMSI" + imsi;
char *value;
if (!sqlite3_single_lookup(gSubscriberRegistry.db(), "sip_buddies", "username", name.c_str(), key.c_str(), value)) {
return "";
}
if (!value) { return ""; }
string retValue = value;
free(value);
return retValue;
}
void imsiSet(string imsi, string key, string value)
{
string name = imsi.substr(0,4) == "IMSI" ? imsi : "IMSI" + imsi;
ostringstream os2;
os2 << "update sip_buddies set " << key << " = \"" << value << "\" where username = \"" << name << "\"";
if (!sqlite3_command(gSubscriberRegistry.db(), os2.str().c_str())) {
LOG(ERR) << "sqlite3_command problem";
return;
}
}
void imsiSet(string imsi, string key1, string value1, string key2, string value2)
{
string name = imsi.substr(0,4) == "IMSI" ? imsi : "IMSI" + imsi;
ostringstream os2;
os2 << "update sip_buddies set " << key1 << " = \"" << value1 << "\"," << key2 << " = \"" << value2 << "\" where username = \"" << name << "\"";
if (!sqlite3_command(gSubscriberRegistry.db(), os2.str().c_str())) {
LOG(ERR) << "sqlite3_command problem";
return;
}
}
string soGenerateIt()
{
ostringstream os;
@ -180,20 +98,18 @@ string soGenerateIt()
return os.str();
}
// generate a 128' random number
string generateRand(string imsi)
{
string ki = imsiGet(imsi, "ki");
string ki = gSubscriberRegistry.imsiGet(imsi, "ki");
string ret;
if (ki.length() != 0) {
LOG(INFO) << "ki is known";
// generate and return rand (clear any cached rand or sres)
imsiSet(imsi, "rand", "", "sres", "");
gSubscriberRegistry.imsiSet(imsi, "rand", "", "sres", "");
ret = soGenerateIt();
} else {
string wRand = imsiGet(imsi, "rand");
string wRand = gSubscriberRegistry.imsiGet(imsi, "rand");
if (wRand.length() != 0) {
LOG(INFO) << "ki is unknown, rand is cached";
// return cached rand
@ -202,7 +118,7 @@ string generateRand(string imsi)
LOG(INFO) << "ki is unknown, rand is not cached";
// generate rand, cache rand, clear sres, and return rand
wRand = soGenerateIt();
imsiSet(imsi, "rand", wRand, "sres", "");
gSubscriberRegistry.imsiSet(imsi, "rand", wRand, "sres", "");
ret = wRand;
}
}
@ -246,8 +162,8 @@ bool randEqual(string a, string b)
if (a.empty() || b.empty())
return false;
gSubscriberRegistry.stringToUint(a, &rand1h, &rand1l);
gSubscriberRegistry.stringToUint(b, &rand2h, &rand2l);
Utils::stringToUint(a, &rand1h, &rand1l);
Utils::stringToUint(b, &rand2h, &rand2l);
LOG(DEBUG) << "rand1h = " << rand1h << ", rand1l = " << rand1l;
LOG(DEBUG) << "rand2h = " << rand2h << ", rand2l = " << rand2l;
@ -260,40 +176,31 @@ bool randEqual(string a, string b)
// may cache sres and rand
bool authenticate(string imsi, string randx, string sres, string *kc)
{
string ki = imsiGet(imsi, "ki");
string ki = gSubscriberRegistry.imsiGet(imsi, "ki");
bool ret;
if (ki.length() == 0) {
// Ki is unknown
string upstream_server = gConfig.getStr("SubscriberRegistry.UpstreamServer");
if (upstream_server.length()) {
LOG(INFO) << "ki unknown, upstream server";
// there's an upstream server for authentication.
// TODO - call the upstream server
ret = false;
string sres2 = gSubscriberRegistry.imsiGet(imsi, "sres");
if (sres2.length() == 0) {
LOG(INFO) << "ki unknown, no upstream server, sres not cached";
// first time - cache sres and rand so next time
// correct cell phone will calc same sres from same rand
gSubscriberRegistry.imsiSet(imsi, "sres", sres, "rand", randx);
ret = true;
} else {
// there's no upstream server for authentication. fake it.
string sres2 = imsiGet(imsi, "sres");
if (sres2.length() == 0) {
LOG(INFO) << "ki unknown, no upstream server, sres not cached";
// first time - cache sres and rand so next time
// correct cell phone will calc same sres from same rand
imsiSet(imsi, "sres", sres, "rand", randx);
ret = true;
} else {
LOG(INFO) << "ki unknown, no upstream server, sres cached";
// check against cached values of rand and sres
string rand2 = imsiGet(imsi, "rand");
// TODO - on success, compute and return kc
LOG(DEBUG) << "comparing " << sres << " to " << sres2 << " and " << randx << " to " << rand2;
ret = sresEqual(sres, sres2) && randEqual(randx, rand2);
}
LOG(INFO) << "ki unknown, no upstream server, sres cached";
// check against cached values of rand and sres
string rand2 = gSubscriberRegistry.imsiGet(imsi, "rand");
// TODO - on success, compute and return kc
LOG(DEBUG) << "comparing " << sres << " to " << sres2 << " and " << randx << " to " << rand2;
ret = sresEqual(sres, sres2) && randEqual(randx, rand2);
}
} else {
LOG(INFO) << "ki known";
// Ki is known, so do normal authentication
ostringstream os;
// per user value from subscriber registry
string a3a8 = imsiGet(imsi, "a3_a8");
string a3a8 = gSubscriberRegistry.imsiGet(imsi, "a3_a8");
if (a3a8.length() == 0) {
// config value is default
a3a8 = gConfig.getStr("SubscriberRegistry.A3A8");
@ -328,62 +235,6 @@ bool authenticate(string imsi, string randx, string sres, string *kc)
return ret;
}
void decodeQuery(map<string,string> &args)
{
string query;
// this works for GET or POST.
// get the request method
char *g = getenv("REQUEST_METHOD");
string method = g ? g : "";
LOG(INFO) << "REQUEST_METHOD = " << g;
// if POST, then read from stdin the number of bytes specified in CONTENT_LENGTH, and that's the query
if (method == "POST") {
int lth = atoi(getenv("CONTENT_LENGTH"));
LOG(INFO) << "CONTENT_LENGTH = " << lth;
char *buf = new char[lth+1];
cin.get(buf, lth+1);
int nread = cin.gcount();
if (nread != lth) {
LOG(ERR) << "content length changed to " << nread;
lth = nread;
}
query = string(buf, lth);
LOG(INFO) << "QUERY = " << query;
delete[] buf;
// if GET, then the query is in the environment variable QUERY_STRING
} else if (method == "GET") {
char *q = getenv("QUERY_STRING");
query = q ? q : "";
LOG(INFO) << "QUERY_STRING = " << q;
}
if (query.length() != 0) {
// fields of http request are separated with "&"
vector<string> fields;
split('&', query, &fields);
vector<string>::iterator it;
for (it = fields.begin(); it != fields.end(); it++) {
string field = *it;
size_t p = field.find('=');
string key = field.substr(0, p);
string value = field.substr(p+1);
p = 0;
while (1) {
size_t q = value.find('%', p);
if (q == string::npos) break;
string hex = value.substr(q+1, 2);
char s[2];
strcpy(s, "x");
int i;
sscanf(hex.c_str(), "%x", &i);
s[0] = i;
string hexx = s;
value.replace(q, 3, hexx);
}
args[key] = value;
}
}
}
string join(string separator, vector<string> &strings)
{
string result("");

View File

@ -34,31 +34,6 @@ using namespace std;
*/
ConfigurationKeyMap getConfigurationKeys();
/**
Get a subscriber's property.
@param imsi imsi of the subscriber
@param key name of the property
*/
string imsiGet(string imsi, string key);
/**
Set a subscriber's property.
@param imsi imsi of the subscriber
@param key name of the property
@param value value of the property
*/
void imsiSet(string imsi, string key, string value);
/**
Set a subscriber's property.
@param imsi imsi of the subscriber
@param key1 name of the property
@param value1 value of the property
@param key2 name of the property
@param value2 value of the property
*/
void imsiSet(string imsi, string key1, string value1, string key2, string value2);
/**
Generate a 128-bit random number.
@param imsi imsi of subscriber the random number is for
@ -73,12 +48,6 @@ string generateRand(string imsi);
*/
bool authenticate(string imsi, string rand, string sres, string *kc);
/**
Decode the html query.
@param args mapping of query key->value pairs.
*/
void decodeQuery(map<string,string> &args);
/**
Join the strings in strings, separated by separator
@param separator the separator

@ -1 +1 @@
Subproject commit 3d0dbe8e7a819585cac5064beabe9b22f8d47235
Subproject commit effc8fe4744285c07e3710ab97231537a27b7997

View File

@ -1,307 +0,0 @@
/*
* Copyright 2011 Kestrel Signal Processing, Inc.
* Copyright 2011 Range Networks, Inc.
*
* This software is distributed under the terms of the GNU Affero Public License.
* See the COPYING file in the main directory for details.
*
* This use of this software may be subject to additional restrictions.
* See the LEGAL file in the main directory for details.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <iostream>
#include <sstream>
#include <map>
#include <vector>
#include <string>
#include <sqlite3.h>
#include <time.h>
#include "Configuration.h"
#include "Logger.h"
#include <string.h>
#include "servershare.h"
#include "SubscriberRegistry.h"
#include <algorithm>
using namespace std;
ConfigurationTable gConfig("/etc/OpenBTS/OpenBTS.db", "srmanager", getConfigurationKeys());
Log dummy("srmanager",gConfig.getStr("Log.Level").c_str(),LOG_LOCAL7);
map<string,string> gArgs;
string gDatabase;
string gVisibleSipColumns;
string gUrl;
string gTitle;
string gVisibleExtColumns = "exten dial";
// just using this for the database access
SubscriberRegistry gSubscriberRegistry;
#define NO_BUTTON 0
#define UPDATE_BUTTON 1
#define ADD_BUTTON 2
#define DELETE_BUTTON 4
// may need some way to add/update a NULL field
void tableRow(vector<string> &names, vector<string> &values, int buttons, string id)
{
cout << "<form name=\"input\" action=\"" << gUrl << "\" method=\"post\">\n";
for (size_t i = 0; i < names.size(); i++) {
string name = names[i];
string value = values.size() == 0 ? "" : values[i];
cout << "<input type=\"text\" name=\"" << name << "\" value=\"" << value << "\" />\n";
}
cout << "<input type=\"hidden\" name=\"id\" value=\"" << id << "\" />\n";
if (buttons & UPDATE_BUTTON) {
cout << "<input type=\"submit\" name=\"what\" value=\"Update\" />\n";
}
if (buttons & ADD_BUTTON) {
cout << "<input type=\"submit\" name=\"what\" value=\"Add\" />\n";
}
if (buttons & DELETE_BUTTON) {
cout << "<input type=\"submit\" name=\"what\" value=\"Delete\" />\n";
}
cout << "</form>\n";
}
// make the header a fake form to get the same widths
void initTable(vector<string> &cols)
{
tableRow(cols, cols, NO_BUTTON, "0");
}
void table(const char *tableName, vector<string> &cols, bool addButtonP, const char *note)
{
cout << "<h4>" << gDatabase << "." << tableName << " " << note << "</h4>\n";
initTable(cols);
ostringstream os;
os << "select id," << join(",", cols) << " from " << tableName;
sqlite3_stmt *stmt;
if (sqlite3_prepare_statement(gSubscriberRegistry.db(), &stmt,os.str().c_str())) {
LOG(ERR) << "sqlite3_prepare_statement problem - statement: " << os.str();
LOG(ERR) << " " << sqlite3_errmsg(gSubscriberRegistry.db());
return;
}
int src = sqlite3_run_query(gSubscriberRegistry.db(), stmt);
while (src == SQLITE_ROW) {
vector<string> values;
const char *id = (const char*)sqlite3_column_text(stmt, 0);
for (int i = 1; i <= (int)cols.size(); i++) {
const char *value = (const char*)sqlite3_column_text(stmt, i);
values.push_back(value ? value : "(null)");
}
tableRow(cols, values, UPDATE_BUTTON | DELETE_BUTTON, id);
src = sqlite3_run_query(gSubscriberRegistry.db(), stmt);
}
sqlite3_finalize(stmt);
if (addButtonP) {
vector<string> dummy;
tableRow(cols, dummy, ADD_BUTTON, "0");
}
}
void getFields(vector<string> *fields, vector<bool> *isSet)
{
vector<string> vsc;
split(' ', gVisibleSipColumns, &vsc);
sqlite3_stmt *stmt;
const char *cmd = "pragma table_info(sip_buddies)";
if (sqlite3_prepare_statement(gSubscriberRegistry.db(), &stmt, cmd)) {
LOG(ERR) << "sqlite3_prepare_statement problem - statement: " << cmd;
LOG(ERR) << " " << sqlite3_errmsg(gSubscriberRegistry.db());
return;
}
int src = sqlite3_run_query(gSubscriberRegistry.db(), stmt);
while (src == SQLITE_ROW) {
string field = (char*)sqlite3_column_text(stmt, 1);
fields->push_back(field);
isSet->push_back(find(vsc.begin(), vsc.end(), field) != vsc.end());
src = sqlite3_run_query(gSubscriberRegistry.db(), stmt);
}
sqlite3_finalize(stmt);
}
void mainTables()
{
cout << "<form name=\"provision\" action=\"" << gUrl << "\" method=\"post\">\n";
cout << "Phone Number = ";
cout << "<input type=\"text\" name=\"phonenumber\" value=\"\" />\n";
cout << "IMSI = ";
cout << "<input type=\"text\" name=\"imsi\" value=\"\" />\n";
cout << "<input type=\"submit\" name=\"what\" value=\"Provision\" />\n";
cout << "</form>\n";
cout << "<br><hr><br>\n";
vector<string> vsc;
split(' ', gVisibleSipColumns, &vsc);
table("sip_buddies", vsc, false, "(scroll down to change which fields of sip_buddies are visible)");
cout << "<hr>";
vector<string> vec;
split(' ', gVisibleExtColumns, &vec);
table("dialdata_table", vec, true, "");
cout << "<br><hr><br>\n";
cout << "<FORM METHOD=\"LINK\" ACTION=\"" << gUrl << "\"> <INPUT TYPE=\"submit\" VALUE=\"Refresh\"> </FORM>\n";
cout << "<br><hr><br>\n";
cout << "<h4>Selected fields are included in sip_buddies table (submit button at bottom)</h4>\n";
cout << "<form method=\"post\" action=\"" << gUrl << "\">\n";
vector<string> fields;
vector<bool> isSet;
getFields(&fields, &isSet);
for (int i = 0; i < (int)fields.size(); i++) {
string field = fields[i];
string checked = isSet[i] ? "checked" : "";
cout << "<input type=\"checkbox\" name=\"" << field << "\" value=\"" << field << "\"" << checked << " /> " << field << "<br />\n";
}
cout << "<br><INPUT TYPE=SUBMIT name=\"what\" VALUE=\"Submit\">\n";
cout << "</form>\n";
}
string nullCheck(string s)
{
if (s == "(null)") {
return "NULL";
} else {
return "\"" + s + "\"";
}
}
void doCmd(string cmd)
{
string table;
vector<string> cols;
if (gArgs.find("dial") == gArgs.end()) {
table = "sip_buddies";
split(' ', gVisibleSipColumns, &cols);
} else {
table = "dialdata_table";
split(' ', gVisibleExtColumns, &cols);
}
string id = gArgs["id"];
ostringstream os;
if (cmd == "add") {
string names = join(",", cols);
vector<string> values0;
vector<string>::iterator it;
for (it = cols.begin(); it != cols.end(); it++) {
values0.push_back(nullCheck(gArgs[*it]));
}
string values = join(",", values0);
os << "insert into " << table << " (" << names << ") values (" << values << ")";
} else if (cmd == "delete") {
os << "delete from " << table << " where id = " << id;
} else if (cmd == "update") {
vector<string> sets0;
vector<string>::iterator it;
for (it = cols.begin(); it != cols.end(); it++) {
sets0.push_back(*it + "=" + nullCheck(gArgs[*it]));
}
string sets = join(",", sets0);
os << "update " << table << " set " << sets << " where id = " << id;
} else {
LOG(ERR) << "internal error";
}
LOG(INFO) << os.str();
if (!sqlite3_command(gSubscriberRegistry.db(), os.str().c_str())) {
LOG(ERR) << "sqlite3_command problem - statement: " << os.str();
return;
}
mainTables();
}
void initHtml()
{
cout << "Content-Type: text/html\n\n";
cout << "<HTML>\n";
cout << "<HEAD>\n";
cout << "<TITLE>" << gTitle << "</TITLE>\n";
cout << "</HEAD>\n";
cout << "<BODY>\n";
cout << "<h4>" << gTitle << "</h4>\n";
time_t rawtime;
time ( &rawtime );
struct tm * timeinfo;
timeinfo = localtime ( &rawtime );
cout << "<h4>" << asctime(timeinfo) << "</h4>\n";
}
void endHtml()
{
cout << "</BODY>\n";
cout << "</HTML>\n";
}
void doVisibles()
{
gVisibleSipColumns = "";
map<string,string>::iterator it;
bool first = true;
for (it = gArgs.begin(); it != gArgs.end(); it++) {
if (it->first == "what") continue;
if (first) {
first = false;
} else {
gVisibleSipColumns += " ";
}
gVisibleSipColumns += it->first;
}
if (!gConfig.set("SubscriberRegistry.Manager.VisibleColumns", gVisibleSipColumns)) {
LOG(ERR) << "unable to update SubscriberRegistry.Manager.VisibleColumns";
}
}
int main(int argc, char **argv)
{
gSubscriberRegistry.init();
// start the html return
initHtml();
// read the config file
gVisibleSipColumns = gConfig.getStr("SubscriberRegistry.Manager.VisibleColumns");
gUrl = "/cgi/srmanager.cgi";
gTitle = gConfig.getStr("SubscriberRegistry.Manager.Title");
// connect to the database
gDatabase = gConfig.getStr("SubscriberRegistry.db");
// decode the http query
decodeQuery(gArgs);
// execute command
string what = gArgs["what"];
if (!what.length() || what == "Main") {
mainTables();
} else if (what == "Add") {
doCmd("add");
} else if (what == "Update") {
doCmd("update");
} else if (what == "Delete") {
doCmd("delete");
} else if (what == "Provision") {
gSubscriberRegistry.addUser(gArgs["imsi"].c_str(), gArgs["phonenumber"].c_str());
mainTables();
} else if (what == "Submit") {
doVisibles();
mainTables();
} else {
cout << "unrecognized what parameter<br>\n";
map<string,string>::iterator it;
for (it = gArgs.begin(); it != gArgs.end(); it++) {
cout << it->first << " -> " << it->second << "<br>\n";
}
}
// finish the html return
endHtml();
}

View File

@ -1,11 +0,0 @@
PRAGMA foreign_keys=OFF;
BEGIN TRANSACTION;
CREATE TABLE IF NOT EXISTS CONFIG ( KEYSTRING TEXT UNIQUE NOT NULL, VALUESTRING TEXT, STATIC INTEGER DEFAULT 0, OPTIONAL INTEGER DEFAULT 0, COMMENTS TEXT DEFAULT '');
INSERT OR IGNORE INTO "CONFIG" VALUES('SubscriberRegistry.A3A8','./comp128',0,0,'Path to the program that implements the A3/A8 algorithm.');
INSERT OR IGNORE INTO "CONFIG" VALUES('SubscriberRegistry.Manager.Title','Subscriber Registry',0,0,'Title text to be displayed on the subscriber registry manager.');
INSERT OR IGNORE INTO "CONFIG" VALUES('SubscriberRegistry.Port','5064',0,0,'Port used by the SIP Authentication Server. NOTE: In some older releases (pre-2.8.1) this is called SIP.myPort.');
INSERT OR IGNORE INTO "CONFIG" VALUES('SubscriberRegistry.UpstreamServer','',0,0,'URL of the subscriber registry HTTP interface on the upstream server. By default, this feature is disabled. To enable, specify a server URL eg: http://localhost/cgi/subreg.cgi. To disable again, execute "unconfig SubscriberRegistry.UpstreamServer".');
INSERT OR IGNORE INTO "CONFIG" VALUES('SubscriberRegistry.db','/var/lib/asterisk/sqlite3dir/sqlite3.db',0,0,'The location of the sqlite3 database holding the subscriber registry.');
COMMIT;

View File

@ -1,193 +0,0 @@
/*
* Copyright 2011 Kestrel Signal Processing, Inc.
* Copyright 2011 Range Networks, Inc.
*
* This software is distributed under the terms of the GNU Affero Public License.
* See the COPYING file in the main directory for details.
*
* This use of this software may be subject to additional restrictions.
* See the LEGAL file in the main directory for details.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdlib.h>
#include <iostream>
#include <sstream>
#include <fstream>
#include <map>
#include <vector>
#include <string>
#include <sqlite3.h>
#include <time.h>
#include "Configuration.h"
#include "Logger.h"
#include <string.h>
#include "servershare.h"
#include "SubscriberRegistry.h"
using namespace std;
ConfigurationTable gConfig("/etc/OpenBTS/OpenBTS.db", "subscriberserver", getConfigurationKeys());
Log dummy("subscriberserver",gConfig.getStr("Log.Level").c_str(),LOG_LOCAL7);
// just using this for the database access
SubscriberRegistry gSubscriberRegistry;
// map of http query parameters and values
map<string,string> gArgs;
// lines of http response
vector<string> gResponse;
// retrieve a query field from args
string getArg(string label)
{
if (gArgs.find(label) == gArgs.end()) {
LOG(ERR) << "error: " << label << " missing";
return "";
}
return gArgs[label];
}
// run an sql statement through sqlite3 to get a response
void generateSqlResponse()
{
string stmts = getArg("stmts");
vector<string> vstmts;
split(';', stmts, &vstmts);
vector<string>::iterator it;
for (it = vstmts.begin(); it != vstmts.end(); it++) {
sqlite3_stmt *stmt;
if (sqlite3_prepare_statement(gSubscriberRegistry.db(), &stmt, it->c_str())) {
LOG(ERR) << "sqlite3_prepare_statement problem - statement: " << it->c_str();
return;
}
int src = sqlite3_run_query(gSubscriberRegistry.db(), stmt);
while (src == SQLITE_ROW) {
string resp = "res=";
int cols = sqlite3_column_count(stmt);
for (int i = 0; i < cols; i++) {
resp.append((const char*)sqlite3_column_text(stmt, i));
}
gResponse.push_back(resp);
src = sqlite3_run_query(gSubscriberRegistry.db(), stmt);
}
}
}
void sresCheck(bool b)
{
if (b) {
// SRESs match. return success (or Kc (TODO))
gResponse.push_back("status=SUCCESS");
} else {
// SRESs don't match. return failure.
gResponse.push_back("status=FAILURE");
}
}
// authenticate
void generateAuthResponse()
{
string imsi = getArg("imsi");
string randx = getArg("rand");
string sres = getArg("sres");
string kc;
bool st = authenticate(imsi, randx, sres, &kc);
sresCheck(st);
}
// generate a 128' random number
void generateRandResponse()
{
string imsi = getArg("imsi");
string randx = generateRand(imsi);
gResponse.push_back("rand=" + randx);
gResponse.push_back("imsi=" + imsi);
}
// generate our http response, putting each line of it into vector response
void generateResponse()
{
if (gArgs.find("req") == gArgs.end()) {
LOG(ERR) << "req not specified";
return;
}
// check for request types
string req = gArgs["req"];
if (req == "sql") {
generateSqlResponse();
return;
}
if (req == "rand") {
generateRandResponse();
return;
}
if (req == "auth") {
generateAuthResponse();
return;
}
// if none of the above, then it's an error
LOG(ERR) << "unrecognized request";
}
// write the query to the log file
void logQuery()
{
// list query key->value pairs
map<string,string>::iterator it;
LOG(INFO) << "query";
for (it = gArgs.begin(); it != gArgs.end(); it++) {
LOG(INFO) << " " << it->first << " -> " << it->second;
}
}
// write the http response from the global array @response to the log file
void logResponse()
{
LOG(INFO) << "response";
vector<string>::iterator it;
for (it = gResponse.begin(); it != gResponse.end(); it++) {
LOG(INFO) << " " << *it;
}
}
// print the http response to stdout
void respond()
{
vector<string>::iterator it;
for (it = gResponse.begin(); it != gResponse.end(); it++) {
cout << *it << endl;
}
}
int main()
{
cout << "Content-Type: text\n\n";
srand ( time(NULL) + (int)getpid() );
// decode the http query
decodeQuery(gArgs);
// write the http query into the log file
logQuery();
// put the http response into the global array @response
generateResponse();
// write the http response to the log file
logResponse();
// print out the http response to stdout
respond();
}

View File

@ -1,3 +0,0 @@
CommonLibs http://wush.net/svn/range/software/public/CommonLibs/trunk
sqlite3 http://wush.net/svn/range/software/public/sqlite3/trunk

View File

@ -4,6 +4,7 @@
#include <fstream>
#include "../SubscriberRegistry.h"
#include "Configuration.h"
#include "Utils.h"
using namespace std;
@ -14,15 +15,15 @@ SubscriberRegistry sr;
void foo(uint32_t n, string s)
{
LOG(INFO) << sr.uintToString(n) << " " << s;
LOG(INFO) << Utils::uintToString(n) << " " << s;
}
void foo(string s)
{
uint64_t h;
uint64_t l;
sr.stringToUint(s, &h, &l);
LOG(INFO) << sr.uintToString(h, l) << " " << s;
Utils::stringToUint(s, &h, &l);
LOG(INFO) << Utils::uintToString(h, l) << " " << s;
}
int main(int argc, char **argv)
@ -39,8 +40,8 @@ int main(int argc, char **argv)
sr.getIMSI("clid");
// test mapping of unknow user (so it won't be found locally)
sr.getCLIDLocal("imsi_unknown");
sr.getRandForAuthentication(false, "imsi_r1");
sr.authenticate(false, "imsi_a1","rand_a1","sres_a1");
//sr.getRandForAuthentication(false, "imsi_r1");
//sr.authenticate(false, "imsi_a1","rand_a1","sres_a1");
// but test the conversions

View File

@ -2,7 +2,5 @@ BEGIN TRANSACTION;
CREATE TABLE CONFIG ( KEYSTRING TEXT UNIQUE NOT NULL, VALUESTRING TEXT, STATIC INTEGER DEFAULT 0, OPTIONAL INTEGER DEFAULT 0, COMMENTS TEXT DEFAULT '');
INSERT INTO "CONFIG" VALUES('Log.Level','DEBUG',0,0,'');
INSERT INTO "CONFIG" VALUES('SubscriberRegistry.db','sr.db',0,0,'');
INSERT INTO "CONFIG" VALUES('SubscriberRegistry.HTTP.Server','',0,0,'');
INSERT INTO "CONFIG" VALUES('SIP.Proxy.Registration','testing',0,0,'');
INSERT INTO "CONFIG" VALUES('SubscriberRegistry.UpstreamServer','',0,0,'');
COMMIT;

View File

@ -2,7 +2,5 @@ BEGIN TRANSACTION;
CREATE TABLE CONFIG ( KEYSTRING TEXT UNIQUE NOT NULL, VALUESTRING TEXT, STATIC INTEGER DEFAULT 0, OPTIONAL INTEGER DEFAULT 0, COMMENTS TEXT DEFAULT '');
INSERT INTO "CONFIG" VALUES('Log.Level','DEBUG',0,0,'');
INSERT INTO "CONFIG" VALUES('SubscriberRegistry.db','sr.db',0,0,'');
INSERT INTO "CONFIG" VALUES('SubscriberRegistry.HTTP.Server','testing',0,0,'');
INSERT INTO "CONFIG" VALUES('SIP.Proxy.Registration','testing',0,0,'');
INSERT INTO "CONFIG" VALUES('SubscriberRegistry.UpstreamServer','',0,0,'');
COMMIT;

View File

@ -2,7 +2,6 @@ BEGIN TRANSACTION;
CREATE TABLE CONFIG ( KEYSTRING TEXT UNIQUE NOT NULL, VALUESTRING TEXT, STATIC INTEGER DEFAULT 0, OPTIONAL INTEGER DEFAULT 0, COMMENTS TEXT DEFAULT '');
INSERT INTO "CONFIG" VALUES('SubscriberRegistry.db','sr.db',0,0,'The location of the sqlite3 database holding the subscriber registry.');
INSERT INTO "CONFIG" VALUES('SubscriberRegistry.A3A8','../comp128',0,0,'');
INSERT INTO "CONFIG" VALUES('SubscriberRegistry.UpstreamServer','',0,0,'');
INSERT INTO "CONFIG" VALUES('Log.Level','INFO',0,0,'');
INSERT INTO "CONFIG" VALUES('Log.Level.smcommands.cpp','INFO',0,0,'');
INSERT INTO "CONFIG" VALUES('savefile','savedqueue.txt',0,0,'');

File diff suppressed because it is too large Load Diff

View File

@ -1,2 +0,0 @@
select name, username, type, context, host from sip_buddies;
select exten, dial from dialdata_table;

View File

@ -1,90 +0,0 @@
#!/bin/bash
# start with a known state in DBs
rm -f /etc/OpenBTS/OpenBTS.db test.db
# initialize the config db
sqlite3 /etc/OpenBTS/OpenBTS.db < srmanager.db.init
chmod 666 /etc/OpenBTS/OpenBTS.db
# initialize the SR db
sqlite3 test.db < test.db.init
# initialize the output file
date > output.got
# Each test below enters a mark in the syslog so we can extract only
# the log entries for that test.
# Then it sets the environment variables (and stdin for POST) to duplicate
# http state, then it runs the test.
# Then it extracts the test's log entries.
# On some it then dumps the SR database.
# empty http get
logger -plocal7.Debug dwb syslog mark
REQUEST_METHOD=GET;
export REQUEST_METHOD
QUERY_STRING=
export QUERY_STRING
../srmanager.cgi >> output.got
../syslogextractor >> output.got
# http get for main page
logger -plocal7.Debug dwb syslog mark
REQUEST_METHOD=GET;
export REQUEST_METHOD
QUERY_STRING=what=Main
export QUERY_STRING
../srmanager.cgi >> output.got
../syslogextractor >> output.got
# empty http post
logger -plocal7.Debug dwb syslog mark
REQUEST_METHOD=POST;
export REQUEST_METHOD
CONTENT_LENGTH=0
export CONTENT_LENGTH
echo '' | ../srmanager.cgi >> output.got
../syslogextractor >> output.got
# http post for main page
logger -plocal7.Debug dwb syslog mark
REQUEST_METHOD=POST;
export REQUEST_METHOD
CONTENT_LENGTH=9
export CONTENT_LENGTH
echo 'what=Main' | ../srmanager.cgi >> output.got
../syslogextractor >> output.got
# http post for add
logger -plocal7.Debug dwb syslog mark
REQUEST_METHOD=POST;
export REQUEST_METHOD
CONTENT_LENGTH=55
export CONTENT_LENGTH
echo 'name=a&username=b&type=c&context=d&host=e&id=0&what=Add' | ../srmanager.cgi >> output.got
../syslogextractor >> output.got
sqlite3 test.db < ./query.sql >> output.got
# http post for delete
logger -plocal7.Debug dwb syslog mark
REQUEST_METHOD=POST;
export REQUEST_METHOD
CONTENT_LENGTH=58
export CONTENT_LENGTH
echo 'name=a&username=b&type=c&context=d&host=e&id=9&what=Delete' | ../srmanager.cgi >> output.got
../syslogextractor >> output.got
sqlite3 test.db < ./query.sql >> output.got
# http post for update
logger -plocal7.Debug dwb syslog mark
REQUEST_METHOD=POST;
export REQUEST_METHOD
CONTENT_LENGTH=35
export CONTENT_LENGTH
echo 'exten=qrs&dial=tuv&id=1&what=Update' | ../srmanager.cgi >> output.got
../syslogextractor >> output.got
sqlite3 test.db < ./query.sql >> output.got
# normalize the output file for diffing
mv output.got ootput.got
../hexmapper ootput.got > output.got
diff output.exp output.got
exit 0

View File

@ -1,9 +0,0 @@
BEGIN TRANSACTION;
CREATE TABLE CONFIG ( KEYSTRING TEXT UNIQUE NOT NULL, VALUESTRING TEXT, STATIC INTEGER DEFAULT 0, OPTIONAL INTEGER DEFAULT 0, COMMENTS TEXT DEFAULT '');
INSERT INTO "CONFIG" VALUES('Log.Level','DEBUG',0,0,'');
INSERT INTO "CONFIG" VALUES('SubscriberRegistry.db','test.db',0,0,'');
INSERT INTO "CONFIG" VALUES('SubscriberRegistry.Manager.VisibleColumns','name username type context host',0,0,'');
INSERT INTO "CONFIG" VALUES('SubscriberRegistry.Manager.Url','http://localhost/~doug/foo.cgi',0,0,'');
INSERT INTO "CONFIG" VALUES('SubscriberRegistry.Manager.Title','Some Title',0,0,'');
COMMIT;

View File

@ -1,102 +0,0 @@
BEGIN TRANSACTION;
CREATE TABLE dialdata_table (
id INTEGER,
exten VARCHAR(40) NOT NULL DEFAULT '',
dial VARCHAR(128) NOT NULL DEFAULT '',
PRIMARY KEY (id)
);
INSERT INTO "dialdata_table" VALUES(1,'1000','1000');
INSERT INTO "dialdata_table" VALUES(2,'1001','1001');
CREATE TABLE 'sip_buddies'
(
id integer,
name VARCHAR(80) unique not null,
context VARCHAR(80),
callingpres VARCHAR(30) DEFAULT 'allowed_not_screened',
deny VARCHAR(95),
permit VARCHAR(95),
secret VARCHAR(80),
md5secret VARCHAR(80),
remotesecret VARCHAR(250),
transport VARCHAR(10),
host VARCHAR(31) DEFAULT '' not null,
nat VARCHAR(5) DEFAULT 'no' not null,
type VARCHAR(10) DEFAULT 'friend' not null,
accountcode VARCHAR(20),
amaflags VARCHAR(13),
callgroup VARCHAR(10),
callerid VARCHAR(80),
defaultip VARCHAR(40) DEFAULT '0.0.0.0',
dtmfmode VARCHAR(7) DEFAULT 'rfc2833',
fromuser VARCHAR(80),
fromdomain VARCHAR(80),
insecure VARCHAR(4),
language CHAR(2),
mailbox VARCHAR(50),
pickupgroup VARCHAR(10),
qualify CHAR(3),
regexten VARCHAR(80),
rtptimeout CHAR(3),
rtpholdtimeout CHAR(3),
setvar VARCHAR(100),
disallow VARCHAR(100) DEFAULT 'all',
allow VARCHAR(100) DEFAULT 'ulaw' not null,
fullcontact VARCHAR(80),
ipaddr VARCHAR(40),
port int(5) DEFAULT 0,
username VARCHAR(80),
defaultuser VARCHAR(80),
subscribecontext VARCHAR(80),
directmedia VARCHAR(3),
trustrpid VARCHAR(3),
sendrpid VARCHAR(3),
progressinband VARCHAR(5),
promiscredir VARCHAR(3),
useclientcode VARCHAR(3),
callcounter VARCHAR(3),
busylevel int(11),
allowoverlap VARCHAR(3) DEFAULT 'yes',
allowsubscribe VARCHAR(3) DEFAULT 'yes',
allowtransfer VARCHAR(3) DEFAULT 'yes',
ignoresdpversion VARCHAR(3) DEFAULT 'no',
template VARCHAR(100),
videosupport VARCHAR(6) DEFAULT 'no',
maxcallbitrate int(11),
rfc2833compensate VARCHAR(3) DEFAULT 'yes',
'session-timers' VARCHAR(10) DEFAULT 'accept',
'session-expires' int(6) DEFAULT 1800,
'session-minse' int(6) DEFAULT 90,
'session-refresher' VARCHAR(3) DEFAULT 'uas',
t38pt_usertpsource VARCHAR(3),
outboundproxy VARCHAR(250),
callbackextension VARCHAR(250),
registertrying VARCHAR(3) DEFAULT 'yes',
timert1 int(6) DEFAULT 500,
timerb int(9),
qualifyfreq int(6) DEFAULT 120,
contactpermit VARCHAR(250),
contactdeny VARCHAR(250),
lastms int(11) DEFAULT 0 not null,
regserver VARCHAR(100),
regseconds int(11) DEFAULT 0 not null,
useragent VARCHAR(100),
cancallforward CHAR(3) DEFAULT 'yes' not null,
canreinvite CHAR(3) DEFAULT 'yes' not null,
mask VARCHAR(95),
musiconhold VARCHAR(100),
restrictcid CHAR(3),
calllimit int(5),
ki VARCHAR(32) DEFAULT '' not null,
rand VARCHAR(32) DEFAULT '' not null,
sres VARCHAR(32) DEFAULT '' not null,
primary key(id)
);
INSERT INTO "sip_buddies" VALUES(1,'1000','phones','allowed_not_screened',NULL,NULL,NULL,NULL,NULL,NULL,'dynamic','no','friend',NULL,NULL,NULL,NULL,'0.0.0.0','rfc2833',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'all','ulaw',NULL,NULL,0,'1000',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'yes','yes','yes','no',NULL,'no',NULL,'yes','accept',1800,90,'uas',NULL,NULL,NULL,'yes',500,NULL,120,NULL,NULL,0,NULL,0,NULL,'yes','yes',NULL,NULL,NULL,NULL,'','','');
INSERT INTO "sip_buddies" VALUES(2,'1001','phones','allowed_not_screened',NULL,NULL,NULL,NULL,NULL,NULL,'dynamic','no','friend',NULL,NULL,NULL,NULL,'0.0.0.0','rfc2833',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'all','ulaw',NULL,NULL,0,'1001',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'yes','yes','yes','no',NULL,'no',NULL,'yes','accept',1800,90,'uas',NULL,NULL,NULL,'yes',500,NULL,120,NULL,NULL,0,NULL,0,NULL,'yes','yes',NULL,NULL,NULL,NULL,'','','');
INSERT INTO "sip_buddies" VALUES(3,'imsi2',NULL,'allowed_not_screened',NULL,NULL,NULL,NULL,NULL,NULL,'','no','friend',NULL,NULL,NULL,NULL,'0.0.0.0','rfc2833',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'all','ulaw',NULL,'imsi2ipaddr',0,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'yes','yes','yes','no',NULL,'no',NULL,'yes','accept',1800,90,'uas',NULL,NULL,NULL,'yes',500,NULL,120,NULL,NULL,0,NULL,0,NULL,'yes','yes',NULL,NULL,NULL,NULL,'','','');
INSERT INTO "sip_buddies" VALUES(4,'imsi_r1',NULL,'allowed_not_screened',NULL,NULL,NULL,NULL,NULL,NULL,'','no','friend',NULL,NULL,NULL,NULL,'0.0.0.0','rfc2833',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'all','ulaw',NULL,NULL,0,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'yes','yes','yes','no',NULL,'no',NULL,'yes','accept',1800,90,'uas',NULL,NULL,NULL,'yes',500,NULL,120,NULL,NULL,0,NULL,0,NULL,'yes','yes',NULL,NULL,NULL,NULL,'','','');
INSERT INTO "sip_buddies" VALUES(5,'imsi_r2',NULL,'allowed_not_screened',NULL,NULL,NULL,NULL,NULL,NULL,'','no','friend',NULL,NULL,NULL,NULL,'0.0.0.0','rfc2833',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'all','ulaw',NULL,NULL,0,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'yes','yes','yes','no',NULL,'no',NULL,'yes','accept',1800,90,'uas',NULL,NULL,NULL,'yes',500,NULL,120,NULL,NULL,0,NULL,0,NULL,'yes','yes',NULL,NULL,NULL,NULL,'','12345678901234567890123456789012','');
INSERT INTO "sip_buddies" VALUES(6,'imsi_a1',NULL,'allowed_not_screened',NULL,NULL,NULL,NULL,NULL,NULL,'','no','friend',NULL,NULL,NULL,NULL,'0.0.0.0','rfc2833',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'all','ulaw',NULL,NULL,0,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'yes','yes','yes','no',NULL,'no',NULL,'yes','accept',1800,90,'uas',NULL,NULL,NULL,'yes',500,NULL,120,NULL,NULL,0,NULL,0,NULL,'yes','yes',NULL,NULL,NULL,NULL,'7f4d7fbff290e20e60466bc7b5b08e4b','','');
INSERT INTO "sip_buddies" VALUES(7,'imsi_a2',NULL,'allowed_not_screened',NULL,NULL,NULL,NULL,NULL,NULL,'','no','friend',NULL,NULL,NULL,NULL,'0.0.0.0','rfc2833',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'all','ulaw',NULL,NULL,0,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'yes','yes','yes','no',NULL,'no',NULL,'yes','accept',1800,90,'uas',NULL,NULL,NULL,'yes',500,NULL,120,NULL,NULL,0,NULL,0,NULL,'yes','yes',NULL,NULL,NULL,NULL,'','123','456');
INSERT INTO "sip_buddies" VALUES(8,'imsi_a3',NULL,'allowed_not_screened',NULL,NULL,NULL,NULL,NULL,NULL,'','no','friend',NULL,NULL,NULL,NULL,'0.0.0.0','rfc2833',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'all','ulaw',NULL,NULL,0,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'yes','yes','yes','no',NULL,'no',NULL,'yes','accept',1800,90,'uas',NULL,NULL,NULL,'yes',500,NULL,120,NULL,NULL,0,NULL,0,NULL,'yes','yes',NULL,NULL,NULL,NULL,'','123456789012345678901234567890a3','1234567890123456789012a3');
COMMIT;

View File

@ -1,133 +0,0 @@
Fri Mar 18 16:47:36 PDT 2011
Content-Type: text
servershare.cpp:-0-:decodeQuery: REQUEST_METHOD = GET
servershare.cpp:-0-:decodeQuery: QUERY_STRING = req=sql&stmts=insert into sip_buddies (name) values ("newname")
subscriberserver.cpp:-0-:logQuery: query
subscriberserver.cpp:-0-:logQuery: req -> sql
subscriberserver.cpp:-0-:logQuery: stmts -> insert into sip_buddies (name) values ("newname")
subscriberserver.cpp:-0-:logResponse: response
IMSI12345678901231|hexhexhexhexhexhexhexhexhexh1000|||
IMSI12345678901232||||
IMSI12345678901233|||hexhexhexhexhexhexhexhexhexh1000|
newname||||
Content-Type: text
res=IMSI12345678901232
servershare.cpp:-0-:decodeQuery: REQUEST_METHOD = GET
servershare.cpp:-0-:decodeQuery: QUERY_STRING = req=sql&stmts=select name from sip_buddies where id=2
subscriberserver.cpp:-0-:logQuery: query
subscriberserver.cpp:-0-:logQuery: req -> sql
subscriberserver.cpp:-0-:logQuery: stmts -> select name from sip_buddies where id=2
subscriberserver.cpp:-0-:logResponse: response
subscriberserver.cpp:-0-:logResponse: res=IMSI12345678901232
IMSI12345678901231|hexhexhexhexhexhexhexhexhexh1000|||
IMSI12345678901232||||
IMSI12345678901233|||hexhexhexhexhexhexhexhexhexh1000|
newname||||
Content-Type: text
rand=hexhexhexhexhexhexhexhexhexh1000
imsi=IMSI12345678901231
servershare.cpp:-0-:decodeQuery: REQUEST_METHOD = GET
servershare.cpp:-0-:decodeQuery: QUERY_STRING = req=rand&imsi=IMSI12345678901231
subscriberserver.cpp:-0-:logQuery: query
subscriberserver.cpp:-0-:logQuery: imsi -> IMSI12345678901231
subscriberserver.cpp:-0-:logQuery: req -> rand
servershare.cpp:-0-:generateRand: ki is unknown, rand is cached
subscriberserver.cpp:-0-:logResponse: response
subscriberserver.cpp:-0-:logResponse: rand=hexhexhexhexhexhexhexhexhexh1000
subscriberserver.cpp:-0-:logResponse: imsi=IMSI12345678901231
IMSI12345678901231|hexhexhexhexhexhexhexhexhexh1000|||
IMSI12345678901232||||
IMSI12345678901233|||hexhexhexhexhexhexhexhexhexh1000|
newname||||
Content-Type: text
rand=hexhexhexhexhexhexhexhexhexh1000
imsi=IMSI12345678901231
servershare.cpp:-0-:decodeQuery: REQUEST_METHOD = GET
servershare.cpp:-0-:decodeQuery: QUERY_STRING = req=rand&imsi=IMSI12345678901231
subscriberserver.cpp:-0-:logQuery: query
subscriberserver.cpp:-0-:logQuery: imsi -> IMSI12345678901231
subscriberserver.cpp:-0-:logQuery: req -> rand
servershare.cpp:-0-:generateRand: ki is unknown, rand is cached
subscriberserver.cpp:-0-:logResponse: response
subscriberserver.cpp:-0-:logResponse: rand=hexhexhexhexhexhexhexhexhexh1000
subscriberserver.cpp:-0-:logResponse: imsi=IMSI12345678901231
IMSI12345678901231|hexhexhexhexhexhexhexhexhexh1000|||
IMSI12345678901232||||
IMSI12345678901233|||hexhexhexhexhexhexhexhexhexh1000|
newname||||
Content-Type: text
rand=hexhexhexhexhexhexhexhexhexh1001
imsi=IMSI12345678901232
servershare.cpp:-0-:decodeQuery: REQUEST_METHOD = GET
servershare.cpp:-0-:decodeQuery: QUERY_STRING = req=rand&imsi=IMSI12345678901232
subscriberserver.cpp:-0-:logQuery: query
subscriberserver.cpp:-0-:logQuery: imsi -> IMSI12345678901232
subscriberserver.cpp:-0-:logQuery: req -> rand
servershare.cpp:-0-:generateRand: ki is unknown, rand is not cached
subscriberserver.cpp:-0-:logResponse: response
subscriberserver.cpp:-0-:logResponse: rand=hexhexhexhexhexhexhexhexhexh1001
subscriberserver.cpp:-0-:logResponse: imsi=IMSI12345678901232
IMSI12345678901231|hexhexhexhexhexhexhexhexhexh1000|||
IMSI12345678901232|hexhexhexhexhexhexhexhexhexh1001|||
IMSI12345678901233|||hexhexhexhexhexhexhexhexhexh1000|
newname||||
Content-Type: text
rand=hexhexhexhexhexhexhexhexhexh1001
imsi=IMSI12345678901232
servershare.cpp:-0-:decodeQuery: REQUEST_METHOD = GET
servershare.cpp:-0-:decodeQuery: QUERY_STRING = req=rand&imsi=IMSI12345678901232
subscriberserver.cpp:-0-:logQuery: query
subscriberserver.cpp:-0-:logQuery: imsi -> IMSI12345678901232
subscriberserver.cpp:-0-:logQuery: req -> rand
servershare.cpp:-0-:generateRand: ki is unknown, rand is cached
subscriberserver.cpp:-0-:logResponse: response
subscriberserver.cpp:-0-:logResponse: rand=hexhexhexhexhexhexhexhexhexh1001
subscriberserver.cpp:-0-:logResponse: imsi=IMSI12345678901232
IMSI12345678901231|hexhexhexhexhexhexhexhexhexh1000|||
IMSI12345678901232|hexhexhexhexhexhexhexhexhexh1001|||
IMSI12345678901233|||hexhexhexhexhexhexhexhexhexh1000|
newname||||
Content-Type: text
status=SUCCESS
servershare.cpp:-0-:decodeQuery: REQUEST_METHOD = GET
servershare.cpp:-0-:decodeQuery: QUERY_STRING = req=auth&imsi=IMSI12345678901233&rand=hexhexhexhexhexhexhexhexhexh1002&sres=hexh1003
subscriberserver.cpp:-0-:logQuery: query
subscriberserver.cpp:-0-:logQuery: imsi -> IMSI12345678901233
subscriberserver.cpp:-0-:logQuery: rand -> hexhexhexhexhexhexhexhexhexh1002
subscriberserver.cpp:-0-:logQuery: req -> auth
subscriberserver.cpp:-0-:logQuery: sres -> hexh1003
servershare.cpp:-0-:authenticate: ki known
servershare.cpp:-0-:authenticate: result = hexh1003
servershare.cpp:-0-:authenticate: returning = 1
subscriberserver.cpp:-0-:logResponse: response
subscriberserver.cpp:-0-:logResponse: status=SUCCESS
IMSI12345678901231|hexhexhexhexhexhexhexhexhexh1000|||
IMSI12345678901232|hexhexhexhexhexhexhexhexhexh1001|||
IMSI12345678901233|||hexhexhexhexhexhexhexhexhexh1000|
newname||||
Content-Type: text
status=FAILURE
servershare.cpp:-0-:decodeQuery: REQUEST_METHOD = GET
servershare.cpp:-0-:decodeQuery: QUERY_STRING = req=auth&imsi=IMSI12345678901233&rand=hexhexhexhexhexhexhexhexhexh1002&sres=hexh1004
subscriberserver.cpp:-0-:logQuery: query
subscriberserver.cpp:-0-:logQuery: imsi -> IMSI12345678901233
subscriberserver.cpp:-0-:logQuery: rand -> hexhexhexhexhexhexhexhexhexh1002
subscriberserver.cpp:-0-:logQuery: req -> auth
subscriberserver.cpp:-0-:logQuery: sres -> hexh1004
servershare.cpp:-0-:authenticate: ki known
servershare.cpp:-0-:authenticate: result = hexh1003
servershare.cpp:-0-:authenticate: returning = 0
subscriberserver.cpp:-0-:logResponse: response
subscriberserver.cpp:-0-:logResponse: status=FAILURE
IMSI12345678901231|hexhexhexhexhexhexhexhexhexh1000|||
IMSI12345678901232|hexhexhexhexhexhexhexhexhexh1001|||
IMSI12345678901233|||hexhexhexhexhexhexhexhexhexh1000|
newname||||

View File

@ -1 +0,0 @@
select name, rand, sres, ki, kc from sip_buddies;

View File

@ -1,89 +0,0 @@
#!/bin/bash
rm -f /etc/OpenBTS/OpenBTS.db sr.db
sqlite3 /etc/OpenBTS/OpenBTS.db < subscriberserver.db.init
sqlite3 sr.db < sr.db.init
date > output.got
# test an sql update
logger -plocal7.Debug dwb syslog mark
REQUEST_METHOD=GET
export REQUEST_METHOD
QUERY_STRING="req=sql&stmts=insert into sip_buddies (name) values (\"newname\")"
export QUERY_STRING
../subscriberserver.cgi >> output.got
../syslogextractor >> output.got
sqlite3 sr.db < query.sql >> output.got
# test an sql query
logger -plocal7.Debug dwb syslog mark
REQUEST_METHOD=GET
export REQUEST_METHOD
QUERY_STRING="req=sql&stmts=select name from sip_buddies where id=2"
export QUERY_STRING
../subscriberserver.cgi >> output.got
../syslogextractor >> output.got
sqlite3 sr.db < query.sql >> output.got
# Some rand and auth tests just to see that things are connected ok.
# No need for exhaustive testing because the code is shared, and
# tested better elsewhere.
logger -plocal7.Debug dwb syslog mark
REQUEST_METHOD=GET
export REQUEST_METHOD
QUERY_STRING="req=rand&imsi=IMSI12345678901231"
export QUERY_STRING
../subscriberserver.cgi >> output.got
../syslogextractor >> output.got
sqlite3 sr.db < query.sql >> output.got
logger -plocal7.Debug dwb syslog mark
REQUEST_METHOD=GET
export REQUEST_METHOD
QUERY_STRING="req=rand&imsi=IMSI12345678901231"
export QUERY_STRING
../subscriberserver.cgi >> output.got
../syslogextractor >> output.got
sqlite3 sr.db < query.sql >> output.got
logger -plocal7.Debug dwb syslog mark
REQUEST_METHOD=GET
export REQUEST_METHOD
QUERY_STRING="req=rand&imsi=IMSI12345678901232"
export QUERY_STRING
../subscriberserver.cgi >> output.got
../syslogextractor >> output.got
sqlite3 sr.db < query.sql >> output.got
logger -plocal7.Debug dwb syslog mark
REQUEST_METHOD=GET
export REQUEST_METHOD
QUERY_STRING="req=rand&imsi=IMSI12345678901232"
export QUERY_STRING
../subscriberserver.cgi >> output.got
../syslogextractor >> output.got
sqlite3 sr.db < query.sql >> output.got
logger -plocal7.Debug dwb syslog mark
REQUEST_METHOD=GET
export REQUEST_METHOD
QUERY_STRING="req=auth&imsi=IMSI12345678901233&rand=d76fe9a5839089a2ebeb269b46bf46ff&sres=726259AF"
export QUERY_STRING
../subscriberserver.cgi >> output.got
../syslogextractor >> output.got
sqlite3 sr.db < query.sql >> output.got
logger -plocal7.Debug dwb syslog mark
REQUEST_METHOD=GET
export REQUEST_METHOD
QUERY_STRING="req=auth&imsi=IMSI12345678901233&rand=d76fe9a5839089a2ebeb269b46bf46ff&sres=726259A0"
export QUERY_STRING
../subscriberserver.cgi >> output.got
../syslogextractor >> output.got
sqlite3 sr.db < query.sql >> output.got
mv output.got ootput.got
../hexmapper ootput.got > output.got
diff output.exp output.got
exit 0

View File

@ -1,101 +0,0 @@
BEGIN TRANSACTION;
CREATE TABLE dialdata_table (
id INTEGER,
exten VARCHAR(40) NOT NULL DEFAULT '',
dial VARCHAR(128) NOT NULL DEFAULT '',
PRIMARY KEY (id)
);
INSERT INTO "dialdata_table" VALUES(1,'1000','1000');
INSERT INTO "dialdata_table" VALUES(2,'1001','1001');
INSERT INTO "dialdata_table" VALUES(3,'clid1','imsi1');
CREATE TABLE 'sip_buddies'
(
id integer,
name VARCHAR(80) unique not null,
context VARCHAR(80),
callingpres VARCHAR(30) DEFAULT 'allowed_not_screened',
deny VARCHAR(95),
permit VARCHAR(95),
secret VARCHAR(80),
md5secret VARCHAR(80),
remotesecret VARCHAR(250),
transport VARCHAR(10),
host VARCHAR(31) DEFAULT '' not null,
nat VARCHAR(5) DEFAULT 'no' not null,
type VARCHAR(10) DEFAULT 'friend' not null,
accountcode VARCHAR(20),
amaflags VARCHAR(13),
callgroup VARCHAR(10),
callerid VARCHAR(80),
defaultip VARCHAR(40) DEFAULT '0.0.0.0',
dtmfmode VARCHAR(7) DEFAULT 'rfc2833',
fromuser VARCHAR(80),
fromdomain VARCHAR(80),
insecure VARCHAR(4),
language CHAR(2),
mailbox VARCHAR(50),
pickupgroup VARCHAR(10),
qualify CHAR(3),
regexten VARCHAR(80),
rtptimeout CHAR(3),
rtpholdtimeout CHAR(3),
setvar VARCHAR(100),
disallow VARCHAR(100) DEFAULT 'all',
allow VARCHAR(100) DEFAULT 'ulaw' not null,
fullcontact VARCHAR(80),
ipaddr VARCHAR(40),
port int(5) DEFAULT 0,
username VARCHAR(80),
defaultuser VARCHAR(80),
subscribecontext VARCHAR(80),
directmedia VARCHAR(3),
trustrpid VARCHAR(3),
sendrpid VARCHAR(3),
progressinband VARCHAR(5),
promiscredir VARCHAR(3),
useclientcode VARCHAR(3),
callcounter VARCHAR(3),
busylevel int(11),
allowoverlap VARCHAR(3) DEFAULT 'yes',
allowsubscribe VARCHAR(3) DEFAULT 'yes',
allowtransfer VARCHAR(3) DEFAULT 'yes',
ignoresdpversion VARCHAR(3) DEFAULT 'no',
template VARCHAR(100),
videosupport VARCHAR(6) DEFAULT 'no',
maxcallbitrate int(11),
rfc2833compensate VARCHAR(3) DEFAULT 'yes',
'session-timers' VARCHAR(10) DEFAULT 'accept',
'session-expires' int(6) DEFAULT 1800,
'session-minse' int(6) DEFAULT 90,
'session-refresher' VARCHAR(3) DEFAULT 'uas',
t38pt_usertpsource VARCHAR(3),
outboundproxy VARCHAR(250),
callbackextension VARCHAR(250),
registertrying VARCHAR(3) DEFAULT 'yes',
timert1 int(6) DEFAULT 500,
timerb int(9),
qualifyfreq int(6) DEFAULT 120,
contactpermit VARCHAR(250),
contactdeny VARCHAR(250),
lastms int(11) DEFAULT 0 not null,
regserver VARCHAR(100),
regseconds int(11) DEFAULT 0 not null,
useragent VARCHAR(100),
cancallforward CHAR(3) DEFAULT 'yes' not null,
canreinvite CHAR(3) DEFAULT 'yes' not null,
mask VARCHAR(95),
musiconhold VARCHAR(100),
restrictcid CHAR(3),
calllimit int(5),
rand varchar(33) default '',
sres varchar(33) default '',
ki varchar(33) default '',
kc varchar(33) default '',
primary key(id)
);
insert into sip_buddies (name,rand,sres,ki,kc) values ('IMSI12345678901231','48091124c1155bd95c6f1860dc66ce92','','','');
insert into sip_buddies (name,rand,sres,ki,kc) values ('IMSI12345678901232','','','','');
insert into sip_buddies (name,rand,sres,ki,kc) values ('IMSI12345678901233','','','48091124c1155bd95c6f1860dc66ce92','');
COMMIT;

View File

@ -1,7 +0,0 @@
BEGIN TRANSACTION;
CREATE TABLE CONFIG ( KEYSTRING TEXT UNIQUE NOT NULL, VALUESTRING TEXT, STATIC INTEGER DEFAULT 0, OPTIONAL INTEGER DEFAULT 0, COMMENTS TEXT DEFAULT '');
INSERT INTO "CONFIG" VALUES('SubscriberRegistry.db','sr.db',0,0,'');
INSERT INTO "CONFIG" VALUES('SubscriberRegistry.A3A8','../comp128',0,0,'');
INSERT INTO "CONFIG" VALUES('SubscriberRegistry.UpstreamServer','',0,0,'');
INSERT INTO "CONFIG" VALUES('Log.Level','DEBUG',0,0,'');
COMMIT;