123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641 |
- // Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
- //
- // Permission to use, copy, modify, and/or distribute this software for any
- // purpose with or without fee is hereby granted, provided that the above
- // copyright notice and this permission notice appear in all copies.
- //
- // THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
- // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
- // AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
- // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
- // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
- // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
- // PERFORMANCE OF THIS SOFTWARE.
- // $Id$
- #include "data_source_sqlite3.h"
- #include <dns/rrttl.h>
- #include <dns/rdata.h>
- #include <dns/rdataclass.h>
- #include <dns/rrset.h>
- #include <dns/rrsetlist.h>
- #include <iostream>
- using namespace std;
- using namespace isc::dns;
- using namespace isc::dns::rdata;
- namespace isc {
- namespace auth {
- //
- // Prepare a statement. Can call release() or sqlite3_finalize()
- // directly.
- //
- sqlite3_stmt* Sqlite3DataSrc::prepare(const char *statement) {
- int rc;
- sqlite3_stmt *prepared = NULL;
- rc = sqlite3_prepare_v2(db, statement, -1, &prepared, NULL);
- if (rc != SQLITE_OK) {
- throw("could not prepare");
- }
- return (prepared);
- }
- //
- // Release memory associated with a prepared query.
- //
- void Sqlite3DataSrc::release(sqlite3_stmt* prepared) {
- sqlite3_finalize(prepared);
- }
- //
- // Get the database schema version.
- //
- int Sqlite3DataSrc::getVersion(void) {
- if (database_version == -1) {
- loadVersion();
- }
- return (database_version);
- }
- //
- // Find the exact zone match. Return -1 if not found, or the zone's
- // ID if found. This will always be >= 0 if found.
- //
- int Sqlite3DataSrc::hasExactZone(const char *name) const {
- int rc, i;
- sqlite3_reset(q_zone);
- rc = sqlite3_bind_text(q_zone, 1, name, -1, SQLITE_STATIC);
- if (rc != SQLITE_OK) {
- throw("Could not bind");
- }
- rc = sqlite3_step(q_zone);
- if (rc == SQLITE_ROW) {
- i = sqlite3_column_int(q_zone, 0);
- } else {
- i = -1;
- }
- sqlite3_reset(q_zone);
- return (i);
- }
- int
- Sqlite3DataSrc::
- findRecords(const Name& name, const RRType& rdtype, RRsetList& target,
- Name* zone, const Mode mode, uint32_t& flags) const
- {
- int rc;
- const string s_name = name.toText();
- const char *c_name = s_name.c_str();
- const string s_rdtype = rdtype.toText();
- const char *c_rdtype = s_rdtype.c_str();
- sqlite3_stmt *query;
- switch (mode) {
- case ADDRESS:
- query = q_addrs;
- break;
- case DELEGATION:
- query = q_referral;
- break;
- default:
- if (rdtype == RRType::ANY()) {
- query = q_any;
- } else {
- query = q_record;
- }
- break;
- }
- flags = 0;
- int zone_id;
- if (zone == NULL) {
- zone_id = findClosest(c_name, NULL);
- } else {
- const string s_zone = zone->toText();
- const char *c_zone = s_zone.c_str();
- zone_id = findClosest(c_zone, NULL);
- }
- if (zone_id < 0) {
- flags = NO_SUCH_ZONE;
- return (0);
- }
-
- sqlite3_reset(query);
- sqlite3_clear_bindings(query);
- rc = sqlite3_bind_int(query, 1, zone_id);
- if (rc != SQLITE_OK) {
- throw("Could not bind 1 (record)");
- }
- rc = sqlite3_bind_text(query, 2, c_name, -1, SQLITE_STATIC);
- if (rc != SQLITE_OK) {
- throw("Could not bind 2 (record)");
- }
- if (query == q_record) {
- rc = sqlite3_bind_text(query, 3, c_rdtype, -1, SQLITE_STATIC);
- if (rc != SQLITE_OK) {
- throw("Could not bind 3 (record)");
- }
- }
- // loop
- int target_ttl = -1;
- int sig_ttl = -1;
- int rows = 0;
- RRsetPtr rrset;
- bool any = (rdtype == RRType::ANY());
- rc = sqlite3_step(query);
- while (rc == SQLITE_ROW) {
- const char *type = (const char *)sqlite3_column_text(query, 0);
- int ttl = sqlite3_column_int(query, 1);
- const char *sigtype = (const char *)sqlite3_column_text(query, 2);
- const char *rdata = (const char *)sqlite3_column_text(query, 3);
- RRType rt(sigtype ? sigtype : type);
- // looking for something else but found NS; we need to inform
- // the caller that this is a referral, but we do not return the
- // NS RRset to the caller.
- if (rdtype != RRType::NS() && !any && rt == RRType::NS()) {
- flags |= REFERRAL;
- rc = sqlite3_step(query);
- continue;
- }
- rows++;
- // Looking for something else but found CNAME
- if (rt == RRType::CNAME() && rdtype != RRType::CNAME()) {
- if (rdtype == RRType::NSEC()) {
- // NSEC query, just skip the CNAME
- rc = sqlite3_step(query);
- continue;
- } else if (!any) {
- // include the CNAME, but don't flag it for chasing if
- // this is an ANY query
- flags |= CNAME_FOUND;
- }
- }
- if (!target[rt]) {
- rrset = RRsetPtr(new RRset(name, RRClass::IN(), rt, RRTTL(3600)));
- target.addRRset(rrset);
- }
- if (!sigtype && RRType(type) == rrset->getType()) {
- RdataPtr item = createRdata(RRType(type), RRClass("IN"), rdata);
- rrset->addRdata(item);
- if (target_ttl == -1 || target_ttl > ttl) {
- target_ttl = ttl;
- }
- rrset->setTTL(RRTTL(target_ttl));
- } else if (sigtype && RRType(sigtype) == rrset->getType()) {
- RdataPtr rrsig = createRdata(RRType::RRSIG(), RRClass::IN(), rdata);
- if (rrset->getRRsig()) {
- rrset->getRRsig()->addRdata(rrsig);
- } else {
- RRsetPtr sigs = RRsetPtr(new RRset(name, RRClass::IN(),
- RRType::RRSIG(),
- RRTTL(3600)));
- sigs->addRdata(rrsig);
- rrset->addRRsig(sigs);
- }
- if (sig_ttl == -1 || sig_ttl > ttl) {
- sig_ttl = ttl;
- }
- rrset->getRRsig()->setTTL(RRTTL(sig_ttl));
- }
-
- rc = sqlite3_step(query);
- }
- sqlite3_reset(query);
- if (rows > 0) {
- return (rows);
- }
- //
- // No rows were found. We need to find out whether there are
- // any RRs with that name to determine whether this is NXDOMAIN or
- // NXRRSET
- //
- sqlite3_reset(q_count);
- sqlite3_clear_bindings(q_count);
- rc = sqlite3_bind_int(q_count, 1, zone_id);
- if (rc != SQLITE_OK) {
- throw("Could not bind 1 (count)");
- }
- rc = sqlite3_bind_text(q_count, 2, c_name, -1, SQLITE_STATIC);
- if (rc != SQLITE_OK) {
- throw("Could not bind 2 (count)");
- }
- rc = sqlite3_step(q_count);
- if(rc == SQLITE_ROW) {
- int count = sqlite3_column_int(q_count, 0);
- if (count != 0) {
- flags |= TYPE_NOT_FOUND;
- sqlite3_reset(q_count);
- return (0);
- }
- }
- flags |= NAME_NOT_FOUND;
- sqlite3_reset(q_count);
- return (0);
- }
- //
- // Search for the closest enclosing zone. Will return -1 if not found,
- // >= 0 if found. If position is not NULL, it will be filled in with the
- // longest match found.
- //
- int Sqlite3DataSrc::findClosest(const char *name, const char **position) const {
- int rc;
- const char *current = name;
-
- while (*current != 0) {
- rc = hasExactZone(current);
- if (rc >= 0) {
- if (position != NULL) {
- *position = current;
- }
- return (rc);
- }
- while (*current != '.' && *current != 0) {
- current++;
- }
- if (*current == '.') {
- current++;
- }
- }
- return (-1);
- }
- void Sqlite3DataSrc::loadVersion(void) {
- int rc;
- const char *q = "SELECT version FROM schema_version";
- sqlite3_stmt *prepared = prepare(q);
- rc = sqlite3_step(prepared);
- if (rc != SQLITE_ROW) {
- throw("failed to find a row in schema_version table");
- }
- database_version = sqlite3_column_int(prepared, 0);
- release(prepared);
- }
- void Sqlite3DataSrc::setupPreparedStatements(void) {
- const char *q_zone_str = "SELECT id FROM zones WHERE name=?1";
- try {
- q_zone = prepare(q_zone_str);
- } catch (const char *e) {
- cout << e << endl << q_zone_str << endl;
- cout << sqlite3_errmsg(db) << endl;
- throw(e);
- }
- const char *q_record_str = "SELECT rdtype, ttl, sigtype, rdata "
- "FROM records WHERE zone_id=?1 AND name=?2 AND "
- "((rdtype=?3 OR sigtype=?3) OR "
- "(rdtype='CNAME' OR sigtype='CNAME') OR "
- "(rdtype='NS' OR sigtype='NS'))";
- try {
- q_record = prepare(q_record_str);
- } catch (const char *e) {
- cout << e << endl << q_record_str << endl;
- cout << sqlite3_errmsg(db) << endl;
- throw(e);
- }
- const char *q_addrs_str = "SELECT rdtype, ttl, sigtype, rdata "
- "FROM records WHERE zone_id=?1 AND name=?2 AND "
- "(rdtype='A' OR sigtype='A' OR "
- "rdtype='AAAA' OR sigtype='AAAA')";
- try {
- q_addrs = prepare(q_addrs_str);
- } catch (const char *e) {
- cout << e << endl << q_addrs_str << endl;
- cout << sqlite3_errmsg(db) << endl;
- throw(e);
- }
- const char *q_referral_str = "SELECT rdtype, ttl, sigtype, rdata FROM "
- "records WHERE zone_id=?1 AND name=?2 AND"
- "(rdtype='NS' OR sigtype='NS' OR "
- "rdtype='DS' OR sigtype='DS' OR "
- "rdtype='DNAME' OR sigtype='DNAME')";
- try {
- q_referral = prepare(q_referral_str);
- } catch (const char *e) {
- cout << e << endl << q_referral_str << endl;
- cout << sqlite3_errmsg(db) << endl;
- throw(e);
- }
- const char *q_any_str = "SELECT rdtype, ttl, sigtype, rdata "
- "FROM records WHERE zone_id=?1 AND name=?2";
- try {
- q_any = prepare(q_any_str);
- } catch (const char *e) {
- cout << e << endl << q_any_str << endl;
- cout << sqlite3_errmsg(db) << endl;
- throw(e);
- }
- const char *q_count_str = "SELECT COUNT(*) FROM records "
- "WHERE zone_id=?1 AND (name=?2 OR "
- "name LIKE '%.' || ?2);";
- try {
- q_count = prepare(q_count_str);
- } catch (const char *e) {
- cout << e << endl << q_count_str << endl;
- cout << sqlite3_errmsg(db) << endl;
- throw(e);
- }
- const char *q_previous_str = "SELECT name FROM records "
- "WHERE zone_id=?1 AND rdtype = 'NSEC' AND "
- "rname < $2 ORDER BY rname DESC LIMIT 1";
- try {
- q_previous = prepare(q_previous_str);
- } catch (const char *e) {
- cout << e << endl << q_previous_str << endl;
- cout << sqlite3_errmsg(db) << endl;
- throw(e);
- }
- }
- void Sqlite3DataSrc::execSetupQuery(const char *query) {
- int rc;
- rc = sqlite3_exec(db, query, NULL, NULL, NULL);
- if (rc != SQLITE_OK) {
- throw(query);
- }
- }
- void Sqlite3DataSrc::checkAndSetupSchema(void) {
- try {
- loadVersion();
- setupPreparedStatements();
- cout << "Loaded existing schema" << endl;
- } catch(...) {
- execSetupQuery("CREATE TABLE schema_version ("
- "version INTEGER NOT NULL)");
- execSetupQuery("INSERT INTO schema_version VALUES (1)");
- execSetupQuery("CREATE TABLE zones ("
- "id INTEGER PRIMARY KEY, "
- "name STRING NOT NULL, "
- "rdclass STRING NOT NULL DEFAULT 'IN', "
- "dnssec BOOLEAN NOT NULL DEFAULT 0)");
- execSetupQuery("CREATE TABLE records ("
- "id INTEGER PRIMARY KEY, "
- "zone_id INTEGER NOT NULL, "
- "name STRING NOT NULL, "
- "rname STRING NOT NULL, "
- "ttl INTEGER NOT NULL, "
- "rdtype STRING NOT NULL, "
- "sigtype STRING, "
- "rdata STRING NOT NULL)");
- execSetupQuery("CREATE INDEX records_byname ON records (name)");
- execSetupQuery("CREATE INDEX records_byrname ON records (rname)");
- execSetupQuery("CREATE INDEX zones_byname ON zones (name)");
- setupPreparedStatements();
- cout << "Created new file and schema" << endl;
- }
- }
- Sqlite3DataSrc::Sqlite3DataSrc()
- {
- db = NULL;
- database_version = -1;
- q_zone = NULL;
- q_record = NULL;
- q_addrs = NULL;
- q_referral = NULL;
- q_any = NULL;
- q_count = NULL;
- q_previous = NULL;
- }
- Sqlite3DataSrc::~Sqlite3DataSrc()
- {
- close();
- }
- DataSrc::Result
- Sqlite3DataSrc::init()
- {
- try {
- open("/tmp/zone.sqlite3");
-
- cout << "Schema version: " << getVersion() << endl;
- } catch (const char *e) {
- cout << e << endl;
- }
- return (SUCCESS);
- }
- void
- Sqlite3DataSrc::findClosestEnclosure(NameMatch& match) const {
- const Name& qname = match.qname();
- const string target_string = qname.toText();
- const char *position = NULL;
-
- int ret = findClosest(target_string.c_str(), &position);
- if (ret == -1) {
- return;
- }
- match.update(*this, Name(position));
- }
- DataSrc::Result
- Sqlite3DataSrc::findPreviousName(const Query& q,
- const Name& qname,
- Name& target,
- Name* zone) const
- {
- const char *c_rname = qname.reverse().toText().c_str();
- int zone_id;
- if (zone == NULL) {
- const char *c_name = qname.toText().c_str();
- zone_id = findClosest(c_name, NULL);
- } else {
- const char *c_zone = zone->toText().c_str();
- zone_id = findClosest(c_zone, NULL);
- }
- if (zone_id < 0) {
- return (ERROR);
- }
-
- sqlite3_reset(q_previous);
- sqlite3_clear_bindings(q_previous);
- int rc = sqlite3_bind_int(q_previous, 1, zone_id);
- if (rc != SQLITE_OK) {
- throw ("Could not bind 1 (record)");
- }
- rc = sqlite3_bind_text(q_previous, 2, c_rname, -1, SQLITE_STATIC);
- if (rc != SQLITE_OK) {
- throw ("Could not bind 2 (record)");
- }
- rc = sqlite3_step(q_previous);
- if (rc != SQLITE_ROW) {
- sqlite3_reset(q_previous);
- return (ERROR);
- }
- const char *prev = (const char *) sqlite3_column_text(q_previous, 0);
- target = Name(prev);
- sqlite3_reset(q_previous);
- return (SUCCESS);
- }
- DataSrc::Result
- Sqlite3DataSrc::findRRset(const Query& q,
- const Name& qname,
- const RRClass& qclass,
- const RRType& qtype,
- RRsetList& target,
- uint32_t& flags,
- Name* zone) const
- {
- findRecords(qname, qtype, target, zone, NORMAL, flags);
- return (SUCCESS);
- }
- DataSrc::Result
- Sqlite3DataSrc::findExactRRset(const Query& q,
- const Name& qname,
- const RRClass& qclass,
- const RRType& qtype,
- RRsetList& target,
- uint32_t& flags,
- Name* zone) const
- {
- findRecords(qname, qtype, target, zone, NORMAL, flags);
- // Ignore referrals in this case
- flags &= ~REFERRAL;
- // CNAMEs don't count in this case
- if (flags & CNAME_FOUND) {
- flags &= ~CNAME_FOUND;
- flags |= TYPE_NOT_FOUND;
- }
- return (SUCCESS);
- }
- DataSrc::Result
- Sqlite3DataSrc::findAddrs(const Query& q,
- const Name& qname,
- const RRClass& qclass,
- RRsetList& target,
- uint32_t& flags,
- Name* zone) const {
- findRecords(qname, RRType::ANY(), target, zone, ADDRESS, flags);
- return (SUCCESS);
- }
- DataSrc::Result
- Sqlite3DataSrc::findReferral(const Query& q,
- const Name& qname,
- const RRClass& qclass,
- RRsetList& target,
- uint32_t& flags,
- Name* zone) const {
- findRecords(qname, RRType::ANY(), target, zone, DELEGATION, flags);
- return (SUCCESS);
- }
- //
- // Open the database.
- //
- void
- Sqlite3DataSrc::open(const string& name)
- {
- int rc;
- database_name = name;
-
- rc = sqlite3_open(database_name.c_str(), &db);
- if (rc) {
- cerr << "open database: " << sqlite3_errmsg(db) << "\n";
- sqlite3_close(db);
- throw("Cannot open database");
- }
-
- checkAndSetupSchema();
- }
- DataSrc::Result
- Sqlite3DataSrc::close(void)
- {
- if (db == NULL) {
- return (SUCCESS);
- }
- if (q_zone != NULL) {
- release(q_zone);
- q_zone = NULL;
- }
- if (q_record) {
- release(q_record);
- q_record = NULL;
- }
- if (q_addrs) {
- release(q_addrs);
- q_addrs = NULL;
- }
- if (q_referral) {
- release(q_referral);
- q_referral = NULL;
- }
- if (q_any) {
- release(q_any);
- q_any = NULL;
- }
- if (q_count) {
- release(q_count);
- q_count = NULL;
- }
- if (q_previous) {
- release(q_previous);
- q_previous = NULL;
- }
- sqlite3_close(db);
- db = NULL;
- return (SUCCESS);
- }
- }
- }
|