3 * db.c: load and save in-memory database from/to file
4 * copyright (c) 2007-2012 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 */
24 static const unsigned char export_file_header
[] = {0xDA, 0x31, 0x41, 0x59};
25 static const unsigned char export_tag_hosts_ver1
[] = {0xDA, 'H', 'S', 0x01};
26 static const unsigned char export_tag_graph_ver1
[] = {0xDA, 'G', 'R', 0x01};
29 static uint64_t swap64(uint64_t _x
) {
30 /* this is __bswap64 from:
31 * $FreeBSD: src/sys/i386/include/endian.h,v 1.41$
33 return ((_x
>> 56) | ((_x
>> 40) & 0xff00) | ((_x
>> 24) & 0xff0000) |
34 ((_x
>> 8) & 0xff000000) | ((_x
<< 8) & ((uint64_t)0xff << 32)) |
35 ((_x
<< 24) & ((uint64_t)0xff << 40)) |
36 ((_x
<< 40) & ((uint64_t)0xff << 48)) | ((_x
<< 56)));
41 static uint64_t hton64(const uint64_t ho
) {
42 if (ntohs(0x1234) == 0x1234)
51 static const char str
[] = { 0x79,0x74,0x69,0x63,0x6b,0x72,0x65,0x6a };
54 assert(sizeof(no
) == 8);
57 assert(ho
== 8751735851613054314ULL);
58 assert(hton64(ntoh64(no
)) == no
);
61 /* ---------------------------------------------------------------------------
62 * Read-from-file helpers. They all return 0 on failure, and 1 on success.
68 off_t ofs
= lseek(fd
, 0, SEEK_CUR
);
70 err(1, "lseek(0, SEEK_CUR) failed");
71 return (unsigned int)ofs
;
74 /* Read <len> bytes from <fd>, warn() and return 0 on failure,
75 * or return 1 for success.
78 readn(const int fd
, void *dest
, const size_t len
)
82 numread
= read(fd
, dest
, len
);
83 if (numread
== (ssize_t
)len
) return 1;
86 warn("at pos %u: couldn't read %d bytes", xtell(fd
), (int)len
);
88 warnx("at pos %u: tried to read %d bytes, got %d",
89 xtell(fd
), (int)len
, (int)numread
);
95 read8(const int fd
, uint8_t *dest
)
97 assert(sizeof(*dest
) == 1);
98 return readn(fd
, dest
, sizeof(*dest
));
101 /* Read a byte and compare it to the expected data.
102 * Returns 0 on failure or mismatch, 1 on success.
105 expect8(const int fd
, uint8_t expecting
)
109 assert(sizeof(tmp
) == 1);
110 if (!readn(fd
, &tmp
, sizeof(tmp
))) return 0;
111 if (tmp
== expecting
) return 1;
113 warnx("at pos %u: expecting 0x%02x, got 0x%02x",
114 xtell(fd
)-1, expecting
, tmp
);
118 /* Read a network order uint16_t from a file
119 * and store it in host order in memory.
122 read16(const int fd
, uint16_t *dest
)
126 assert(sizeof(tmp
) == 2);
127 if (!read(fd
, &tmp
, sizeof(tmp
))) return 0;
132 /* Read a network order uint32_t from a file
133 * and store it in host order in memory.
136 read32(const int fd
, uint32_t *dest
)
140 assert(sizeof(tmp
) == 4);
141 if (!read(fd
, &tmp
, sizeof(tmp
))) return 0;
146 /* Read an IPv4 addr from a file. This is for backward compatibility with
147 * host records version 1 and 2.
150 readaddr_ipv4(const int fd
, struct addr
*dest
)
153 return readn(fd
, &(dest
->ip
.v4
), sizeof(dest
->ip
.v4
));
156 /* Read a struct addr from a file. Addresses are always stored in network
157 * order, both in the file and in the host's memory (FIXME: is that right?)
160 readaddr(const int fd
, struct addr
*dest
)
162 unsigned char family
;
164 if (!read8(fd
, &family
))
169 return readn(fd
, &(dest
->ip
.v4
), sizeof(dest
->ip
.v4
));
171 else if (family
== 6) {
173 return readn(fd
, dest
->ip
.v6
.s6_addr
, sizeof(dest
->ip
.v6
.s6_addr
));
176 return 0; /* no address family I ever heard of */
179 /* Read a network order uint64_t from a file
180 * and store it in host order in memory.
183 read64(const int fd
, uint64_t *dest
)
187 assert(sizeof(tmp
) == 8);
188 if (!read(fd
, &tmp
, sizeof(tmp
))) return 0;
193 /* ---------------------------------------------------------------------------
194 * Write-to-file helpers. They all return 0 on failure, and 1 on success.
197 /* Write <len> bytes to <fd>, warn() and return 0 on failure,
198 * or return 1 for success.
201 writen(const int fd
, const void *dest
, const size_t len
)
205 numwr
= write(fd
, dest
, len
);
206 if (numwr
== (ssize_t
)len
) return 1;
209 warn("couldn't write %d bytes", (int)len
);
211 warnx("tried to write %d bytes but wrote %d",
212 (int)len
, (int)numwr
);
217 write8(const int fd
, const uint8_t i
)
219 assert(sizeof(i
) == 1);
220 return writen(fd
, &i
, sizeof(i
));
223 /* Given a uint16_t in host order, write it to a file in network order.
226 write16(const int fd
, const uint16_t i
)
228 uint16_t tmp
= htons(i
);
229 assert(sizeof(tmp
) == 2);
230 return writen(fd
, &tmp
, sizeof(tmp
));
233 /* Given a uint32_t in host order, write it to a file in network order.
236 write32(const int fd
, const uint32_t i
)
238 uint32_t tmp
= htonl(i
);
239 assert(sizeof(tmp
) == 4);
240 return writen(fd
, &tmp
, sizeof(tmp
));
243 /* Given a uint64_t in host order, write it to a file in network order.
246 write64(const int fd
, const uint64_t i
)
248 uint64_t tmp
= hton64(i
);
249 assert(sizeof(tmp
) == 8);
250 return writen(fd
, &tmp
, sizeof(tmp
));
254 /* Write the active address part in a struct addr to a file.
255 * Addresses are always stored in network order, both in the file and
256 * in the host's memory (FIXME: is that right?)
259 writeaddr(const int fd
, const struct addr
*const a
)
261 if (!write8(fd
, a
->family
))
264 if (a
->family
== IPv4
)
265 return writen(fd
, &(a
->ip
.v4
), sizeof(a
->ip
.v4
));
267 assert(a
->family
== IPv6
);
268 return writen(fd
, a
->ip
.v6
.s6_addr
, sizeof(a
->ip
.v6
.s6_addr
));
272 /* ---------------------------------------------------------------------------
273 * db import/export code follows.
276 /* Check that the global file header is correct / supported. */
278 read_file_header(const int fd
, const uint8_t expected
[4])
282 if (!readn(fd
, got
, sizeof(got
))) return 0;
284 /* Check the header data */
285 if (memcmp(got
, expected
, sizeof(got
)) != 0) {
287 "expecting %02x%02x%02x%02x, got %02x%02x%02x%02x",
288 expected
[0], expected
[1], expected
[2], expected
[3],
289 got
[0], got
[1], got
[2], got
[3]);
295 /* Returns 0 on failure, 1 on success. */
297 db_import_from_fd(const int fd
)
299 if (!read_file_header(fd
, export_file_header
)) return 0;
300 if (!read_file_header(fd
, export_tag_hosts_ver1
)) return 0;
301 if (!hosts_db_import(fd
)) return 0;
302 if (!read_file_header(fd
, export_tag_graph_ver1
)) return 0;
303 if (!graph_import(fd
)) return 0;
308 db_import(const char *filename
)
310 int fd
= open(filename
, O_RDONLY
| O_NOFOLLOW
);
312 warn("can't import from \"%s\"", filename
);
315 if (!db_import_from_fd(fd
)) {
316 warnx("import failed");
317 /* don't stay in an inconsistent state: */
324 /* Returns 0 on failure, 1 on success. */
326 db_export_to_fd(const int fd
)
328 if (!writen(fd
, export_file_header
, sizeof(export_file_header
)))
330 if (!writen(fd
, export_tag_hosts_ver1
, sizeof(export_tag_hosts_ver1
)))
332 if (!hosts_db_export(fd
))
334 if (!writen(fd
, export_tag_graph_ver1
, sizeof(export_tag_graph_ver1
)))
336 if (!graph_export(fd
))
342 db_export(const char *filename
)
344 int fd
= open(filename
, O_WRONLY
| O_CREAT
| O_NOFOLLOW
| O_TRUNC
, 0600);
346 warn("can't export to \"%s\"", filename
);
349 verbosef("exporting db to file \"%s\"", filename
);
350 if (!db_export_to_fd(fd
))
351 warnx("export failed");
353 verbosef("export successful");
355 /* FIXME: should write to another filename and use the rename() syscall to
356 * atomically update the output file on success
361 /* vim:set ts=3 sw=3 tw=78 et: */