blob: 38574eca429b4ee28c4375b082477c8f96e933cd [file] [log] [blame]
// Copyright 2017 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "garnet/bin/network_time/time_server_config.h"
#include <string>
#include <vector>
#include <fstream>
#include <sstream>
#include <protocol.h>
#include <rapidjson/document.h>
#include <rapidjson/error/en.h>
#include <rapidjson/schema.h>
#include <rapidjson/stringbuffer.h>
#include "lib/syslog/cpp/logger.h"
#define MULTILINE(...) #__VA_ARGS__
namespace time_server {
const char* config_schema = MULTILINE({
"$schema" : "http://json-schema.org/draft-04/schema#",
"properties" : {
"servers" : {
"items" : {
"properties" : {
"addresses" : {
"items" : {
"properties" : {"address" : {"type" : "string"}},
"required" : ["address"],
"type" : "object"
},
"minItems" : 1,
"type" : "array"
},
"name" : {"type" : "string"},
"publicKey" : {"maxLength" : 64, "minLength" : 64, "type" : "string"}
},
"required" : [ "publicKey", "addresses", "name" ],
"type" : "object"
},
"minItems" : 1,
"type" : "array"
}
},
"required" : ["servers"],
"type" : "object"
});
static bool readFile(std::string* out_contents, const char* filename) {
std::ifstream serverFile;
serverFile.open(filename);
if ((serverFile.rdstate() & std::ifstream::failbit) != 0) {
FX_LOGS(ERROR) << "Opening " << filename << ": " << strerror(errno);
return false;
}
std::stringstream strStream;
strStream << serverFile.rdbuf();
out_contents->assign(strStream.str());
return true;
}
std::vector<RoughTimeServer> TimeServerConfig::ServerList() {
return server_list_;
}
bool checkSchema(rapidjson::Document& d) {
rapidjson::Document sd;
if (sd.Parse(config_schema).HasParseError()) {
FX_LOGS(ERROR) << "Schema not valid";
return false;
}
rapidjson::SchemaDocument schema(sd);
rapidjson::SchemaValidator validator(schema);
if (!d.Accept(validator)) {
// Input JSON is invalid according to the schema
// Output diagnostic information
rapidjson::StringBuffer sb;
validator.GetInvalidSchemaPointer().StringifyUriFragment(sb);
FX_LOGS(ERROR) << "Invalid schema: " << sb.GetString();
FX_LOGS(ERROR) << "Invalid keyword: "
<< validator.GetInvalidSchemaKeyword();
sb.Clear();
validator.GetInvalidDocumentPointer().StringifyUriFragment(sb);
FX_LOGS(ERROR) << "Invalid document: " << sb.GetString();
return false;
}
return true;
}
bool TimeServerConfig::Parse(std::string config_file) {
std::string json;
if (!readFile(&json, config_file.c_str())) {
return false;
}
rapidjson::Document doc;
rapidjson::ParseResult ok = doc.Parse(json.c_str());
if (!ok) {
FX_LOGS(ERROR) << "JSON parse error: "
<< rapidjson::GetParseError_En(ok.Code()) << "("
<< ok.Offset() << ")";
return false;
}
if (!checkSchema(doc)) {
return false;
}
const rapidjson::Value& servers = doc["servers"];
for (rapidjson::SizeType i = 0; i < servers.Size(); i++) {
const rapidjson::Value& server = servers[i];
const rapidjson::Value& addresses = server["addresses"];
std::string name = server["name"].GetString();
std::string public_key_str = server["publicKey"].GetString();
for (rapidjson::SizeType j = 0; j < addresses.Size(); j++) {
const rapidjson::Value& address = addresses[j];
std::string address_str = address["address"].GetString();
uint8_t public_key[roughtime::kPublicKeyLength];
if (public_key_str.length() != roughtime::kPublicKeyLength * 2) {
FX_LOGS(ERROR) << "Invalid public key: " << public_key_str;
return false;
}
for (unsigned int k = 0; k < roughtime::kPublicKeyLength; k++) {
char hex[3] = {0};
hex[0] = public_key_str.at(k * 2);
hex[1] = public_key_str.at(k * 2 + 1);
public_key[k] = strtoul(hex, NULL, 16);
}
RoughTimeServer server(std::move(name), std::move(address_str),
public_key, roughtime::kPublicKeyLength);
server_list_.push_back(server);
}
}
return true;
}
} // namespace time_server