mirror of
https://github.com/irungentoo/toxcore.git
synced 2024-03-22 13:30:51 +08:00
State loading/saving: Instead of a blob, segment into sections marked with tags.
Primary rationale: The part that DHT saves changes if IP is expanded to IPv6. To let people keep their friends/name, change the datafile format now, while everybody is still on the same page. Loading/Saving rewritten to allow a part of the file to be incomprehensible to the loading routine. Added a magic cookie at the beginning to mark the file as tox's. Changes in some part of the datafile can be skipped and the remaining parts still be consumed. Allows a wide margin of forward compatibility (like the IP to IPv6 transition, but also e.g. a change in the key format). As long as the file is not completely garbled, the routine will read as much as possible. Only the KEY section is considered mandatory: a malformed key section leads to a negative result. util.*: - holds the driving function which jumps from section to section and calls back with section length and tag (type) Messenger.c,DHT.*: - new loading functions call the util-function with a callback, which subsequently consumes the sections - old routines are kept to fall back onto if the magic cookie at the beginning isn't present - saving is still done in one local routine
This commit is contained in:
parent
b37b521cd5
commit
87f5f9d4eb
132
toxcore/DHT.c
132
toxcore/DHT.c
|
@ -1236,13 +1236,13 @@ void kill_DHT(DHT *dht)
|
|||
}
|
||||
|
||||
/* Get the size of the DHT (for saving). */
|
||||
uint32_t DHT_size(DHT *dht)
|
||||
uint32_t DHT_size_old(DHT *dht)
|
||||
{
|
||||
return sizeof(dht->close_clientlist) + sizeof(DHT_Friend) * dht->num_friends;
|
||||
}
|
||||
|
||||
/* Save the DHT in data where data is an array of size DHT_size(). */
|
||||
void DHT_save(DHT *dht, uint8_t *data)
|
||||
void DHT_save_old(DHT *dht, uint8_t *data)
|
||||
{
|
||||
memcpy(data, dht->close_clientlist, sizeof(dht->close_clientlist));
|
||||
memcpy(data + sizeof(dht->close_clientlist), dht->friends_list, sizeof(DHT_Friend) * dht->num_friends);
|
||||
|
@ -1253,7 +1253,7 @@ void DHT_save(DHT *dht, uint8_t *data)
|
|||
* return -1 if failure.
|
||||
* return 0 if success.
|
||||
*/
|
||||
int DHT_load(DHT *dht, uint8_t *data, uint32_t size)
|
||||
int DHT_load_old(DHT *dht, uint8_t *data, uint32_t size)
|
||||
{
|
||||
if (size < sizeof(dht->close_clientlist))
|
||||
return -1;
|
||||
|
@ -1295,6 +1295,132 @@ int DHT_load(DHT *dht, uint8_t *data, uint32_t size)
|
|||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* new DHT format for load/save, more robust and forward compatible */
|
||||
|
||||
#define DHT_STATE_COOKIE_GLOBAL 0x159000d
|
||||
|
||||
#define DHT_STATE_COOKIE_TYPE 0x11ce
|
||||
#define DHT_STATE_TYPE_FRIENDS 1
|
||||
#define DHT_STATE_TYPE_CLIENTS 2
|
||||
|
||||
typedef uint16_t statelensub_t;
|
||||
|
||||
/* Get the size of the DHT (for saving). */
|
||||
uint32_t DHT_size(DHT *dht)
|
||||
{
|
||||
uint32_t num = 0, i;
|
||||
for (i = 0; i < LCLIENT_LIST; ++i)
|
||||
if (dht->close_clientlist[i].timestamp != 0)
|
||||
num++;
|
||||
|
||||
uint32_t size32 = sizeof(uint32_t), lengthsublen = sizeof(statelensub_t);
|
||||
uint32_t sizesubhead = lengthsublen + size32;
|
||||
return size32
|
||||
+ sizesubhead + sizeof(DHT_Friend) * dht->num_friends
|
||||
+ sizesubhead + sizeof(Client_data) * num;
|
||||
}
|
||||
|
||||
static uint8_t *z_state_save_subheader(uint8_t *data, statelensub_t len, uint16_t type)
|
||||
{
|
||||
*(statelensub_t *)data = len;
|
||||
data += sizeof(statelensub_t);
|
||||
*(uint32_t *)data = (DHT_STATE_COOKIE_TYPE << 16) | type;
|
||||
data += sizeof(uint32_t);
|
||||
return data;
|
||||
}
|
||||
|
||||
/* Save the DHT in data where data is an array of size DHT_size(). */
|
||||
void DHT_save(DHT *dht, uint8_t *data)
|
||||
{
|
||||
statelensub_t len;
|
||||
uint16_t type;
|
||||
*(uint32_t *)data = DHT_STATE_COOKIE_GLOBAL;
|
||||
data += sizeof(uint32_t);
|
||||
|
||||
len = sizeof(DHT_Friend) * dht->num_friends;
|
||||
type = DHT_STATE_TYPE_FRIENDS;
|
||||
data = z_state_save_subheader(data, len, type);
|
||||
memcpy(data, dht->friends_list, len);
|
||||
data += len;
|
||||
|
||||
uint32_t num = 0, i;
|
||||
for (i = 0; i < LCLIENT_LIST; ++i)
|
||||
if (dht->close_clientlist[i].timestamp != 0)
|
||||
num++;
|
||||
|
||||
if (!num)
|
||||
return;
|
||||
|
||||
len = num * sizeof(Client_data);
|
||||
type = DHT_STATE_TYPE_CLIENTS;
|
||||
data = z_state_save_subheader(data, len, type);
|
||||
Client_data *clients = (Client_data *)data;
|
||||
for (num = 0, i = 0; i < LCLIENT_LIST; ++i)
|
||||
if (dht->close_clientlist[i].timestamp != 0)
|
||||
memcpy(&clients[num++], &dht->close_clientlist[i], sizeof(Client_data));
|
||||
data += len;
|
||||
}
|
||||
|
||||
static int dht_load_state_callback(void *outer, uint8_t *data, uint32_t length, uint16_t type)
|
||||
{
|
||||
DHT *dht = outer;
|
||||
uint32_t num, i, j;
|
||||
switch(type) {
|
||||
case DHT_STATE_TYPE_FRIENDS:
|
||||
if (length % sizeof(DHT_Friend) != 0)
|
||||
break;
|
||||
|
||||
DHT_Friend *friend_list = (DHT_Friend *)data;
|
||||
num = length / sizeof(DHT_Friend);
|
||||
for (i = 0; i < num; ++i) {
|
||||
DHT_addfriend(dht, friend_list[i].client_id);
|
||||
for (j = 0; j < MAX_FRIEND_CLIENTS; ++j) {
|
||||
Client_data *client = &friend_list[i].client_list[j];
|
||||
if (client->timestamp != 0)
|
||||
getnodes(dht, client->ip_port, client->client_id, friend_list[i].client_id);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case DHT_STATE_TYPE_CLIENTS:
|
||||
if ((length % sizeof(Client_data)) != 0)
|
||||
break;
|
||||
|
||||
num = length / sizeof(Client_data);
|
||||
Client_data *client_list = (Client_data *)data;
|
||||
for (i = 0; i < num; ++i)
|
||||
if (client_list[i].timestamp != 0)
|
||||
DHT_bootstrap(dht, client_list[i].ip_port, client_list[i].client_id);
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
fprintf(stderr, "Load state (DHT): contains unrecognized part (len %u, type %u)\n",
|
||||
length, type);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Load the DHT from data of size size.
|
||||
*
|
||||
* return -1 if failure.
|
||||
* return 0 if success.
|
||||
*/
|
||||
int DHT_load_new(DHT *dht, uint8_t *data, uint32_t length)
|
||||
{
|
||||
uint32_t cookie_len = sizeof(uint32_t);
|
||||
if (length > cookie_len) {
|
||||
uint32_t *data32 = (uint32_t *)data;
|
||||
if (data32[0] == DHT_STATE_COOKIE_GLOBAL)
|
||||
return load_state(dht_load_state_callback, dht, data + cookie_len,
|
||||
length - cookie_len, DHT_STATE_COOKIE_TYPE);
|
||||
}
|
||||
|
||||
return DHT_load_old(dht, data, length);
|
||||
}
|
||||
/* return 0 if we are not connected to the DHT.
|
||||
* return 1 if we are.
|
||||
*/
|
||||
|
|
|
@ -188,11 +188,13 @@ DHT *new_DHT(Net_Crypto *c);
|
|||
void kill_DHT(DHT *dht);
|
||||
|
||||
/* Load the DHT from data of size size.
|
||||
* old/new: version of config file
|
||||
*
|
||||
* return -1 if failure.
|
||||
* return 0 if success.
|
||||
*/
|
||||
int DHT_load(DHT *dht, uint8_t *data, uint32_t size);
|
||||
int DHT_load_old(DHT *dht, uint8_t *data, uint32_t size);
|
||||
int DHT_load_new(DHT *dht, uint8_t *data, uint32_t size);
|
||||
|
||||
/* return 0 if we are not connected to the DHT.
|
||||
* return 1 if we are.
|
||||
|
|
|
@ -1120,7 +1120,7 @@ void doMessenger(Messenger *m)
|
|||
}
|
||||
|
||||
/* return size of the messenger data (for saving) */
|
||||
uint32_t Messenger_size(Messenger *m)
|
||||
uint32_t Messenger_size_old(Messenger *m)
|
||||
{
|
||||
return crypto_box_PUBLICKEYBYTES + crypto_box_SECRETKEYBYTES
|
||||
+ sizeof(uint32_t) // nospam.
|
||||
|
@ -1133,8 +1133,8 @@ uint32_t Messenger_size(Messenger *m)
|
|||
;
|
||||
}
|
||||
|
||||
/* Save the messenger in data of size Messenger_size(). */
|
||||
void Messenger_save(Messenger *m, uint8_t *data)
|
||||
/* Save the messenger in data of size Messenger_size(). Old version without cookies. */
|
||||
static void Messenger_save_old(Messenger *m, uint8_t *data)
|
||||
{
|
||||
save_keys(m->net_crypto, data);
|
||||
data += crypto_box_PUBLICKEYBYTES + crypto_box_SECRETKEYBYTES;
|
||||
|
@ -1157,8 +1157,8 @@ void Messenger_save(Messenger *m, uint8_t *data)
|
|||
memcpy(data, m->name, small_size);
|
||||
}
|
||||
|
||||
/* Load the messenger from data of size length. */
|
||||
int Messenger_load(Messenger *m, uint8_t *data, uint32_t length)
|
||||
/* Load the messenger from data of size length. Old version without cookies. */
|
||||
static int Messenger_load_old(Messenger *m, uint8_t *data, uint32_t length)
|
||||
{
|
||||
if (length == ~((uint32_t)0))
|
||||
return -1;
|
||||
|
@ -1182,8 +1182,9 @@ int Messenger_load(Messenger *m, uint8_t *data, uint32_t length)
|
|||
|
||||
length -= size;
|
||||
|
||||
if (DHT_load(m->dht, data, size) == -1)
|
||||
return -1;
|
||||
if (DHT_load_old(m->dht, data, size) == -1)
|
||||
fprintf(stderr, "Data file: Something wicked happened to the stored connections...\n");
|
||||
/* DO go on, friends/name still might be intact */
|
||||
|
||||
data += size;
|
||||
memcpy(&size, data, sizeof(size));
|
||||
|
@ -1236,6 +1237,153 @@ int Messenger_load(Messenger *m, uint8_t *data, uint32_t length)
|
|||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* new messenger format for load/save, more robust and forward compatible */
|
||||
|
||||
#define MESSENGER_STATE_COOKIE_GLOBAL 0x15ed1b1e
|
||||
|
||||
#define MESSENGER_STATE_COOKIE_TYPE 0x01ce
|
||||
#define MESSENGER_STATE_TYPE_NOSPAMKEYS 1
|
||||
#define MESSENGER_STATE_TYPE_DHT 2
|
||||
#define MESSENGER_STATE_TYPE_FRIENDS 3
|
||||
#define MESSENGER_STATE_TYPE_NAME 4
|
||||
|
||||
typedef uint16_t statelensub_t;
|
||||
|
||||
/* return size of the messenger data (for saving) */
|
||||
uint32_t Messenger_size(Messenger *m)
|
||||
{
|
||||
uint32_t size32 = sizeof(uint32_t), lengthsublen = sizeof(statelensub_t);
|
||||
uint32_t sizesubhead = lengthsublen + size32;
|
||||
return size32 * 2 // global cookie
|
||||
+ sizesubhead + sizeof(uint32_t) + crypto_box_PUBLICKEYBYTES + crypto_box_SECRETKEYBYTES
|
||||
+ sizesubhead + DHT_size(m->dht) // DHT
|
||||
+ sizesubhead + sizeof(Friend) * m->numfriends // Friendlist itself.
|
||||
+ sizesubhead + m->name_length // Own nickname.
|
||||
;
|
||||
}
|
||||
|
||||
static uint8_t *z_state_save_subheader(uint8_t *data, statelensub_t len, uint16_t type)
|
||||
{
|
||||
*(statelensub_t *)data = len;
|
||||
data += sizeof(statelensub_t);
|
||||
*(uint32_t *)data = (MESSENGER_STATE_COOKIE_TYPE << 16) | type;
|
||||
data += sizeof(uint32_t);
|
||||
return data;
|
||||
}
|
||||
|
||||
/* Save the messenger in data of size Messenger_size(). */
|
||||
void Messenger_save(Messenger *m, uint8_t *data)
|
||||
{
|
||||
statelensub_t len;
|
||||
uint16_t type;
|
||||
uint32_t *data32, size32 = sizeof(uint32_t);
|
||||
|
||||
data32 = (uint32_t *)data;
|
||||
data32[0] = 0;
|
||||
data32[1] = MESSENGER_STATE_COOKIE_GLOBAL;
|
||||
data += size32 * 2;
|
||||
|
||||
#ifdef DEBUG
|
||||
assert(sizeof(get_nospam(&(m->fr))) == sizeof(uint32_t));
|
||||
#endif
|
||||
len = size32 + crypto_box_PUBLICKEYBYTES + crypto_box_SECRETKEYBYTES;
|
||||
type = MESSENGER_STATE_TYPE_NOSPAMKEYS;
|
||||
data = z_state_save_subheader(data, len, type);
|
||||
*(uint32_t *)data = get_nospam(&(m->fr));
|
||||
save_keys(m->net_crypto, data + size32);
|
||||
data += len;
|
||||
|
||||
len = DHT_size(m->dht);
|
||||
type = MESSENGER_STATE_TYPE_DHT;
|
||||
data = z_state_save_subheader(data, len, type);
|
||||
DHT_save(m->dht, data);
|
||||
data += len;
|
||||
|
||||
len = sizeof(Friend) * m->numfriends;
|
||||
type = MESSENGER_STATE_TYPE_FRIENDS;
|
||||
data = z_state_save_subheader(data, len, type);
|
||||
memcpy(data, m->friendlist, len);
|
||||
data += len;
|
||||
|
||||
len = m->name_length;
|
||||
type = MESSENGER_STATE_TYPE_NAME;
|
||||
data = z_state_save_subheader(data, len, type);
|
||||
memcpy(data, m->name, len);
|
||||
data += len;
|
||||
}
|
||||
|
||||
static int messenger_load_state_callback(void *outer, uint8_t *data, uint32_t length, uint16_t type)
|
||||
{
|
||||
Messenger *m = outer;
|
||||
switch(type) {
|
||||
case MESSENGER_STATE_TYPE_NOSPAMKEYS:
|
||||
if (length == crypto_box_PUBLICKEYBYTES + crypto_box_SECRETKEYBYTES + sizeof(uint32_t)) {
|
||||
set_nospam(&(m->fr), *(uint32_t *)data);
|
||||
load_keys(m->net_crypto, &data[sizeof(uint32_t)]);
|
||||
}
|
||||
else
|
||||
return -1; /* critical */
|
||||
break;
|
||||
|
||||
case MESSENGER_STATE_TYPE_DHT:
|
||||
DHT_load_new(m->dht, data, length);
|
||||
break;
|
||||
|
||||
case MESSENGER_STATE_TYPE_FRIENDS:
|
||||
if (!(length % sizeof(Friend))) {
|
||||
uint16_t num = length / sizeof(Friend);
|
||||
Friend *friends = (Friend *)data;
|
||||
uint32_t i;
|
||||
for (i = 0; i < num; ++i) {
|
||||
if (friends[i].status >= 3) {
|
||||
int fnum = m_addfriend_norequest(m, friends[i].client_id);
|
||||
setfriendname(m, fnum, friends[i].name, friends[i].name_length);
|
||||
/* set_friend_statusmessage(fnum, temp[i].statusmessage, temp[i].statusmessage_length); */
|
||||
} else if (friends[i].status != 0) {
|
||||
/* TODO: This is not a good way to do this. */
|
||||
uint8_t address[FRIEND_ADDRESS_SIZE];
|
||||
memcpy(address, friends[i].client_id, crypto_box_PUBLICKEYBYTES);
|
||||
memcpy(address + crypto_box_PUBLICKEYBYTES, &(friends[i].friendrequest_nospam), sizeof(uint32_t));
|
||||
uint16_t checksum = address_checksum(address, FRIEND_ADDRESS_SIZE - sizeof(checksum));
|
||||
memcpy(address + crypto_box_PUBLICKEYBYTES + sizeof(uint32_t), &checksum, sizeof(checksum));
|
||||
m_addfriend(m, address, friends[i].info, friends[i].info_size);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case MESSENGER_STATE_TYPE_NAME:
|
||||
if ((length > 0) && (length < MAX_NAME_LENGTH)) {
|
||||
setname(m, data, length);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
fprintf(stderr, "Load state: contains unrecognized part (len %u, type %u)\n",
|
||||
length, type);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Load the messenger from data of size length. */
|
||||
int Messenger_load(Messenger *m, uint8_t *data, uint32_t length)
|
||||
{
|
||||
uint32_t cookie_len = 2 * sizeof(uint32_t);
|
||||
if (length > cookie_len) {
|
||||
uint32_t *data32 = (uint32_t *)data;
|
||||
if (!data32[0] && (data32[1] == MESSENGER_STATE_COOKIE_GLOBAL)) {
|
||||
return load_state(messenger_load_state_callback, m, data + cookie_len,
|
||||
length - cookie_len, MESSENGER_STATE_COOKIE_TYPE);
|
||||
}
|
||||
|
||||
/* old state file or too short */
|
||||
}
|
||||
|
||||
return Messenger_load_old(m, data, length);
|
||||
}
|
||||
|
||||
/* Allocate and return a list of valid friend id's. List must be freed by the
|
||||
* caller.
|
||||
*
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include <stdbool.h>
|
||||
|
||||
#include "DHT.h"
|
||||
#include "util.h"
|
||||
|
||||
uint64_t now()
|
||||
{
|
||||
|
@ -46,3 +47,44 @@ void id_cpy(uint8_t *dest, uint8_t *src)
|
|||
{
|
||||
memcpy(dest, src, CLIENT_ID_SIZE);
|
||||
}
|
||||
|
||||
int load_state(load_state_callback_func load_state_callback, void *outer,
|
||||
uint8_t *data, uint32_t length, uint16_t cookie_inner)
|
||||
{
|
||||
if (!load_state_callback || !data) {
|
||||
fprintf(stderr, "load_state() called with invalid args.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
state_length_sub_t length_sub;
|
||||
uint16_t type;
|
||||
uint32_t size32 = sizeof(uint32_t), length_sub_len = sizeof(state_length_sub_t);
|
||||
uint32_t size_head = length_sub_len + size32, cookie_type;
|
||||
while (length > size_head) {
|
||||
length_sub = *(state_length_sub_t *)data;
|
||||
cookie_type = *(uint32_t *)(data + length_sub_len);
|
||||
data += size_head;
|
||||
length -= size_head;
|
||||
|
||||
if (length < length_sub) {
|
||||
/* file truncated */
|
||||
fprintf(stderr, "state file too short: %u < %u\n", length, length_sub);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((cookie_type >> 16) != cookie_inner) {
|
||||
/* something is not matching up in a bad way, give up */
|
||||
fprintf(stderr, "state file garbeled: %04hx != %04hx\n", (cookie_type >> 16), cookie_inner);
|
||||
return -1;
|
||||
}
|
||||
|
||||
type = cookie_type & 0xFFFF;
|
||||
if (-1 == load_state_callback(outer, data, length_sub, type))
|
||||
return -1;
|
||||
|
||||
data += length_sub;
|
||||
length -= length_sub;
|
||||
}
|
||||
|
||||
return length == 0 ? 0 : - 1;
|
||||
};
|
||||
|
|
|
@ -5,8 +5,18 @@
|
|||
* Copyright 2013 plutooo
|
||||
*/
|
||||
|
||||
#ifndef __UTIL_H__
|
||||
#define __UTIL_H__
|
||||
|
||||
uint64_t now();
|
||||
uint64_t random_64b();
|
||||
bool ipp_eq(IP_Port a, IP_Port b);
|
||||
bool id_eq(uint8_t *dest, uint8_t *src);
|
||||
void id_cpy(uint8_t *dest, uint8_t *src);
|
||||
|
||||
typedef uint16_t state_length_sub_t;
|
||||
typedef int (*load_state_callback_func)(void *outer, uint8_t *data, uint32_t len, uint16_t type);
|
||||
int load_state(load_state_callback_func load_state_callback, void *outer,
|
||||
uint8_t *data, uint32_t length, uint16_t cookie_inner);
|
||||
|
||||
#endif /* __UTIL_H__ */
|
||||
|
|
Loading…
Reference in New Issue
Block a user