00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021 #ifdef HAVE_CONFIG_H
00022 #include <config.h>
00023 #endif
00024
00025 #include "db.h"
00026 #include "defs.h"
00027
00028 #include <syslog.h>
00029 #include <iostream>
00030 #include <iomanip>
00031 #include "triplet.h"
00032 #include <sstream>
00033 #include <stdexcept>
00034 #include "cfg.h"
00035 #include "tmplwlmod.h"
00036 #include "wldb.h"
00037 #include "wlcacheddb.h"
00038 #include "dbdefs.h"
00039 #include "dbiquote.h"
00040
00041 #define WEAKRVHOSTDBG false
00042
00043 using namespace std;
00044
00045 typedef TmplWlMod<WLDB> DbWlModule;
00046 typedef TmplWlMod<WlCachedDB> CachedDbWlModule;
00047
00048 DB::DB() throw(std::exception)
00049 : _con(0),_graceful_quit(0)
00050 {
00051 if(dbi_initialize(0) <= 0)
00052 throw runtime_error("DB: no dbi drivers installed, or none found");
00053
00054
00055
00056
00057
00058
00059
00060
00061
00062
00063
00064
00065
00066
00067
00068
00069
00070
00071
00072
00073
00074
00075
00076
00077
00078
00079
00080
00081
00082
00083
00084
00085
00086
00087
00088
00089
00090
00091
00092
00093
00094 open();
00095
00096
00097
00098 }
00099 void DB::open() throw (std::exception)
00100 {
00101 bool initmode = g_getCfg().isModeInit();
00102 bool verbose = g_getCfg().isVerbose();
00103 if(_con)
00104 throw runtime_error("already connected to db");
00105 const char* dbdriver = g_getCfg().getDbType().c_str();
00106 _con = dbi_conn_new(dbdriver);
00107 if(!_con) {
00108 stringstream stm;
00109 stm << "DB: connection/driver " << dbdriver << " could not be opened\nAvailable drivers:\n";
00110 list_drivers(stm);
00111 throw runtime_error(stm.str());
00112 } else {
00113 if(verbose)
00114 syslog(LOG_DEBUG,"connecting to DB, using driver %s",dbdriver);
00115 }
00116 const ParamVector& params = g_getCfg().getDbParamVector();
00117 for(ParamVector::const_iterator itr=params.begin(); itr != params.end();++itr) {
00118
00119
00120 string strKey = (*itr);
00121 ++itr;
00122 if(itr != params.end()) {
00123 if(verbose) {
00124 string strLog = (strKey=="password") ? "setting DB option: "+strKey+" to: (hidden)"
00125 : "setting DB option: "+strKey+" to: "+(*itr);
00126 syslog(LOG_DEBUG,ESC_LOG(strLog).c_str());
00127 }
00129 string strValue = (*itr);
00130 if(dbi_conn_set_option(_con, strKey.c_str(), strValue.c_str()) != 0)
00131 throw runtime_error( getConError("dbi_conn_set_option "+(*itr)) );
00132 }
00133 }
00134 if(dbi_conn_connect(_con) != 0)
00135 throw runtime_error( getConError("dbi_conn_connect") );
00136 if(verbose)
00137 syslog(LOG_DEBUG,"connected to DB");
00138 if(_wlMods.size() == 0)
00139 init_wlMods();
00140
00141 if(initmode)
00142 createTable(g_getTripletDef());
00143 }
00144
00145 void DB::close()
00146 {
00147
00148 for(WlModuleList::const_iterator itr=_wlMods.begin(); itr != _wlMods.end();++itr)
00149 delete (*itr);
00150 _wlMods.clear();
00151 if(_con) {
00152
00153 syslog(LOG_INFO,"disconnecting from DB");
00154 dbi_conn_close(_con);
00155 _con = 0;
00156 }
00157 dbi_shutdown();
00158 }
00159 int DB::handle_signal(int signum) {
00160 syslog(LOG_DEBUG,"received signal %i",signum);
00161
00162 this->_graceful_quit = 1;
00163 this->close();
00164 return 0;
00165 }
00166 void DB::init_wlMods() {
00167 bool verbose = g_getCfg().isVerbose();
00168 bool initmode = g_getCfg().isModeInit();
00169 try {
00170
00171 const Cfg::WLMap& wlMap = g_getCfg().getWLMap();
00172 for(Cfg::WLMap::const_iterator itr=wlMap.begin(); itr != wlMap.end(); ++itr) {
00173 bool created = false;
00174 const std::string& tableName = itr->first;
00175
00176 const WLStaticDef& wldef = g_getWlDef(tableName);
00177 switch(itr->second.getMode()) {
00178 case Cfg::WLEntry::WL_db:
00179 _wlMods.push_back(new DbWlModule (*this, initmode, wldef) );
00180 created = true;
00181 break;
00182 case Cfg::WLEntry::WL_dbcached:
00183 _wlMods.push_back(new CachedDbWlModule (*this, initmode, wldef) );
00184 created = true;
00185 break;
00186 case Cfg::WLEntry::WL_off:
00187 break;
00188 default:
00189 throw runtime_error("wl mode not supported for table: "+tableName+". Check your configuration file.");
00190 }
00191 if(verbose && created)
00192 syslog(LOG_DEBUG,"created wl module: %s, type: %s",
00193 tableName.c_str(),g_getCfg().getWlModeName(itr->second.getMode()).c_str());
00194 }
00195 } catch(runtime_error &e) {
00196 syslog(LOG_ERR,"Error while initialising wl module: %s",e.what());
00197 }
00198 }
00199
00200 std::string DB::getConError(const std::string& pre) const
00201 {
00202 if(_con != 0) {
00203 const char *errBuff;
00204 dbi_conn_error(_con,&errBuff);
00205 return "DB: "+pre+": "+string(errBuff)+" ";
00206 } else {
00207 return "DB: "+pre+": no connection to db";
00208 }
00209 }
00210 void DB::list_drivers(ostream &os)
00211 {
00212 dbi_driver drv = 0;
00213 while((drv=dbi_driver_list(drv)) != 0) {
00214 os << " " << dbi_driver_get_name(drv);
00215 }
00216 }
00217 const std::string DB::trimLastByte(std::string str)
00218 {
00219 string::size_type lastdotpos = str.rfind('.');
00220 if(lastdotpos != string::npos) {
00221 str.erase(lastdotpos,string::npos);
00222 str += '.';
00223 }
00224 return str;
00225 }
00226
00227
00228
00229 DB::RecordStatus DB::update(const Triplet& triplet) throw(std::exception)
00230 {
00231
00232 if(!_con)
00233 throw runtime_error(getConError("update"));
00234 if(dbi_conn_ping(_con) != 1) {
00235 _con = 0;
00236 syslog(LOG_INFO,"lost db connection, reconnecting");
00238 this->open();
00239 }
00240 dbi_driver dbidrv = dbi_conn_get_driver(_con);
00241 DBIQuote sender(dbidrv, triplet.getSender().substr(0,MAX_LEN_SENDER));
00242 DBIQuote recipient(dbidrv, triplet.getRecipient().substr(0,MAX_LEN_RECIPIENT));
00243 DBIQuote client(dbidrv, triplet.getClientAddress().substr(0,MAX_LEN_CLIENT));
00244 string clientLoggable = client();
00245 string clientNumeric = triplet.getClientAddressNumeric();
00246 bool weak = g_getCfg().isModeWeak();
00247 bool reverse = g_getCfg().isModeReverse();
00248 bool verbose = g_getCfg().isVerbose();
00249 RecordStatus recStatus = unknown;
00250 time_t ltime;
00251 dbi_result result;
00252
00253 if(clientNumeric.size() != IPADDR_NUM)
00254 throw runtime_error("Invalid IP Address: "+triplet.getClientAddress()
00255 + " parsed: "+triplet.getClientAddressNumericStr());
00256
00257 if(!_wlMods.empty()) {
00258 Triplet quotedTriplet(client(),sender(),recipient(),clientNumeric);
00259 for(WlModuleList::const_iterator itr=_wlMods.begin(); itr != _wlMods.end();++itr)
00260 if( (*itr)->check(quotedTriplet) )
00261 return registered;
00262 }
00263
00264 unsigned clientIpBytes = clientNumeric.size();
00265 stringstream sqlSelect;
00266 stringstream sqlWhere;
00267
00268 sqlSelect << "SELECT count,uts FROM "TABLE_TRIPLETS" WHERE ( ";
00269 sqlWhere << ATTR_SENDER"=" << sender() << " AND "ATTR_RECIPIENT"=" << recipient();
00270
00271 if(reverse) {
00272
00273 string revClient = getWeakHost(triplet.getResolved());
00274 if(revClient.size() > 0) {
00275 transform(revClient.begin(),revClient.end(),revClient.begin(), ToLower());
00276 client = DBIQuote(dbidrv,revClient);
00277 clientLoggable = client();
00278 sqlWhere << " AND "ATTR_CLIENT"=" << client();
00279 } else {
00280
00281
00282 if(verbose) {
00283 if(triplet.getResolveError().size() > 0) {
00284
00285 syslog(LOG_DEBUG,"name resolve error: %s, %s",triplet.getResolveError().c_str(),client());
00286 } else {
00287
00288 syslog(LOG_INFO,"host name conversion failed: %s",client());
00289 }
00290 }
00291 reverse = false;
00292 weak = true;
00293 client.setNULL();
00294 }
00295 }
00296 if(weak) {
00297 clientIpBytes = g_getCfg().getWeakBytes();
00298 if( (clientIpBytes < 1) || (clientIpBytes > clientNumeric.size()) )
00299 throw runtime_error("Invalid client IP bytes (weakbytes in config): "
00300 +numToStr(clientIpBytes)+" (1-"+numToStr(clientNumeric.size())+"): "+triplet.getClientAddress()
00301 + " parsed: "+triplet.getClientAddressNumericStr());
00302 }
00303 if(!reverse)
00304 sqlWhere << " AND " << triplet.getClientAddressNumericStrSQL(" AND ","ip",64,"=",2,clientIpBytes);
00305 sqlSelect << sqlWhere.str() << " )";
00306
00307
00308
00309
00310
00311 if(verbose)
00312 syslog(LOG_DEBUG,ESC_LOG(sqlSelect.str()).c_str());
00313
00314 result = DBI_CON_QUERYF(_con, ESC_LOG(sqlSelect.str()).c_str());
00315 if(!result)
00316 throw runtime_error(getConError("update select: ")+sqlSelect.str()+" " );
00317
00318
00319 time(<ime);
00320 unsigned long lNow = ltime;
00321 int rows = dbi_result_get_numrows(result);
00322 if( rows == 0) {
00323 dbi_result_free(result);
00324 syslog(LOG_INFO,"new: %s -> %s, %s",sender(),recipient(),clientLoggable.c_str());
00325
00326 stringstream sqlInsert;
00327
00328
00329
00330
00331
00332
00333
00341 sqlInsert << "INSERT INTO "TABLE_TRIPLETS" VALUES (" << client() << "," << sender() << ',' << recipient() << ',';
00342 sqlInsert << triplet.getClientAddressNumericStr(',') << ",0," << lNow << ")";
00343 if(verbose)
00344 syslog(LOG_DEBUG,ESC_LOG(sqlInsert.str()).c_str());
00345 result = DBI_CON_QUERYF( _con, ESC_LOG(sqlInsert.str()).c_str() );
00346 if(!result)
00347 throw runtime_error(getConError("update insert: ")+sqlInsert.str() );
00348 recStatus = unknown;
00349 } else {
00350 dbi_result_next_row(result);
00351 unsigned long count = dbi_result_get_ulong_idx(result,1);
00352 unsigned long recTimeStamp = dbi_result_get_ulong_idx(result,2);
00353 unsigned long lTimeDiff = 0;
00354
00355 if(recTimeStamp < lNow)
00356 lTimeDiff = lNow - recTimeStamp;
00357 if( (count > 0) || (lTimeDiff > g_getCfg().getTimeout()) ) {
00358 syslog(LOG_INFO,"ok: %s -> %s, %s (%lu, %lu secs)",sender(),recipient(),clientLoggable.c_str(),
00359 count,lTimeDiff);
00360 dbi_result_free(result);
00361 recTimeStamp = lNow;
00362 stringstream sqlUpdate;
00363 sqlUpdate << "UPDATE "TABLE_TRIPLETS" SET count=count+1,uts=" << lNow;
00364 sqlUpdate << " WHERE ( " << sqlWhere.str() << " )";
00365 if(verbose)
00366 syslog(LOG_DEBUG,ESC_LOG(sqlUpdate.str()).c_str());
00367 result = DBI_CON_QUERYF(_con,ESC_LOG(sqlUpdate.str()).c_str());
00368 if(!result)
00369 throw runtime_error(getConError("update update: ")+sqlUpdate.str());
00370 recStatus = registered;
00371 } else {
00372 syslog(LOG_INFO,"wait: %s -> %s, %s (%lu, %lu secs)",sender(),recipient(),clientLoggable.c_str(),
00373 count,lTimeDiff);
00374 recStatus = waiting;
00375 }
00376 }
00377 if(result)
00378 dbi_result_free(result);
00379 return recStatus;
00380 }
00381 dbi_result DB::executeSQL(const std::string& sql) throw(std::exception)
00382 {
00383 if(!_con)
00384 throw runtime_error(getConError("executeSQL"));
00385 dbi_result result = DBI_CON_QUERYF(_con, ESC_LOG(sql).c_str() );
00386 if(!result) {
00387 syslog(LOG_INFO,"SQL error: %s",getConError("executeSQL").c_str());
00388
00389 }
00390 return result;
00391 }
00392 void DB::createTable(const WLStaticDef& staticDef) throw(std::exception)
00393 {
00394 if(!_con)
00395 throw runtime_error(getConError("createTable: no db connection"));
00396 bool verbose = g_getCfg().isVerbose();
00397 stringstream sqlTestTable;
00398 sqlTestTable << staticDef._testTable << " WHERE 1=0";
00399 if(verbose)
00400 syslog(LOG_DEBUG,sqlTestTable.str().c_str());
00401 dbi_result result = DBI_CON_QUERYF(_con, sqlTestTable.str().c_str() );
00402 if(!result) {
00403 syslog(LOG_INFO,"testing table failed: %s : %s, creating table",
00404 staticDef._tableName,getConError("").c_str());
00405 unsigned iCT = 0;
00406 while( staticDef._createTable[iCT] ) {
00407 stringstream sqlCreateTable;
00408 sqlCreateTable << staticDef._createTable[iCT];
00409 if(staticDef._primaryKey)
00410 sqlCreateTable << " PRIMARY KEY (" << staticDef._primaryKey << " )";
00411 sqlCreateTable << ")";
00412 if(verbose)
00413 syslog(LOG_DEBUG,ESC_LOG(sqlCreateTable.str()).c_str());
00414 result = DBI_CON_QUERYF(_con, sqlCreateTable.str().c_str());
00415 if(!result && (g_getCfg().getDbType() == "sqlite") ) {
00416
00417 result = DBI_CON_QUERYF(_con, sqlTestTable.str().c_str() );
00418 }
00419 if(!result) {
00420 throw runtime_error( getConError("create table: ")+staticDef._tableName
00421 + string( verbose?" SQL: "+sqlCreateTable.str() : "") );
00422 } else {
00423 dbi_result_free(result);
00424 result = 0;
00425 }
00426 iCT++;
00427 }
00428 syslog(LOG_INFO,"table %s created", staticDef._tableName );
00429 }
00430 if(result)
00431 dbi_result_free(result);
00432 }
00433 std::string DB::getSqlCondition(const WLCompares& compares,const Triplet& triplet) const
00434 {
00435 string sql;
00436 int j = 0;
00437 while(j < MAX_WL_COMPARES) {
00438 if(compares[j]._compareWith) {
00439 if(compares[j]._compareBinder)
00440 sql += std::string(" ") + compares[j]._compareBinder + " ";
00441 sql += compares[j]._compareWith;
00442 sql += compares[j]._compareOp;
00443 sql += triplet.getMember(compares[j]._tripletMember);
00444 j++;
00445 } else
00446 break;
00447 }
00448 return sql;
00449 }
00450 bool DB::exactMatch(const std::string& logprefix,const WLStaticDef& staticDef,
00451 const Triplet& triplet) throw(std::exception)
00452 {
00453 bool verbose = g_getCfg().isVerbose();
00454 string sqlWlSelect = staticDef._searchTable;
00455 sqlWlSelect += " WHERE " + getSqlCondition(staticDef._compares,triplet);
00456 sqlWlSelect += " LIMIT 1";
00457 if(verbose)
00458 syslog(LOG_DEBUG,ESC_LOG(sqlWlSelect).c_str());
00459 dbi_result result = DBI_CON_QUERYF(_con, ESC_LOG(sqlWlSelect).c_str());
00460 if(!result)
00461 throw runtime_error(getConError("exactMatch")+staticDef._tableName );
00462 int rows = dbi_result_get_numrows(result);
00463 if( rows == 0) {
00464 dbi_result_free(result);
00465 return false;
00466 } else {
00467 dbi_result_next_row(result);
00468 const char *comment = dbi_result_get_string_idx(result,1);
00469 syslog(LOG_INFO,"%s %s: %s -> %s, %s: %s",
00470 logprefix.c_str(),staticDef._tableName,triplet.getSender().c_str(),
00471 triplet.getRecipient().c_str(),triplet.getClientAddress().c_str(),comment);
00472 dbi_result_free(result);
00473 return true;
00474 }
00475 }
00476 const std::string DB::numToStr(unsigned u) const
00477 {
00478 stringstream stm;
00479 stm << u;
00480 return stm.str();
00481 }
00482 std::string DB::getWeakHost(const std::string& host) const throw(std::exception)
00483 {
00484 if(WEAKRVHOSTDBG) cout << host << " len:" << host.size() <<endl;
00485 if(host.size() == 0)
00486 return string(host);
00487 string::size_type pos = host.find('.');
00488 if(pos == string::npos)
00489 return string(host);
00490 if(WEAKRVHOSTDBG) cout << host.substr(pos) << " pos:" << pos << endl;
00491 if(WEAKRVHOSTDBG) cout << host.substr(pos+1,string::npos) << endl;
00492 return host.substr(pos+1,string::npos);
00493 }
00494
00495
00496