3 * db.c: load and save in-memory database from/to file
4 * copyright (c) 2007-2011 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>
13 #include <netinet/in.h> /* for ntohs() and friends */
25 static const unsigned char export_file_header
[] = {0xDA, 0x31, 0x41, 0x59};
26 static const unsigned char export_tag_hosts_ver1
[] = {0xDA, 'H', 'S', 0x01};
27 static const unsigned char export_tag_graph_ver1
[] = {0xDA, 'G', 'R', 0x01};
30 static uint64_t swap64(uint64_t _x
) {
31 /* this is __bswap64 from:
32 * $FreeBSD: src/sys/i386/include/endian.h,v 1.41$
34 return ((_x
>> 56) | ((_x
>> 40) & 0xff00) | ((_x
>> 24) & 0xff0000) |
35 ((_x
>> 8) & 0xff000000) | ((_x
<< 8) & ((uint64_t)0xff << 32)) |
36 ((_x
<< 24) & ((uint64_t)0xff << 40)) |
37 ((_x
<< 40) & ((uint64_t)0xff << 48)) | ((_x
<< 56)));
42 static inline uint64_t hton64(const uint64_t ho
) {
43 if (ntohs(0x1234) == 0x1234)
52 static const char str
[] = { 0x79,0x74,0x69,0x63,0x6b,0x72,0x65,0x6a };
55 assert(sizeof(no
) == 8);
58 assert(ho
== 8751735851613054314ULL);
59 assert(hton64(ntoh64(no
)) == no
);
62 /* ---------------------------------------------------------------------------
63 * Read-from-file helpers. They all return 0 on failure, and 1 on success.
69 off_t ofs
= lseek(fd
, 0, SEEK_CUR
);
71 err(1, "lseek(0, SEEK_CUR) failed");
72 return (unsigned int)ofs
;
75 /* Read <len> bytes from <fd>, warn() and return 0 on failure,
76 * or return 1 for success.
79 readn(const int fd
, void *dest
, const size_t len
)
83 numread
= read(fd
, dest
, len
);
84 if (numread
== (ssize_t
)len
) return 1;
87 warn("at pos %u: couldn't read %d bytes", xtell(fd
), (int)len
);
89 warnx("at pos %u: tried to read %d bytes, got %d",
90 xtell(fd
), (int)len
, (int)numread
);
96 read8(const int fd
, uint8_t *dest
)
98 assert(sizeof(*dest
) == 1);
99 return readn(fd
, dest
, sizeof(*dest
));
102 /* Read a byte and compare it to the expected data.
103 * Returns 0 on failure or mismatch, 1 on success.
106 expect8(const int fd
, uint8_t expecting
)
110 assert(sizeof(tmp
) == 1);
111 if (!readn(fd
, &tmp
, sizeof(tmp
))) return 0;
112 if (tmp
== expecting
) return 1;
114 warnx("at pos %u: expecting 0x%02x, got 0x%02x",
115 xtell(fd
)-1, expecting
, tmp
);
119 /* Read a network order uint16_t from a file
120 * and store it in host order in memory.
123 read16(const int fd
, uint16_t *dest
)
127 assert(sizeof(tmp
) == 2);
128 if (!read(fd
, &tmp
, sizeof(tmp
))) return 0;
133 /* Read a network order uint32_t from a file
134 * and store it in host order in memory.
137 read32(const int fd
, uint32_t *dest
)
141 assert(sizeof(tmp
) == 4);
142 if (!read(fd
, &tmp
, sizeof(tmp
))) return 0;
147 /* Read an IPv4 addr from a file. This is for backward compatibility with
148 * host records version 1 and 2.
151 readaddr_ipv4(const int fd
, struct addr
*dest
)
154 return readn(fd
, &(dest
->ip
.v4
), sizeof(dest
->ip
.v4
));
157 /* Read a struct addr from a file. Addresses are always stored in network
158 * order, both in the file and in the host's memory (FIXME: is that right?)
161 readaddr(const int fd
, struct addr
*dest
)
163 unsigned char family
;
165 if (!read8(fd
, &family
))
170 return readn(fd
, &(dest
->ip
.v4
), sizeof(dest
->ip
.v4
));
172 else if (family
== 6) {
174 return readn(fd
, dest
->ip
.v6
.s6_addr
, sizeof(dest
->ip
.v6
.s6_addr
));
177 return 0; /* no address family I ever heard of */
180 /* Read a network order uint64_t from a file
181 * and store it in host order in memory.
184 read64(const int fd
, uint64_t *dest
)
188 assert(sizeof(tmp
) == 8);
189 if (!read(fd
, &tmp
, sizeof(tmp
))) return 0;
194 /* ---------------------------------------------------------------------------
195 * Write-to-file helpers. They all return 0 on failure, and 1 on success.
198 /* Write <len> bytes to <fd>, warn() and return 0 on failure,
199 * or return 1 for success.
202 writen(const int fd
, const void *dest
, const size_t len
)
206 numwr
= write(fd
, dest
, len
);
207 if (numwr
== (ssize_t
)len
) return 1;
210 warn("couldn't write %d bytes", (int)len
);
212 warnx("tried to write %d bytes but wrote %d",
213 (int)len
, (int)numwr
);
218 write8(const int fd
, const uint8_t i
)
220 assert(sizeof(i
) == 1);
221 return writen(fd
, &i
, sizeof(i
));
224 /* Given a uint16_t in host order, write it to a file in network order.
227 write16(const int fd
, const uint16_t i
)
229 uint16_t tmp
= htons(i
);
230 assert(sizeof(tmp
) == 2);
231 return writen(fd
, &tmp
, sizeof(tmp
));
234 /* Given a uint32_t in host order, write it to a file in network order.
237 write32(const int fd
, const uint32_t i
)
239 uint32_t tmp
= htonl(i
);
240 assert(sizeof(tmp
) == 4);
241 return writen(fd
, &tmp
, sizeof(tmp
));
244 /* Given a uint64_t in host order, write it to a file in network order.
247 write64(const int fd
, const uint64_t i
)
249 uint64_t tmp
= hton64(i
);
250 assert(sizeof(tmp
) == 8);
251 return writen(fd
, &tmp
, sizeof(tmp
));
255 /* Write the active address part in a struct addr to a file.
256 * Addresses are always stored in network order, both in the file and
257 * in the host's memory (FIXME: is that right?)
260 writeaddr(const int fd
, const struct addr
*const a
)
262 if (!write8(fd
, a
->family
))
265 if (a
->family
== IPv4
)
266 return writen(fd
, &(a
->ip
.v4
), sizeof(a
->ip
.v4
));
268 assert(a
->family
== IPv6
);
269 return writen(fd
, a
->ip
.v6
.s6_addr
, sizeof(a
->ip
.v6
.s6_addr
));
273 /* ---------------------------------------------------------------------------
274 * db import/export code follows.
277 /* Check that the global file header is correct / supported. */
279 read_file_header(const int fd
, const uint8_t expected
[4])
283 if (!readn(fd
, got
, sizeof(got
))) return 0;
285 /* Check the header data */
286 if (memcmp(got
, expected
, sizeof(got
)) != 0) {
288 "expecting %02x%02x%02x%02x, got %02x%02x%02x%02x",
289 expected
[0], expected
[1], expected
[2], expected
[3],
290 got
[0], got
[1], got
[2], got
[3]);
296 /* Returns 0 on failure, 1 on success. */
298 db_import_from_fd(const int fd
)
300 if (!read_file_header(fd
, export_file_header
)) return 0;
301 if (!read_file_header(fd
, export_tag_hosts_ver1
)) return 0;
302 if (!hosts_db_import(fd
)) return 0;
303 if (!read_file_header(fd
, export_tag_graph_ver1
)) return 0;
304 if (!graph_import(fd
)) return 0;
309 db_import(const char *filename
)
311 int fd
= open(filename
, O_RDONLY
| O_NOFOLLOW
);
313 warn("can't import from \"%s\"", filename
);
316 if (!db_import_from_fd(fd
)) {
317 warnx("import failed");
318 /* don't stay in an inconsistent state: */
325 /* Returns 0 on failure, 1 on success. */
327 db_export_to_fd(const int fd
)
329 if (!writen(fd
, export_file_header
, sizeof(export_file_header
)))
331 if (!writen(fd
, export_tag_hosts_ver1
, sizeof(export_tag_hosts_ver1
)))
333 if (!hosts_db_export(fd
))
335 if (!writen(fd
, export_tag_graph_ver1
, sizeof(export_tag_graph_ver1
)))
337 if (!graph_export(fd
))
343 db_export(const char *filename
)
345 int fd
= open(filename
, O_WRONLY
| O_CREAT
| O_NOFOLLOW
| O_TRUNC
, 0600);
347 warn("can't export to \"%s\"", filename
);
350 verbosef("exporting db to file \"%s\"", filename
);
351 if (!db_export_to_fd(fd
))
352 warnx("export failed");
354 verbosef("export successful");
356 /* FIXME: should write to another filename and use the rename() syscall to
357 * atomically update the output file on success
362 /* vim:set ts=3 sw=3 tw=78 et: */