Files
2021-11-10 11:27:58 -05:00

339 lines
11 KiB
C

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
* (c)2019-2021 ZeroTier, Inc.
* https://www.zerotier.com/
*/
#ifndef ZT_LF_DB_H
#define ZT_LF_DB_H
#include "common.h"
#include "vector.h"
#include "mappedfile.h"
#include "suint96.h"
#include "sqlite3/sqlite3.h"
#define ZTLF_DB_GRAPH_NODE_LOCK_ARRAY_SIZE 197
/* NOTE: these reputations must match constants in db.go */
/* Reputation for default good records */
#define ZTLF_DB_REPUTATION_DEFAULT 63
#define ZTLF_DB_REPUTATION_DEFAULT_S "63"
/* Reputation for records that appear to be collisions with other record composite keys */
#define ZTLF_DB_REPUTATION_COLLISION 0
/* commentAssertionRecordCollidesWithClaimedID from Go */
#define ZTLF_DB_COMMENT_ASSERTION_RECORD_COLLIDES_WITH_CLAIMED_ID 1
/**
* Structure making up graph.bin
*
* This packed structure tracks records' weights and links to other records by
* graph node offset. It's stored in little endian format since most systems are
* little endian and this therefore will usually give the best performance. The
* graph.bin file is memory mapped for extremely fast traversal and weight
* adjustment.
*/
ZTLF_PACKED_STRUCT(struct ZTLF_DB_GraphNode
{
uint64_t weightsFileOffset; /* offset of weight in weights "file" */
uint8_t linkCount; /* size of linkedRecordGoff[] */
int64_t linkedRecordGoff[]; /* graph node offsets of linked records or -1 for holes (will be filled later) */
});
/* Big enough for the largest NIST ECC curve, can be increased if needed. */
#define ZTLF_DB_QUERY_MAX_OWNER_SIZE 72
struct ZTLF_DB;
struct ZTLF_QueryResult
{
uint64_t ts;
uint64_t weightL,weightH;
uint64_t doff;
unsigned int dlen;
unsigned int ownerSize;
unsigned int negativeComments;
int localReputation;
uint64_t ckey;
uint8_t owner[ZTLF_DB_QUERY_MAX_OWNER_SIZE];
};
struct ZTLF_QueryResults
{
long count;
struct ZTLF_QueryResult results[1]; /* this is actually variable size, but Go doesn't support [] */
};
struct ZTLF_RecordIndex
{
uint64_t doff;
uint64_t dlen;
int localReputation;
};
struct ZTLF_RecordList
{
long count;
struct ZTLF_RecordIndex records[1]; /* this is actually variable size, but Go doesn't support [] */
};
struct ZTLF_CertificateResults
{
void *certificates;
struct ZTLF_RecordIndex *crls;
unsigned long certificatesLength;
unsigned long crlCount;
};
#define ZTLF_DB_MAX_GRAPH_NODE_SIZE (sizeof(struct ZTLF_DB_GraphNode) + (256 * sizeof(int64_t)))
/**
* Callback for when records are fully synchronized
*
* Parameters are: database, record hash, data offset, data length, reputation, and an arbitrary argument.
*/
typedef void (*RecordSynchronizedCallback)(struct ZTLF_DB *,const void *,uint64_t,unsigned int,int,void *);
/**
* An instance of the LF database (C side)
*/
struct ZTLF_DB
{
char path[PATH_MAX];
LogOutputCallback logger;
RecordSynchronizedCallback recordSyncCallback;
uintptr_t loggerArg;
uintptr_t recordSyncArg;
sqlite3 *dbc;
sqlite3_stmt *sSetConfig;
sqlite3_stmt *sGetConfig;
sqlite3_stmt *sAddRejected;
sqlite3_stmt *sAddRecord;
sqlite3_stmt *sIncRecordLinkedCountByGoff;
sqlite3_stmt *sAddSelector;
sqlite3_stmt *sGetRecordCount;
sqlite3_stmt *sGetDataSize;
sqlite3_stmt *sGetAllRecords;
sqlite3_stmt *sGetAllByOwner;
sqlite3_stmt *sGetAllByIDNotOwner;
sqlite3_stmt *sGetIDOwnerReputation;
sqlite3_stmt *sHaveRecordsWithIDNotOwner;
sqlite3_stmt *sDemoteCollisions;
sqlite3_stmt *sUpdateRecordReputationByHash;
sqlite3_stmt *sGetLinkCandidates;
sqlite3_stmt *sGetRecordByHash;
sqlite3_stmt *sGetMaxRecordDoff;
sqlite3_stmt *sGetMaxRecordGoff;
sqlite3_stmt *sGetRecordGoffByHash;
sqlite3_stmt *sGetRecordInfoByGoff;
sqlite3_stmt *sGetDanglingLinks;
sqlite3_stmt *sDeleteDanglingLinks;
sqlite3_stmt *sDeleteWantedHash;
sqlite3_stmt *sAddDanglingLink;
sqlite3_stmt *sAddWantedHash;
sqlite3_stmt *sAddHole;
sqlite3_stmt *sFlagRecordWeightApplicationPending;
sqlite3_stmt *sGetRecordsForWeightApplication;
sqlite3_stmt *sGetHoles;
sqlite3_stmt *sDeleteHole;
sqlite3_stmt *sUpdatePendingHoleCount;
sqlite3_stmt *sDeleteCompletedPending;
sqlite3_stmt *sGetPendingCount;
sqlite3_stmt *sHaveDanglingLinks;
sqlite3_stmt *sGetWanted;
sqlite3_stmt *sIncWantedRetries;
sqlite3_stmt *sLogComment;
sqlite3_stmt *sGetCommentsBySubjectAndCommentOracle;
sqlite3_stmt *sQueryClearRecordSet;
sqlite3_stmt *sQueryOrSelectorRange;
sqlite3_stmt *sQueryAndSelectorRange;
sqlite3_stmt *sQueryGetResults;
sqlite3_stmt *sPutCert;
sqlite3_stmt *sPutCertRevocation;
sqlite3_stmt *sGetCertsBySubject;
sqlite3_stmt *sGetCertRevocationsByRevokedSerial;
sqlite3_stmt *sMarkInLimbo;
sqlite3_stmt *sTakeFromLimbo;
sqlite3_stmt *sHaveRecordInLimbo;
sqlite3_stmt *sRegisterPulseToken;
sqlite3_stmt *sUpdatePulse;
sqlite3_stmt *sGetPulse;
pthread_mutex_t dbLock;
pthread_mutex_t graphNodeLocks[ZTLF_DB_GRAPH_NODE_LOCK_ARRAY_SIZE]; /* used to lock graph nodes by locking node lock goff % NODE_LOCK_ARRAY_SIZE */
struct ZTLF_MappedFile gf;
pthread_rwlock_t gfLock; /* this is only locked for write when the mapped file's size might be adjusted */
int df;
struct ZTLF_SUInt96 wf;
pthread_t graphThread;
int graphThreadStarted;
int running;
};
int ZTLF_DB_Open(
struct ZTLF_DB *db,
const char *path,
char *errbuf,
unsigned int errbufSize,
LogOutputCallback logger,
void *loggerArg,
RecordSynchronizedCallback recordSync,
void *recordSyncArg);
void ZTLF_DB_Close(struct ZTLF_DB *db);
int ZTLF_DB_PutRecord(
struct ZTLF_DB *db,
const void *rec,
const unsigned int rsize,
const int rtype,
const void *owner,
const unsigned int ownerSize,
const void *hash,
const void *id,
const uint64_t ts,
const uint64_t pulseToken,
const uint32_t score,
const void **selKey,
const unsigned int selCount,
const void *links,
const unsigned int linkCount);
struct ZTLF_QueryResults *ZTLF_DB_Query(
struct ZTLF_DB *db,
const void **sel,
const unsigned int *selSize,
const unsigned int selCount,
const void **oracles,
const unsigned int *oracleSize,
const unsigned int oracleCount);
struct ZTLF_RecordList *ZTLF_DB_GetAllByOwner(struct ZTLF_DB *db,const void *owner,const unsigned int ownerLen);
struct ZTLF_RecordList *ZTLF_DB_GetAllByIDNotOwner(struct ZTLF_DB *db,const void *id,const void *owner,const unsigned int ownerLen);
/* Gets the data offset and data length of a record by its hash (returns length, sets doff and ts). */
unsigned int ZTLF_DB_GetByHash(struct ZTLF_DB *db,const void *hash,uint64_t *doff,uint64_t *ts);
/* Gets up to cnt hashes of records to which a new record should link, returning actual number of links written to lbuf. */
unsigned int ZTLF_DB_GetLinks(struct ZTLF_DB *db,void *const lbuf,unsigned int cnt);
/* This sets a record's reputation */
void ZTLF_DB_UpdateRecordReputationByHash(struct ZTLF_DB *db,const void *const hash,const int reputation);
/* Fill result pointer arguments with statistics about this database. */
void ZTLF_DB_Stats(struct ZTLF_DB *db,uint64_t *recordCount,uint64_t *dataSize);
/* Compute a CRC64 of all record hashes and their weights in deterministic order (for testing and consistency checking) */
uint64_t ZTLF_DB_CRC64(struct ZTLF_DB *db);
/* -1: no records at all, 0: no pending, 1: pending records */
int ZTLF_DB_HasPending(struct ZTLF_DB *db);
/* returns non-zero if we have dangling links that haven't been retried more than N times */
int ZTLF_DB_HaveDanglingLinks(struct ZTLF_DB *db,int ignoreWantedAfterNRetries);
/* gets wanted hashes, returns count of hashes. buf must have enough space for up to maxHashes hashes. */
unsigned int ZTLF_DB_GetWanted(struct ZTLF_DB *db,void *buf,const unsigned int maxHashes,const unsigned int retryCountMin,const unsigned int retryCountMax,const int incrementRetryCount);
/* log commentary */
int ZTLF_DB_LogComment(struct ZTLF_DB *db,const int64_t byRecordDoff,const int assertion,const int reason,const void *const subject,const int subjectLen);
int ZTLF_DB_SetConfig(struct ZTLF_DB *db,const char *key,const void *value,const unsigned int vlen);
unsigned int ZTLF_DB_GetConfig(struct ZTLF_DB *db,const char *key,void *value,const unsigned int valueMaxLen);
static inline const char *ZTLF_DB_LastSqliteErrorMessage(struct ZTLF_DB *db) { return sqlite3_errmsg(db->dbc); }
static inline int ZTLF_DB_GetRecordData(struct ZTLF_DB *db,uint64_t doff,void *data,unsigned int dlen)
{
if (db->df >= 0) {
if ((long)pread(db->df,data,(size_t)dlen,(off_t)doff) == (long)dlen)
return 1;
}
return 0;
}
int ZTLF_DB_PutCert(
struct ZTLF_DB *db,
const char *serial,
const char *subjectSerial,
const uint64_t recordDoff,
const void *cert,
const unsigned int certLen);
int ZTLF_DB_PutCertRevocation(
struct ZTLF_DB *db,
const char *revokedSerialNumber,
const uint64_t recordDoff,
const unsigned int recordDlen);
struct ZTLF_CertificateResults *ZTLF_DB_GetCertInfo(struct ZTLF_DB *db,const char *subjectSerial);
static inline void ZTLF_DB_FreeCertificateResults(struct ZTLF_CertificateResults *cr)
{
if (cr) {
if (cr->certificates)
free((void *)cr->certificates);
if (cr->crls)
free((void *)cr->crls);
free((void *)cr);
}
}
int ZTLF_DB_MarkInLimbo(struct ZTLF_DB *db,const void *hash,const void *owner,const unsigned int ownerSize,const uint64_t localReceiveTime,const uint64_t ts);
int ZTLF_DB_HaveRecordIncludeLimbo(struct ZTLF_DB *db,const void *hash);
int ZTLF_DB_UpdatePulse(struct ZTLF_DB *db,const uint64_t token,const uint64_t minutes,const uint64_t startRangeStart,const uint64_t startRangeEnd);
uint64_t ZTLF_DB_GetPulse(struct ZTLF_DB *db,const uint64_t token);
/* Golang-specific shims to get around some inconvenient aspects of cgo */
#ifdef ZTLF_GOLANG
static inline int ZTLF_DB_Open_fromGo(struct ZTLF_DB *db,const char *path,char *errbuf,unsigned int errbufSize,uintptr_t loggerArg,uintptr_t syncCallbackArg)
{
return ZTLF_DB_Open(db,path,errbuf,errbufSize,&ztlfLogOutputCCallback,(void *)loggerArg,&ztlfSyncCCallback,(void *)syncCallbackArg);
}
static inline int ZTLF_DB_PutRecord_fromGo(
struct ZTLF_DB *db,
const void *rec,
const unsigned int rsize,
const int rtype,
const void *owner,
const unsigned int ownerSize,
const void *hash,
const void *id,
const uint64_t ts,
const uint64_t pulseToken,
const uint32_t score,
const uintptr_t selKey,
const unsigned int selCount,
const void *links,
const unsigned int linkCount)
{
return ZTLF_DB_PutRecord(db,rec,rsize,rtype,owner,ownerSize,hash,id,ts,pulseToken,score,(const void **)selKey,selCount,links,linkCount);
}
static inline struct ZTLF_QueryResults *ZTLF_DB_Query_fromGo(
struct ZTLF_DB *db,
const uintptr_t sel,
const unsigned int *selSize,
const unsigned int selCount,
const uintptr_t oracles,
const unsigned int *oracleSize,
const unsigned int oracleCount)
{
return ZTLF_DB_Query(db,(const void **)sel,selSize,selCount,(const void **)oracles,oracleSize,oracleCount);
}
#endif
#endif