Files
resiprocate/repro/AbstractDb.cxx
2022-06-01 12:22:35 +02:00

1025 lines
23 KiB
C++

#include "rutil/ResipAssert.h"
#include "rutil/Data.hxx"
#include "rutil/DataStream.hxx"
#include "rutil/ParseBuffer.hxx"
#include "resip/stack/Symbols.hxx"
#include "rutil/Logger.hxx"
#include "repro/UserStore.hxx"
#include "repro/AbstractDb.hxx"
using namespace resip;
using namespace repro;
using namespace std;
#define RESIPROCATE_SUBSYSTEM Subsystem::REPRO
#define REPRO_DB_VERSION_USERS 3
static void
encodeString(oDataStream& s, const Data& data)
{
short len = (short)data.size();
s.write( (char*)(&len) , sizeof( len ) );
s.write( data.data(), len );
}
static void
decodeString(iDataStream& s, Data& data)
{
data.clear();
if(s.eof()) return;
short len;
s.read((char*)(&len), sizeof(len));
if(s.eof()) return;
// [TODO] This is probably OK for now, but we can do better than this.
if (len > 8192)
{
ErrLog( << "Tried to decode a database record that was much larger (>8k) than expected. Returning an empty Data instead." );
return;
}
s.read(data.getBuf(len), len);
}
AbstractDb::AbstractDb()
{
}
AbstractDb::~AbstractDb()
{
}
Data
AbstractDb::dbFirstKey(const AbstractDb::Table table)
{
return dbNextKey(table,true/*first*/);
}
bool
AbstractDb::dbFirstRecord(const AbstractDb::Table table,
const Data& key,
Data& data,
bool forUpdate)
{
return dbNextRecord(table, key, data, forUpdate, true/*first*/);
}
// Callback used by BerkeleyDb for secondary table support. Returns key to use in
// secondary database table
// secondaryKey should point to persistent data (ie. typically something from data itself)
int
AbstractDb::getSecondaryKey(const Table table,
const Key& key,
const Data& data,
void** secondaryKey,
unsigned int* secondaryKeyLen)
{
if(table == SiloTable)
{
// Secondary Key for Silo table is DestUri
Data nonConstData(Data::Share, data.data(), data.size());
iDataStream s(nonConstData);
short version;
resip_assert(sizeof(version) == 2);
s.read((char*)(&version), sizeof(version));
resip_assert(version == 1);
if (version == 1)
{
// DestUri is first element after version
short len;
s.read( (char*)(&len), sizeof(len));
*secondaryKeyLen = (unsigned int)len;
*secondaryKey = (void*)(nonConstData.data() + (sizeof(version)+sizeof(len)));
return 0;
}
}
return -1;
}
void
AbstractDb::encodeUser(const UserRecord& rec, resip::Data& data)
{
oDataStream s(data);
short version = REPRO_DB_VERSION_USERS;
resip_assert( sizeof( version) == 2 );
s.write( (char*)(&version) , sizeof(version) );
encodeString( s, rec.user );
encodeString( s, rec.domain);
encodeString( s, rec.realm);
encodeString( s, rec.passwordHash);
encodeString( s, rec.passwordHashAlt);
encodeString( s, rec.name);
encodeString( s, rec.email);
encodeString( s, rec.forwardAddress);
s.flush();
}
bool
AbstractDb::addUser( const AbstractDb::Key& key, const AbstractDb::UserRecord& rec )
{
resip_assert( !key.empty() );
Data data;
encodeUser(rec, data);
return dbWriteRecord(UserTable,key,data);
}
void
AbstractDb::eraseUser( const AbstractDb::Key& key )
{
dbEraseRecord( UserTable, key);
}
AbstractDb::UserRecord
AbstractDb::getUser( const AbstractDb::Key& key ) const
{
AbstractDb::UserRecord rec;
Data data;
bool stat = dbReadRecord( UserTable, key, data );
if ( !stat )
{
return rec;
}
if ( data.empty() )
{
return rec;
}
iDataStream s(data);
short version;
resip_assert( sizeof(version) == 2 );
s.read( (char*)(&version), sizeof(version) );
if ( version == REPRO_DB_VERSION_USERS )
{
decodeString(s, rec.user);
decodeString(s, rec.domain);
decodeString(s, rec.realm);
decodeString(s, rec.passwordHash);
decodeString(s, rec.passwordHashAlt);
decodeString(s, rec.name);
decodeString(s, rec.email);
decodeString(s, rec.forwardAddress);
}
else if ( version == 2 )
{
// We can read from the older version DB, but the entry
// will be written back in the new format with
// passwordHashAlt blank
decodeString(s, rec.user);
decodeString(s, rec.domain);
decodeString(s, rec.realm);
decodeString(s, rec.passwordHash);
decodeString(s, rec.name);
decodeString(s, rec.email);
decodeString(s, rec.forwardAddress);
rec.passwordHashAlt = Data::Empty;
}
else
{
// unknown version
ErrLog( <<"Data in user database with unknown version " << version );
ErrLog( <<"record size is " << data.size() );
}
return rec;
}
Data
AbstractDb::getUserAuthInfo( const AbstractDb::Key& key ) const
{
AbstractDb::UserRecord rec = getUser(key);
return rec.passwordHash;
}
AbstractDb::Key
AbstractDb::firstUserKey()
{
return dbFirstKey(UserTable);
}
AbstractDb::Key
AbstractDb::nextUserKey()
{
return dbNextKey(UserTable);
}
bool
AbstractDb::addTlsPeerIdentity( const AbstractDb::Key& key, const AbstractDb::TlsPeerIdentityRecord& rec )
{
resip_assert( !key.empty() );
Data data;
{
oDataStream s(data);
short version=1;
resip_assert( sizeof( version) == 2 );
s.write( (char*)(&version) , sizeof(version) );
encodeString( s, rec.peerName );
encodeString( s, rec.authorizedIdentity );
s.flush();
}
return dbWriteRecord(TlsPeerIdentityTable,key,data);
}
void
AbstractDb::eraseTlsPeerIdentity( const AbstractDb::Key& key )
{
dbEraseRecord( TlsPeerIdentityTable, key);
}
AbstractDb::TlsPeerIdentityRecord
AbstractDb::getTlsPeerIdentity( const AbstractDb::Key& key ) const
{
AbstractDb::TlsPeerIdentityRecord rec;
Data data;
bool stat = dbReadRecord( TlsPeerIdentityTable, key, data );
if ( !stat )
{
return rec;
}
if ( data.empty() )
{
return rec;
}
iDataStream s(data);
short version;
resip_assert( sizeof(version) == 2 );
s.read( (char*)(&version), sizeof(version) );
if ( version == 1 )
{
decodeString(s, rec.peerName);
decodeString(s, rec.authorizedIdentity);
}
else
{
// unknown version
ErrLog( <<"Data in TlsPeerIdentity database with unknown version " << version );
ErrLog( <<"record size is " << data.size() );
}
return rec;
}
bool
AbstractDb::isAuthorized(const std::set<resip::Data>& peerNames, const std::set<resip::Data>& identities) const
{
// FIXME
resip_assert("TlsPeerIdentity not implemented for this database backend");
return false;
}
AbstractDb::Key
AbstractDb::firstTlsPeerIdentityKey()
{
return dbFirstKey(TlsPeerIdentityTable);
}
AbstractDb::Key
AbstractDb::nextTlsPeerIdentityKey()
{
return dbNextKey(TlsPeerIdentityTable);
}
void
AbstractDb::encodeRoute(const RouteRecord& rec, resip::Data& data)
{
oDataStream s(data);
short version=1;
resip_assert( sizeof( version) == 2 );
s.write( (char*)(&version) , sizeof(version) );
encodeString( s, rec.mMethod );
encodeString( s, rec.mEvent );
encodeString( s, rec.mMatchingPattern );
encodeString( s, rec.mRewriteExpression );
s.write( (char*)(&rec.mOrder) , sizeof( rec.mOrder ) );
resip_assert( sizeof( rec.mOrder) == 2 );
//!cj! TODO - add the extra local use only flag
s.flush();
}
bool
AbstractDb::addRoute( const AbstractDb::Key& key,
const AbstractDb::RouteRecord& rec )
{
resip_assert( !key.empty() );
Data data;
encodeRoute(rec, data);
return dbWriteRecord( RouteTable, key, data );
}
void
AbstractDb::eraseRoute( const AbstractDb::Key& key )
{
dbEraseRecord (RouteTable, key);
}
AbstractDb::RouteRecord
AbstractDb::getRoute( const AbstractDb::Key& key) const
{
AbstractDb::RouteRecord rec;
Data data;
bool stat = dbReadRecord( RouteTable, key, data );
if ( !stat )
{
return rec;
}
if ( data.empty() )
{
return rec;
}
iDataStream s(data);
short version;
resip_assert( sizeof(version) == 2 );
s.read( (char*)(&version), sizeof(version) );
if ( version == 1 )
{
decodeString(s, rec.mMethod);
decodeString(s, rec.mEvent);
decodeString(s, rec.mMatchingPattern);
decodeString(s, rec.mRewriteExpression);
s.read( (char*)(&rec.mOrder), sizeof(rec.mOrder) );
resip_assert( sizeof( rec.mOrder) == 2 );
}
else
{
// unknown version
ErrLog( <<"Data in route database with unknown version " << version );
ErrLog( <<"record size is " << data.size() );
}
return rec;
}
AbstractDb::RouteRecordList
AbstractDb::getAllRoutes()
{
AbstractDb::RouteRecordList ret;
AbstractDb::Key key = firstRouteKey();
while ( !key.empty() )
{
AbstractDb::RouteRecord rec = getRoute(key);
ret.push_back(rec);
key = nextRouteKey();
}
return ret;
}
AbstractDb::Key
AbstractDb::firstRouteKey()
{
return dbFirstKey(RouteTable);
}
AbstractDb::Key
AbstractDb::nextRouteKey()
{
return dbNextKey(RouteTable);
}
bool
AbstractDb::addAcl( const AbstractDb::Key& key,
const AbstractDb::AclRecord& rec )
{
resip_assert( !key.empty() );
Data data;
{
oDataStream s(data);
short version=1;
resip_assert( sizeof( version) == 2 );
s.write( (char*)(&version) , sizeof(version) );
encodeString( s, rec.mTlsPeerName );
encodeString( s, rec.mAddress );
s.write( (char*)(&rec.mMask) , sizeof( rec.mMask ) );
s.write( (char*)(&rec.mPort) , sizeof( rec.mPort ) );
s.write( (char*)(&rec.mFamily) , sizeof( rec.mFamily ) );
s.write( (char*)(&rec.mTransport) , sizeof( rec.mTransport ) );
s.flush();
}
return dbWriteRecord( AclTable, key, data );
}
void
AbstractDb::eraseAcl( const AbstractDb::Key& key )
{
dbEraseRecord (AclTable, key);
}
AbstractDb::AclRecord
AbstractDb::getAcl( const AbstractDb::Key& key) const
{
AbstractDb::AclRecord rec;
Data data;
bool stat = dbReadRecord( AclTable, key, data );
if ( !stat )
{
return rec;
}
if ( data.empty() )
{
return rec;
}
iDataStream s(data);
short version;
resip_assert( sizeof(version) == 2 );
s.read( (char*)(&version), sizeof(version) );
if ( version == 1 )
{
decodeString(s, rec.mTlsPeerName);
decodeString(s, rec.mAddress);
s.read( (char*)(&rec.mMask), sizeof(rec.mMask) );
s.read( (char*)(&rec.mPort), sizeof(rec.mPort) );
s.read( (char*)(&rec.mFamily), sizeof(rec.mFamily) );
s.read( (char*)(&rec.mTransport), sizeof(rec.mTransport) );
}
else
{
// unknown version
ErrLog( <<"Data in ACL database with unknown version " << version );
ErrLog( <<"record size is " << data.size() );
}
return rec;
}
AbstractDb::AclRecordList
AbstractDb::getAllAcls()
{
AbstractDb::AclRecordList ret;
AbstractDb::Key key = firstAclKey();
while ( !key.empty() )
{
AbstractDb::AclRecord rec = getAcl(key);
ret.push_back(rec);
key = nextAclKey();
}
return ret;
}
AbstractDb::Key
AbstractDb::firstAclKey()
{
return dbFirstKey(AclTable);
}
AbstractDb::Key
AbstractDb::nextAclKey()
{
return dbNextKey(AclTable);
}
bool
AbstractDb::addConfig( const AbstractDb::Key& key,
const AbstractDb::ConfigRecord& rec )
{
resip_assert( !key.empty() );
Data data;
{
oDataStream s(data);
short version=1;
resip_assert( sizeof( version) == 2 );
s.write( (char*)(&version) , sizeof(version) );
encodeString( s, rec.mDomain );
s.write( (char*)(&rec.mTlsPort) , sizeof( rec.mTlsPort ) );
resip_assert( sizeof(rec.mTlsPort) == 2 );
s.flush();
}
return dbWriteRecord( ConfigTable, key, data );
}
void
AbstractDb::eraseConfig( const AbstractDb::Key& key )
{
dbEraseRecord (ConfigTable, key);
}
AbstractDb::ConfigRecord
AbstractDb::getConfig( const AbstractDb::Key& key) const
{
AbstractDb::ConfigRecord rec;
Data data;
bool stat = dbReadRecord( ConfigTable, key, data );
if ( !stat )
{
return rec;
}
if ( data.empty() )
{
return rec;
}
iDataStream s(data);
short version;
resip_assert( sizeof(version) == 2 );
s.read( (char*)(&version), sizeof(version) );
if ( version == 1 )
{
decodeString(s, rec.mDomain);
s.read( (char*)(&rec.mTlsPort), sizeof(rec.mTlsPort) );
resip_assert( sizeof( rec.mTlsPort) == 2 );
}
else
{
// unknown version
ErrLog( <<"Data in Config database with unknown version " << version );
ErrLog( <<"record size is " << data.size() );
}
return rec;
}
AbstractDb::ConfigRecordList
AbstractDb::getAllConfigs()
{
AbstractDb::ConfigRecordList ret;
AbstractDb::Key key = firstConfigKey();
while ( !key.empty() )
{
AbstractDb::ConfigRecord rec = getConfig(key);
ret.push_back(rec);
key = nextConfigKey();
}
return ret;
}
AbstractDb::Key
AbstractDb::firstConfigKey()
{
return dbFirstKey(ConfigTable);
}
AbstractDb::Key
AbstractDb::nextConfigKey()
{
return dbNextKey(ConfigTable);
}
bool
AbstractDb::addStaticReg( const AbstractDb::Key& key,
const AbstractDb::StaticRegRecord& rec )
{
resip_assert( !key.empty() );
Data data;
{
oDataStream s(data);
short version=1;
resip_assert( sizeof( version) == 2 );
s.write( (char*)(&version) , sizeof(version) );
encodeString( s, rec.mAor );
encodeString( s, rec.mContact );
encodeString( s, rec.mPath );
s.flush();
}
return dbWriteRecord( StaticRegTable, key, data );
}
void
AbstractDb::eraseStaticReg( const AbstractDb::Key& key )
{
dbEraseRecord (StaticRegTable, key);
}
AbstractDb::StaticRegRecord
AbstractDb::getStaticReg( const AbstractDb::Key& key) const
{
AbstractDb::StaticRegRecord rec;
Data data;
bool stat = dbReadRecord( StaticRegTable, key, data );
if ( !stat )
{
return rec;
}
if ( data.empty() )
{
return rec;
}
iDataStream s(data);
short version;
resip_assert( sizeof(version) == 2 );
s.read( (char*)(&version), sizeof(version) );
if ( version == 1 )
{
decodeString(s, rec.mAor);
decodeString(s, rec.mContact);
decodeString(s, rec.mPath);
}
else
{
// unknown version
ErrLog( <<"Data in StaticReg database with unknown version " << version );
ErrLog( <<"record size is " << data.size() );
}
return rec;
}
AbstractDb::StaticRegRecordList
AbstractDb::getAllStaticRegs()
{
AbstractDb::StaticRegRecordList ret;
AbstractDb::Key key = firstStaticRegKey();
while ( !key.empty() )
{
AbstractDb::StaticRegRecord rec = getStaticReg(key);
ret.push_back(rec);
key = nextStaticRegKey();
}
return ret;
}
AbstractDb::Key
AbstractDb::firstStaticRegKey()
{
return dbFirstKey(StaticRegTable);
}
AbstractDb::Key
AbstractDb::nextStaticRegKey()
{
return dbNextKey(StaticRegTable);
}
void
AbstractDb::encodeFilter(const FilterRecord& rec, resip::Data& data)
{
oDataStream s(data);
short version=1;
resip_assert(sizeof( version) == 2);
s.write((char*)(&version) , sizeof(version));
encodeString(s, rec.mCondition1Header);
encodeString(s, rec.mCondition1Regex);
encodeString(s, rec.mCondition2Header);
encodeString(s, rec.mCondition2Regex);
encodeString(s, rec.mMethod);
encodeString(s, rec.mEvent);
s.write((char*)(&rec.mAction), sizeof (rec.mAction));
resip_assert(sizeof(rec.mAction) == 2);
encodeString(s, rec.mActionData);
s.write((char*)(&rec.mOrder), sizeof(rec.mOrder));
resip_assert(sizeof(rec.mOrder) == 2);
s.flush();
}
bool
AbstractDb::addFilter(const AbstractDb::Key& key,
const AbstractDb::FilterRecord& rec)
{
resip_assert( !key.empty() );
Data data;
encodeFilter(rec, data);
return dbWriteRecord(FilterTable, key, data);
}
void
AbstractDb::eraseFilter(const AbstractDb::Key& key)
{
dbEraseRecord(FilterTable, key);
}
AbstractDb::FilterRecord
AbstractDb::getFilter( const AbstractDb::Key& key) const
{
AbstractDb::FilterRecord rec;
Data data;
bool stat = dbReadRecord(FilterTable, key, data);
if (!stat)
{
return rec;
}
if (data.empty())
{
return rec;
}
iDataStream s(data);
short version;
resip_assert(sizeof(version) == 2);
s.read((char*)(&version), sizeof(version));
if (version == 1)
{
decodeString(s, rec.mCondition1Header);
decodeString(s, rec.mCondition1Regex);
decodeString(s, rec.mCondition2Header);
decodeString(s, rec.mCondition2Regex);
decodeString(s, rec.mMethod);
decodeString(s, rec.mEvent);
s.read((char*)(&rec.mAction), sizeof(rec.mAction));
resip_assert(sizeof(rec.mAction) == 2);
decodeString(s, rec.mActionData);
s.read((char*)(&rec.mOrder), sizeof(rec.mOrder));
resip_assert(sizeof(rec.mOrder) == 2);
}
else
{
// unknown version
ErrLog( <<"Data in filter database with unknown version " << version );
ErrLog( <<"record size is " << data.size() );
}
return rec;
}
AbstractDb::FilterRecordList
AbstractDb::getAllFilters()
{
AbstractDb::FilterRecordList ret;
AbstractDb::Key key = firstFilterKey();
while ( !key.empty() )
{
AbstractDb::FilterRecord rec = getFilter(key);
ret.push_back(rec);
key = nextFilterKey();
}
return ret;
}
AbstractDb::Key
AbstractDb::firstFilterKey()
{
return dbFirstKey(FilterTable);
}
AbstractDb::Key
AbstractDb::nextFilterKey()
{
return dbNextKey(FilterTable);
}
bool
AbstractDb::addToSilo(const Key& key, const SiloRecord& rec)
{
resip_assert( !key.empty() );
Data data;
{
oDataStream s(data);
short version=1;
resip_assert(sizeof( version) == 2);
s.write((char*)(&version) , sizeof(version));
encodeString(s, rec.mDestUri);
encodeString(s, rec.mSourceUri);
s.write((char*)(&rec.mOriginalSentTime), sizeof (rec.mOriginalSentTime));
resip_assert(sizeof(rec.mOriginalSentTime) == 8);
encodeString(s, rec.mTid);
encodeString(s, rec.mMimeType);
encodeString(s, rec.mMessageBody);
s.flush();
}
return dbWriteRecord(SiloTable, key, data);
}
void
AbstractDb::decodeSiloRecord(Data& data, SiloRecord& rec)
{
iDataStream s(data);
short version;
resip_assert(sizeof(version) == 2);
s.read((char*)(&version), sizeof(version));
if (version == 1)
{
decodeString(s, rec.mDestUri);
decodeString(s, rec.mSourceUri);
s.read((char*)(&rec.mOriginalSentTime), sizeof(rec.mOriginalSentTime));
resip_assert(sizeof(rec.mOriginalSentTime) == 8);
decodeString(s, rec.mTid);
decodeString(s, rec.mMimeType);
decodeString(s, rec.mMessageBody);
}
else
{
// unknown version
ErrLog( <<"Data in silo database with unknown version " << version );
ErrLog( <<"record size is " << data.size() );
}
}
bool
AbstractDb::getSiloRecords(const Key& skey, AbstractDb::SiloRecordList& recordList)
{
AbstractDb::SiloRecord rec;
Data data;
bool moreRecords = dbFirstRecord(SiloTable, skey, data, false /* forUpdate? */);
if(moreRecords)
{
// Decode and store data
decodeSiloRecord(data,rec);
recordList.push_back(rec);
while((moreRecords = dbNextRecord(SiloTable, skey, data, false /* forUpdate? */)))
{
// Decode and store data
decodeSiloRecord(data,rec);
recordList.push_back(rec);
}
}
return true;
}
void
AbstractDb::eraseSiloRecord(const Key& key)
{
dbEraseRecord(SiloTable, key);
}
void
AbstractDb::cleanupExpiredSiloRecords(uint64_t now, unsigned long expirationTime)
{
AbstractDb::Key key = dbFirstKey(SiloTable); // Iterate on primary key
// Iterate through all silo records - retrieve Original send time embedded into the
// primary key and see if the record has expired.
Data originalSendTimeData;
uint64_t originalSendTime;
while(!key.empty())
{
ParseBuffer pb(key);
const char* anchor = pb.position();
pb.skipToChar(':');
pb.data(originalSendTimeData, anchor);
originalSendTime = originalSendTimeData.convertUInt64();
if((unsigned long)(now - originalSendTime) > expirationTime)
{
eraseSiloRecord(key);
}
key = dbNextKey(SiloTable);
}
}
/* ====================================================================
* The Vovida Software License, Version 1.0
*
* Copyright (c) 2000 Vovida Networks, Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The names "VOCAL", "Vovida Open Communication Application Library",
* and "Vovida Open Communication Application Library (VOCAL)" must
* not be used to endorse or promote products derived from this
* software without prior written permission. For written
* permission, please contact vocal@vovida.org.
*
* 4. Products derived from this software may not be called "VOCAL", nor
* may "VOCAL" appear in their name, without prior written
* permission of Vovida Networks, Inc.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND
* NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL VOVIDA
* NETWORKS, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT DAMAGES
* IN EXCESS OF $1,000, NOR FOR ANY INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* ====================================================================
*/