]> git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/cdb.cc
Merge pull request #14200 from rgacogne/auth-enable-leak-detection-unit-tests
[thirdparty/pdns.git] / pdns / cdb.cc
1 /*
2 * This file is part of PowerDNS or dnsdist.
3 * Copyright -- PowerDNS.COM B.V. and its contributors
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of version 2 of the GNU General Public License as
7 * published by the Free Software Foundation.
8 *
9 * In addition, for the avoidance of any doubt, permission is granted to
10 * link this program with OpenSSL and to (re)distribute the binaries
11 * produced as the result of such linking.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 */
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25
26 #include <sys/types.h>
27 #include <sys/stat.h>
28 #include <fcntl.h>
29 #include <unistd.h>
30
31 #include "cdb.hh"
32
33 CDB::CDB(const string &cdbfile)
34 {
35 d_fd = open(cdbfile.c_str(), O_RDONLY);
36 if (d_fd < 0)
37 {
38 throw std::runtime_error("Failed to open cdb database file '"+cdbfile+"': " + stringerror());
39 }
40
41 memset(&d_cdbf,0,sizeof(struct cdb_find));
42 int cdbinit = cdb_init(&d_cdb, d_fd);
43 if (cdbinit < 0)
44 {
45 close(d_fd);
46 d_fd = -1;
47 throw std::runtime_error("Failed to initialize cdb structure for database '+cdbfile+': '" + std::to_string(cdbinit) + "'");
48 }
49 }
50
51 CDB::~CDB() {
52 cdb_free(&d_cdb);
53 close(d_fd);
54 }
55
56 int CDB::searchKey(const string &key) {
57 d_searchType = SearchKey;
58
59 // A 'bug' in tinycdb (the lib used for reading the CDB files) means we have to copy the key because the cdb_find struct
60 // keeps a pointer to it.
61 d_key = key;
62 return cdb_findinit(&d_cdbf, &d_cdb, d_key.c_str(), d_key.size());
63 }
64
65 bool CDB::searchSuffix(const string &key) {
66 d_searchType = SearchSuffix;
67
68 //See CDB::searchKey()
69 d_key = key;
70
71 // We are ok with a search on things, but we do want to know if a record with that key exists.........
72 bool hasDomain = (cdb_find(&d_cdb, d_key.c_str(), d_key.size()) == 1);
73 if (hasDomain) {
74 cdb_seqinit(&d_seqPtr, &d_cdb);
75 }
76
77 return hasDomain;
78 }
79
80 void CDB::searchAll() {
81 d_searchType = SearchAll;
82 cdb_seqinit(&d_seqPtr, &d_cdb);
83 }
84
85 bool CDB::moveToNext() {
86 int hasNext = 0;
87 if (d_searchType == SearchKey) {
88 hasNext = cdb_findnext(&d_cdbf);
89 } else {
90 hasNext = cdb_seqnext(&d_seqPtr, &d_cdb);
91 }
92 return (hasNext > 0);
93 }
94
95 bool CDB::readNext(pair<string, string> &value) {
96 while (moveToNext()) {
97 unsigned int pos;
98 unsigned int len;
99
100 pos = cdb_keypos(&d_cdb);
101 len = cdb_keylen(&d_cdb);
102
103 std::string key;
104 key.resize(len);
105 int ret = cdb_read(&d_cdb, &key[0], len, pos);
106 if (ret < 0) {
107 throw std::runtime_error("Error while reading key for key '" + key + "' from CDB database: " + std::to_string(ret));
108 }
109
110 if (d_searchType == SearchSuffix) {
111 char *p = strstr(const_cast<char*>(key.c_str()), d_key.c_str());
112 if (p == nullptr) {
113 continue;
114 }
115 }
116
117 pos = cdb_datapos(&d_cdb);
118 len = cdb_datalen(&d_cdb);
119 std::string val;
120 val.resize(len);
121 ret = cdb_read(&d_cdb, &val[0], len, pos);
122 if (ret < 0) {
123 throw std::runtime_error("Error while reading value for key '" + key + "' from CDB database: " + std::to_string(ret));
124 }
125
126 value = {std::move(key), std::move(val)};
127 return true;
128 }
129
130 // We're done searching, so we can clean up d_key
131 if (d_searchType != SearchAll) {
132 d_key.clear();
133 }
134
135 return false;
136 }
137
138 vector<string> CDB::findall(string &key)
139 {
140 vector<string> ret;
141 struct cdb_find cdbf;
142
143 int res = cdb_findinit(&cdbf, &d_cdb, key.c_str(), key.size());
144 if (res < 0) {
145 throw std::runtime_error("Error looking up key '" + key + "' from CDB database: " + std::to_string(res));
146 }
147
148 while(cdb_findnext(&cdbf) > 0) {
149 unsigned int vpos = cdb_datapos(&d_cdb);
150 unsigned int vlen = cdb_datalen(&d_cdb);
151 std::string val;
152 val.resize(vlen);
153 res = cdb_read(&d_cdb, &val[0], vlen, vpos);
154 if (res < 0) {
155 throw std::runtime_error("Error while reading value for key '" + key + "' from CDB database: " + std::to_string(res));
156 }
157 ret.push_back(std::move(val));
158 }
159
160 return ret;
161 }
162
163 bool CDB::keyExists(const string& key)
164 {
165 int ret = cdb_find(&d_cdb, key.c_str(), key.size());
166 if (ret < 0) {
167 throw std::runtime_error("Error while looking up key '" + key + "' from CDB database: " + std::to_string(ret));
168 }
169 if (ret == 0) {
170 /* no such key */
171 return false;
172 }
173
174 return true;
175 }
176
177 bool CDB::findOne(const string& key, string& value)
178 {
179 if (!keyExists(key)) {
180 return false;
181 }
182
183 unsigned int vpos = cdb_datapos(&d_cdb);
184 unsigned int vlen = cdb_datalen(&d_cdb);
185 value.resize(vlen);
186 int ret = cdb_read(&d_cdb, &value[0], vlen, vpos);
187 if (ret < 0) {
188 throw std::runtime_error("Error while reading value for key '" + key + "' from CDB database: " + std::to_string(ret));
189 }
190
191 return true;
192 }
193
194 CDBWriter::CDBWriter(int fd): d_fd(fd)
195 {
196 cdb_make_start(&d_cdbm, d_fd);
197 }
198
199 CDBWriter::~CDBWriter()
200 {
201 close();
202 }
203
204 void CDBWriter::close()
205 {
206 if (d_fd >= 0) {
207 cdb_make_finish(&d_cdbm);
208 ::close(d_fd);
209 d_fd = -1;
210 }
211 }
212
213 bool CDBWriter::addEntry(const std::string& key, const std::string& value)
214 {
215 if (d_fd < 0) {
216 throw std::runtime_error("Can't add an entry to a closed CDB database");
217 }
218
219 int ret = cdb_make_add(&d_cdbm, key.c_str(), key.size(), value.c_str(), value.size());
220 if (ret != 0) {
221 throw std::runtime_error("Error adding key '" + key + "' to CDB database: " + std::to_string(ret));
222 }
223
224 return true;
225 }