3 * db.c: load and save in-memory database from/to file
4 * copyright (c) 2007 Ben Stewart, Emil Mikulic.
6 * You may use, modify and redistribute this file under the terms of the
7 * GNU General Public License version 2. (see COPYING.GPL)
10 #define _GNU_SOURCE 1 /* for O_NOFOLLOW in Linux */
12 #include <sys/types.h>
20 #include <netinet/in.h> /* for ntohs() and friends */
26 static const unsigned char export_file_header[] = {0xDA, 0x31, 0x41, 0x59};
27 static const unsigned char export_tag_hosts_ver1[] = {0xDA, 'H', 'S', 0x01};
28 static const unsigned char export_tag_graph_ver1[] = {0xDA, 'G', 'R', 0x01};
31 static inline uint64_t
34 /* this is __bswap64 from:
35 * $FreeBSD: src/sys/i386/include/endian.h,v 1.41$
37 return ((_x >> 56) | ((_x >> 40) & 0xff00) | ((_x >> 24) & 0xff0000) |
38 ((_x >> 8) & 0xff000000) | ((_x << 8) & ((uint64_t)0xff << 32)) |
39 ((_x << 24) & ((uint64_t)0xff << 40)) |
40 ((_x << 40) & ((uint64_t)0xff << 48)) | ((_x << 56)));
45 hton64(const uint64_t ho)
47 if (ntohs(0x1234) == 0x1234) return ho;
48 else return swap64(ho);
52 ntoh64(const uint64_t no)
60 static const char str[] = { 0x79,0x74,0x69,0x63,0x6b,0x72,0x65,0x6a };
63 assert(sizeof(no) == 8);
66 assert(ho == 8751735851613054314ULL);
67 assert(hton64(ntoh64(no)) == no);
70 /* ---------------------------------------------------------------------------
71 * Read-from-file helpers. They all return 0 on failure, and 1 on success.
77 off_t ofs = lseek(fd, 0, SEEK_CUR);
79 err(1, "lseek(0, SEEK_CUR) failed");
80 return (unsigned int)ofs;
83 /* Read <len> bytes from <fd>, warn() and return 0 on failure,
84 * or return 1 for success.
87 readn(const int fd, void *dest, const size_t len)
91 numread = read(fd, dest, len);
92 if (numread == (ssize_t)len) return 1;
95 warn("at pos %u: couldn't read %d bytes", xtell(fd), (int)len);
97 warnx("at pos %u: tried to read %d bytes, got %d",
98 xtell(fd), (int)len, (int)numread);
104 read8(const int fd, uint8_t *dest)
106 assert(sizeof(*dest) == 1);
107 return readn(fd, dest, sizeof(*dest));
110 /* Read a byte and compare it to the expected data.
111 * Returns 0 on failure or mismatch, 1 on success.
114 expect8(const int fd, uint8_t expecting)
118 assert(sizeof(tmp) == 1);
119 if (!readn(fd, &tmp, sizeof(tmp))) return 0;
120 if (tmp == expecting) return 1;
122 warnx("at pos %u: expecting 0x%02x, got 0x%02x",
123 xtell(fd)-1, expecting, tmp);
127 /* Read a network order uint16_t from a file
128 * and store it in host order in memory.
131 read16(const int fd, uint16_t *dest)
135 assert(sizeof(tmp) == 2);
136 if (!read(fd, &tmp, sizeof(tmp))) return 0;
141 /* Read a network order uint32_t from a file
142 * and store it in host order in memory.
145 read32(const int fd, uint32_t *dest)
149 assert(sizeof(tmp) == 4);
150 if (!read(fd, &tmp, sizeof(tmp))) return 0;
155 /* Read an IPv4 addr from a file. This is for backward compatibility with
156 * host records version 1 and 2.
159 readaddr_ipv4(const int fd, struct addr *dest)
162 return readn(fd, &(dest->ip.v4), sizeof(dest->ip.v4));
165 /* Read a struct addr from a file. Addresses are always stored in network
166 * order, both in the file and in the host's memory (FIXME: is that right?)
169 readaddr(const int fd, struct addr *dest)
171 unsigned char family;
173 if (!read8(fd, &family))
178 return readn(fd, &(dest->ip.v4), sizeof(dest->ip.v4));
180 else if (family == 6) {
182 return readn(fd, dest->ip.v6.s6_addr, sizeof(dest->ip.v6.s6_addr));
185 return 0; /* no address family I ever heard of */
188 /* Read a network order uint64_t from a file
189 * and store it in host order in memory.
192 read64(const int fd, uint64_t *dest)
196 assert(sizeof(tmp) == 8);
197 if (!read(fd, &tmp, sizeof(tmp))) return 0;
202 /* ---------------------------------------------------------------------------
203 * Write-to-file helpers. They all return 0 on failure, and 1 on success.
206 /* Write <len> bytes to <fd>, warn() and return 0 on failure,
207 * or return 1 for success.
210 writen(const int fd, const void *dest, const size_t len)
214 numwr = write(fd, dest, len);
215 if (numwr == (ssize_t)len) return 1;
218 warn("couldn't write %d bytes", (int)len);
220 warnx("tried to write %d bytes but wrote %d",
221 (int)len, (int)numwr);
226 write8(const int fd, const uint8_t i)
228 assert(sizeof(i) == 1);
229 return writen(fd, &i, sizeof(i));
232 /* Given a uint16_t in host order, write it to a file in network order.
235 write16(const int fd, const uint16_t i)
237 uint16_t tmp = htons(i);
238 assert(sizeof(tmp) == 2);
239 return writen(fd, &tmp, sizeof(tmp));
242 /* Given a uint32_t in host order, write it to a file in network order.
245 write32(const int fd, const uint32_t i)
247 uint32_t tmp = htonl(i);
248 assert(sizeof(tmp) == 4);
249 return writen(fd, &tmp, sizeof(tmp));
252 /* Given a uint64_t in host order, write it to a file in network order.
255 write64(const int fd, const uint64_t i)
257 uint64_t tmp = hton64(i);
258 assert(sizeof(tmp) == 8);
259 return writen(fd, &tmp, sizeof(tmp));
263 /* Write the active address part in a struct addr to a file.
264 * Addresses are always stored in network order, both in the file and
265 * in the host's memory (FIXME: is that right?)
268 writeaddr(const int fd, const struct addr *const a)
270 if (!write8(fd, a->family))
273 if (a->family == IPv4)
274 return writen(fd, &(a->ip.v4), sizeof(a->ip.v4));
276 assert(a->family == IPv6);
277 return writen(fd, a->ip.v6.s6_addr, sizeof(a->ip.v6.s6_addr));
281 /* ---------------------------------------------------------------------------
282 * db import/export code follows.
285 /* Check that the global file header is correct / supported. */
287 read_file_header(const int fd, const uint8_t expected[4])
291 if (!readn(fd, got, sizeof(got))) return 0;
293 /* Check the header data */
294 if (memcmp(got, expected, sizeof(got)) != 0) {
296 "expecting %02x%02x%02x%02x, got %02x%02x%02x%02x",
297 expected[0], expected[1], expected[2], expected[3],
298 got[0], got[1], got[2], got[3]);
304 /* Returns 0 on failure, 1 on success. */
306 db_import_from_fd(const int fd)
308 if (!read_file_header(fd, export_file_header)) return 0;
309 if (!read_file_header(fd, export_tag_hosts_ver1)) return 0;
310 if (!hosts_db_import(fd)) return 0;
311 if (!read_file_header(fd, export_tag_graph_ver1)) return 0;
312 if (!graph_import(fd)) return 0;
317 db_import(const char *filename)
319 int fd = open(filename, O_RDONLY | O_NOFOLLOW);
321 warn("can't import from \"%s\"", filename);
324 if (!db_import_from_fd(fd)) {
325 warnx("import failed");
326 /* don't stay in an inconsistent state: */
333 /* Returns 0 on failure, 1 on success. */
335 db_export_to_fd(const int fd)
337 if (!writen(fd, export_file_header, sizeof(export_file_header)))
339 if (!writen(fd, export_tag_hosts_ver1, sizeof(export_tag_hosts_ver1)))
341 if (!hosts_db_export(fd))
343 if (!writen(fd, export_tag_graph_ver1, sizeof(export_tag_graph_ver1)))
345 if (!graph_export(fd))
351 db_export(const char *filename)
353 int fd = open(filename, O_WRONLY | O_CREAT | O_NOFOLLOW | O_TRUNC, 0600);
355 warn("can't export to \"%s\"", filename);
358 verbosef("exporting db to file \"%s\"", filename);
359 if (!db_export_to_fd(fd))
360 warnx("export failed");
362 verbosef("export successful");
364 /* FIXME: should write to another filename and use the rename() syscall to
365 * atomically update the output file on success
370 /* vim:set ts=3 sw=3 tw=78 et: */