5a82d90967e7769fe0854a56499e8bc8dc6788bd
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 inline uint64_t
33 /* this is __bswap64 from:
34 * $FreeBSD: src/sys/i386/include/endian.h,v 1.41$
36 return ((_x
>> 56) | ((_x
>> 40) & 0xff00) | ((_x
>> 24) & 0xff0000) |
37 ((_x
>> 8) & 0xff000000) | ((_x
<< 8) & ((uint64_t)0xff << 32)) |
38 ((_x
<< 24) & ((uint64_t)0xff << 40)) |
39 ((_x
<< 40) & ((uint64_t)0xff << 48)) | ((_x
<< 56)));
44 hton64(const uint64_t ho
)
46 if (ntohs(0x1234) == 0x1234) return ho
;
47 else return swap64(ho
);
51 ntoh64(const uint64_t no
)
59 static const char str
[] = { 0x79,0x74,0x69,0x63,0x6b,0x72,0x65,0x6a };
62 assert(sizeof(no
) == 8);
65 assert(ho
== 8751735851613054314ULL);
66 assert(hton64(ntoh64(no
)) == no
);
69 /* ---------------------------------------------------------------------------
70 * Read-from-file helpers. They all return 0 on failure, and 1 on success.
76 off_t ofs
= lseek(fd
, 0, SEEK_CUR
);
78 err(1, "lseek(0, SEEK_CUR) failed");
79 return (unsigned int)ofs
;
82 /* Read <len> bytes from <fd>, warn() and return 0 on failure,
83 * or return 1 for success.
86 readn(const int fd
, void *dest
, const size_t len
)
90 numread
= read(fd
, dest
, len
);
91 if (numread
== (ssize_t
)len
) return 1;
94 warn("at pos %u: couldn't read %d bytes", xtell(fd
), (int)len
);
96 warnx("at pos %u: tried to read %d bytes, got %d",
97 xtell(fd
), (int)len
, (int)numread
);
103 read8(const int fd
, uint8_t *dest
)
105 assert(sizeof(*dest
) == 1);
106 return readn(fd
, dest
, sizeof(*dest
));
109 /* Read a byte and compare it to the expected data.
110 * Returns 0 on failure or mismatch, 1 on success.
113 expect8(const int fd
, uint8_t expecting
)
117 assert(sizeof(tmp
) == 1);
118 if (!readn(fd
, &tmp
, sizeof(tmp
))) return 0;
119 if (tmp
== expecting
) return 1;
121 warnx("at pos %u: expecting 0x%02x, got 0x%02x",
122 xtell(fd
)-1, expecting
, tmp
);
126 /* Read a network order uint16_t from a file
127 * and store it in host order in memory.
130 read16(const int fd
, uint16_t *dest
)
134 assert(sizeof(tmp
) == 2);
135 if (!read(fd
, &tmp
, sizeof(tmp
))) return 0;
140 /* Read a network order uint32_t from a file
141 * and store it in host order in memory.
144 read32(const int fd
, uint32_t *dest
)
148 assert(sizeof(tmp
) == 4);
149 if (!read(fd
, &tmp
, sizeof(tmp
))) return 0;
154 /* Read an IPv4 addr from a file. This is for backward compatibility with
155 * host records version 1 and 2.
158 readaddr_ipv4(const int fd
, struct addr
*dest
)
161 return readn(fd
, &(dest
->ip
.v4
), sizeof(dest
->ip
.v4
));
164 /* Read a struct addr from a file. Addresses are always stored in network
165 * order, both in the file and in the host's memory (FIXME: is that right?)
168 readaddr(const int fd
, struct addr
*dest
)
170 unsigned char family
;
172 if (!read8(fd
, &family
))
177 return readn(fd
, &(dest
->ip
.v4
), sizeof(dest
->ip
.v4
));
179 else if (family
== 6) {
181 return readn(fd
, dest
->ip
.v6
.s6_addr
, sizeof(dest
->ip
.v6
.s6_addr
));
184 return 0; /* no address family I ever heard of */
187 /* Read a network order uint64_t from a file
188 * and store it in host order in memory.
191 read64(const int fd
, uint64_t *dest
)
195 assert(sizeof(tmp
) == 8);
196 if (!read(fd
, &tmp
, sizeof(tmp
))) return 0;
201 /* ---------------------------------------------------------------------------
202 * Write-to-file helpers. They all return 0 on failure, and 1 on success.
205 /* Write <len> bytes to <fd>, warn() and return 0 on failure,
206 * or return 1 for success.
209 writen(const int fd
, const void *dest
, const size_t len
)
213 numwr
= write(fd
, dest
, len
);
214 if (numwr
== (ssize_t
)len
) return 1;
217 warn("couldn't write %d bytes", (int)len
);
219 warnx("tried to write %d bytes but wrote %d",
220 (int)len
, (int)numwr
);
225 write8(const int fd
, const uint8_t i
)
227 assert(sizeof(i
) == 1);
228 return writen(fd
, &i
, sizeof(i
));
231 /* Given a uint16_t in host order, write it to a file in network order.
234 write16(const int fd
, const uint16_t i
)
236 uint16_t tmp
= htons(i
);
237 assert(sizeof(tmp
) == 2);
238 return writen(fd
, &tmp
, sizeof(tmp
));
241 /* Given a uint32_t in host order, write it to a file in network order.
244 write32(const int fd
, const uint32_t i
)
246 uint32_t tmp
= htonl(i
);
247 assert(sizeof(tmp
) == 4);
248 return writen(fd
, &tmp
, sizeof(tmp
));
251 /* Given a uint64_t in host order, write it to a file in network order.
254 write64(const int fd
, const uint64_t i
)
256 uint64_t tmp
= hton64(i
);
257 assert(sizeof(tmp
) == 8);
258 return writen(fd
, &tmp
, sizeof(tmp
));
262 /* Write the active address part in a struct addr to a file.
263 * Addresses are always stored in network order, both in the file and
264 * in the host's memory (FIXME: is that right?)
267 writeaddr(const int fd
, const struct addr
*const a
)
269 if (!write8(fd
, a
->family
))
272 if (a
->family
== IPv4
)
273 return writen(fd
, &(a
->ip
.v4
), sizeof(a
->ip
.v4
));
275 assert(a
->family
== IPv6
);
276 return writen(fd
, a
->ip
.v6
.s6_addr
, sizeof(a
->ip
.v6
.s6_addr
));
280 /* ---------------------------------------------------------------------------
281 * db import/export code follows.
284 /* Check that the global file header is correct / supported. */
286 read_file_header(const int fd
, const uint8_t expected
[4])
290 if (!readn(fd
, got
, sizeof(got
))) return 0;
292 /* Check the header data */
293 if (memcmp(got
, expected
, sizeof(got
)) != 0) {
295 "expecting %02x%02x%02x%02x, got %02x%02x%02x%02x",
296 expected
[0], expected
[1], expected
[2], expected
[3],
297 got
[0], got
[1], got
[2], got
[3]);
303 /* Returns 0 on failure, 1 on success. */
305 db_import_from_fd(const int fd
)
307 if (!read_file_header(fd
, export_file_header
)) return 0;
308 if (!read_file_header(fd
, export_tag_hosts_ver1
)) return 0;
309 if (!hosts_db_import(fd
)) return 0;
310 if (!read_file_header(fd
, export_tag_graph_ver1
)) return 0;
311 if (!graph_import(fd
)) return 0;
316 db_import(const char *filename
)
318 int fd
= open(filename
, O_RDONLY
| O_NOFOLLOW
);
320 warn("can't import from \"%s\"", filename
);
323 if (!db_import_from_fd(fd
)) {
324 warnx("import failed");
325 /* don't stay in an inconsistent state: */
332 /* Returns 0 on failure, 1 on success. */
334 db_export_to_fd(const int fd
)
336 if (!writen(fd
, export_file_header
, sizeof(export_file_header
)))
338 if (!writen(fd
, export_tag_hosts_ver1
, sizeof(export_tag_hosts_ver1
)))
340 if (!hosts_db_export(fd
))
342 if (!writen(fd
, export_tag_graph_ver1
, sizeof(export_tag_graph_ver1
)))
344 if (!graph_export(fd
))
350 db_export(const char *filename
)
352 int fd
= open(filename
, O_WRONLY
| O_CREAT
| O_NOFOLLOW
| O_TRUNC
, 0600);
354 warn("can't export to \"%s\"", filename
);
357 verbosef("exporting db to file \"%s\"", filename
);
358 if (!db_export_to_fd(fd
))
359 warnx("export failed");
361 verbosef("export successful");
363 /* FIXME: should write to another filename and use the rename() syscall to
364 * atomically update the output file on success
369 /* vim:set ts=3 sw=3 tw=78 et: */