Merge branch 'adamierymenko-dev' into android-jni

This commit is contained in:
Grant Limberg 2015-05-16 18:55:19 -07:00
commit 9a00366b18
13 changed files with 544 additions and 418 deletions

1
.gitignore vendored
View file

@ -52,3 +52,4 @@ java/build_win32/
/ui/.module-cache
/windows/WebUIWrapper/bin
/windows/WebUIWrapper/obj
node_modules

View file

@ -169,20 +169,19 @@ SqliteNetworkController::SqliteNetworkController(const char *dbPath) :
||(sqlite3_prepare_v2(_db,"SELECT ipNetwork,ipNetmaskBits,ipVersion FROM IpAssignmentPool WHERE networkId = ? ORDER BY ipNetwork ASC",-1,&_sGetIpAssignmentPools2,(const char **)0) != SQLITE_OK)
||(sqlite3_prepare_v2(_db,"SELECT ruleId,nodeId,vlanId,vlanPcp,etherType,macSource,macDest,ipSource,ipDest,ipTos,ipProtocol,ipSourcePort,ipDestPort,\"action\" FROM Rule WHERE networkId = ? ORDER BY ruleId ASC",-1,&_sListRules,(const char **)0) != SQLITE_OK)
||(sqlite3_prepare_v2(_db,"INSERT INTO Rule (networkId,ruleId,nodeId,vlanId,vlanPcP,etherType,macSource,macDest,ipSource,ipDest,ipTos,ipProtocol,ipSourcePort,ipDestPort,\"action\") VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)",-1,&_sCreateRule,(const char **)0) != SQLITE_OK)
||(sqlite3_prepare_v2(_db,"INSERT INTO Network (networkId,name,creationTime,revision) VALUES (?,?,?,1)",-1,&_sCreateNetwork,(const char **)0) != SQLITE_OK)
||(sqlite3_prepare_v2(_db,"UPDATE Network SET ? = ? WHERE networkId = ?",-1,&_sUpdateNetworkField,(const char **)0) != SQLITE_OK)
||(sqlite3_prepare_v2(_db,"INSERT INTO Network (id,name,creationTime,revision) VALUES (?,?,?,1)",-1,&_sCreateNetwork,(const char **)0) != SQLITE_OK)
||(sqlite3_prepare_v2(_db,"SELECT revision FROM Network WHERE id = ?",-1,&_sGetNetworkRevision,(const char **)0) != SQLITE_OK)
||(sqlite3_prepare_v2(_db,"UPDATE Network SET revision = ? WHERE id = ?",-1,&_sSetNetworkRevision,(const char **)0) != SQLITE_OK)
||(sqlite3_prepare_v2(_db,"SELECT ip,ipNetmaskBits,ipVersion FROM IpAssignment WHERE networkId = ? AND nodeId = ?",-1,&_sGetIpAssignmentsForNode2,(const char **)0) != SQLITE_OK)
||(sqlite3_prepare_v2(_db,"SELECT ip,ipNetmaskBits,ipVersion FROM IpAssignment WHERE networkId = ? AND nodeId = ? ORDER BY ip ASC",-1,&_sGetIpAssignmentsForNode2,(const char **)0) != SQLITE_OK)
||(sqlite3_prepare_v2(_db,"DELETE FROM Relay WHERE networkId = ?",-1,&_sDeleteRelaysForNetwork,(const char **)0) != SQLITE_OK)
||(sqlite3_prepare_v2(_db,"INSERT INTO Relay (networkId,nodeId,phyAddress) VALUES (?,?,?)",-1,&_sCreateRelay,(const char **)0) != SQLITE_OK)
||(sqlite3_prepare_v2(_db,"DELETE FROM IpAssignmentPool WHERE networkId = ?",-1,&_sDeleteIpAssignmentPoolsForNetwork,(const char **)0) != SQLITE_OK)
||(sqlite3_prepare_v2(_db,"DELETE FROM Rule WHERE networkId = ?",-1,&_sDeleteRulesForNetwork,(const char **)0) != SQLITE_OK)
||(sqlite3_prepare_v2(_db,"INSERT INTO IpAssignmentPool (networkId,ipNetwork,ipNetmaskBits,ipVersion) VALUES (?,?,?,?)",-1,&_sCreateIpAssignmentPool,(const char **)0) != SQLITE_OK)
||(sqlite3_prepare_v2(_db,"UPDATE Member SET ? = ? WHERE rowid = ?",-1,&_sUpdateMemberField,(const char **)0) != SQLITE_OK)
||(sqlite3_prepare_v2(_db,"DELETE FROM Member WHERE networkId = ? AND nodeId = ?",-1,&_sDeleteMember,(const char **)0) != SQLITE_OK)
||(sqlite3_prepare_v2(_db,"DELETE FROM IpAssignment WHERE networkId = ?; DELETE FROM IpAssignmentPool WHERE networkId = ?; DELETE FROM Member WHERE networkId = ?; DELETE FROM MulticastRate WHERE networkId = ?; DELETE FROM Relay WHERE networkId = ?; DELETE FROM Rule WHERE networkId = ?; DELETE FROM Network WHERE id = ?;",-1,&_sDeleteNetworkAndRelated,(const char **)0) != SQLITE_OK)
) {
//printf("!!! %s\n",sqlite3_errmsg(_db));
sqlite3_close(_db);
throw std::runtime_error("SqliteNetworkController unable to initialize one or more prepared statements");
}
@ -215,7 +214,6 @@ SqliteNetworkController::~SqliteNetworkController()
sqlite3_finalize(_sListRules);
sqlite3_finalize(_sCreateRule);
sqlite3_finalize(_sCreateNetwork);
sqlite3_finalize(_sUpdateNetworkField);
sqlite3_finalize(_sGetNetworkRevision);
sqlite3_finalize(_sSetNetworkRevision);
sqlite3_finalize(_sGetIpAssignmentsForNode2);
@ -224,7 +222,6 @@ SqliteNetworkController::~SqliteNetworkController()
sqlite3_finalize(_sDeleteIpAssignmentPoolsForNetwork);
sqlite3_finalize(_sDeleteRulesForNetwork);
sqlite3_finalize(_sCreateIpAssignmentPool);
sqlite3_finalize(_sUpdateMemberField);
sqlite3_finalize(_sDeleteNetworkAndRelated);
sqlite3_close(_db);
}
@ -562,282 +559,8 @@ unsigned int SqliteNetworkController::handleControlPlaneHttpGET(
std::string &responseBody,
std::string &responseContentType)
{
char json[16384];
if (path.empty())
return 404;
Mutex::Lock _l(_lock);
if (path[0] == "network") {
if ((path.size() >= 2)&&(path[1].length() == 16)) {
uint64_t nwid = Utils::hexStrToU64(path[1].c_str());
char nwids[24];
Utils::snprintf(nwids,sizeof(nwids),"%.16llx",(unsigned long long)nwid);
if (path.size() >= 3) {
if ((path.size() == 4)&&(path[2] == "member")&&(path[3].length() == 10)) {
uint64_t address = Utils::hexStrToU64(path[3].c_str());
char addrs[24];
Utils::snprintf(addrs,sizeof(addrs),"%.10llx",address);
sqlite3_reset(_sGetMember2);
sqlite3_bind_text(_sGetMember2,1,nwids,16,SQLITE_STATIC);
sqlite3_bind_text(_sGetMember2,2,addrs,10,SQLITE_STATIC);
if (sqlite3_step(_sGetMember2) == SQLITE_ROW) {
Utils::snprintf(json,sizeof(json),
"{\n"
"\tnwid: \"%s\",\n"
"\taddress: \"%s\",\n"
"\tauthorized: %s,\n"
"\tactiveBridge: %s,\n"
"\tlastAt: \"%s\",\n"
"\tlastSeen: %llu,\n"
"\tfirstSeen: %llu,\n"
"\tidentity: \"%s\",\n"
"\tipAssignments: [",
nwids,
addrs,
(sqlite3_column_int(_sGetMember2,0) > 0) ? "true" : "false",
(sqlite3_column_int(_sGetMember2,1) > 0) ? "true" : "false",
_jsonEscape((const char *)sqlite3_column_text(_sGetMember2,3)).c_str(),
(unsigned long long)sqlite3_column_int64(_sGetMember2,4),
(unsigned long long)sqlite3_column_int64(_sGetMember2,5),
_jsonEscape((const char *)sqlite3_column_text(_sGetMember2,2)).c_str());
responseBody = json;
sqlite3_reset(_sGetIpAssignmentsForNode2);
sqlite3_bind_text(_sGetIpAssignmentsForNode2,1,nwids,16,SQLITE_STATIC);
sqlite3_bind_text(_sGetIpAssignmentsForNode2,2,addrs,10,SQLITE_STATIC);
bool firstIp = true;
while (sqlite3_step(_sGetIpAssignmentPools2) == SQLITE_ROW) {
InetAddress ip((const void *)sqlite3_column_blob(_sGetIpAssignmentsForNode2,0),(sqlite3_column_int(_sGetIpAssignmentsForNode2,2) == 6) ? 16 : 4,(unsigned int)sqlite3_column_int(_sGetIpAssignmentPools2,1));
responseBody.append(firstIp ? "\"" : ",\"");
firstIp = false;
responseBody.append(_jsonEscape(ip.toString()));
responseBody.push_back('"');
}
responseBody.append("]");
/* It's possible to get the actual netconf dictionary by including these
* three URL arguments. The member identity must be the string
* serialized identity of this member, and the signing identity must be
* the full secret identity of this network controller. The have revision
* is optional but would designate the revision our hypothetical client
* already has.
*
* This is primarily for testing and is not used in production. It makes
* it easy to test the entire network controller via its JSON API.
*
* If these arguments are included, three more object fields are returned:
* 'netconf', 'netconfResult', and 'netconfResultMessage'. These are all
* string fields and contain the actual netconf dictionary, the query
* result code, and any verbose message e.g. an error description. */
std::map<std::string,std::string>::const_iterator memids(urlArgs.find("memberIdentity"));
std::map<std::string,std::string>::const_iterator sigids(urlArgs.find("signingIdentity"));
std::map<std::string,std::string>::const_iterator hrs(urlArgs.find("haveRevision"));
if ((memids != urlArgs.end())&&(sigids != urlArgs.end())) {
Dictionary netconf;
Identity memid,sigid;
try {
if (memid.fromString(memids->second)&&sigid.fromString(sigids->second)&&sigid.hasPrivate()) {
uint64_t hr = 0;
if (hrs != urlArgs.end())
hr = Utils::strToU64(hrs->second.c_str());
const char *result = "";
switch(this->doNetworkConfigRequest(InetAddress(),sigid,memid,nwid,Dictionary(),hr,netconf)) {
case NetworkController::NETCONF_QUERY_OK: result = "OK"; break;
case NetworkController::NETCONF_QUERY_OK_BUT_NOT_NEWER: result = "OK_BUT_NOT_NEWER"; break;
case NetworkController::NETCONF_QUERY_OBJECT_NOT_FOUND: result = "OBJECT_NOT_FOUND"; break;
case NetworkController::NETCONF_QUERY_ACCESS_DENIED: result = "ACCESS_DENIED"; break;
case NetworkController::NETCONF_QUERY_INTERNAL_SERVER_ERROR: result = "INTERNAL_SERVER_ERROR"; break;
default: result = "(unrecognized result code)"; break;
}
responseBody.append(",\n\tnetconf: \"");
responseBody.append(_jsonEscape(netconf.toString().c_str()));
responseBody.append("\",\n\tnetconfResult: \"");
responseBody.append(result);
responseBody.append("\",\n\tnetconfResultMessage: \"");
responseBody.append(_jsonEscape(netconf["error"].c_str()));
responseBody.append("\"");
} else {
responseBody.append(",\n\tnetconf: \"\",\n\tnetconfResult: \"INTERNAL_SERVER_ERROR\",\n\tnetconfResultMessage: \"invalid member or signing identity\"");
}
} catch ( ... ) {
responseBody.append(",\n\tnetconf: \"\",\n\tnetconfResult: \"INTERNAL_SERVER_ERROR\",\n\tnetconfResultMessage: \"unexpected exception\"");
}
}
responseBody.append("\n}\n");
responseContentType = "application/json";
return 200;
} // else 404
} // else 404
} else {
// get network info
sqlite3_reset(_sGetNetworkById);
sqlite3_bind_text(_sGetNetworkById,1,nwids,16,SQLITE_STATIC);
if (sqlite3_step(_sGetNetworkById) == SQLITE_ROW) {
Utils::snprintf(json,sizeof(json),
"{\n"
"\tnwid: \"%s\",\n"
"\tname: \"%s\",\n"
"\tprivate: %s,\n"
"\tenableBroadcast: %s,\n"
"\tallowPassiveBridging: %s,\n"
"\tv4AssignMode: \"%s\",\n"
"\tv6AssignMode: \"%s\",\n"
"\tmulticastLimit: %d,\n"
"\tcreationTime: %llu,\n",
"\trevision: %llu,\n"
"\amembers: [",
nwids,
_jsonEscape((const char *)sqlite3_column_text(_sGetNetworkById,0)).c_str(),
(sqlite3_column_int(_sGetNetworkById,1) > 0) ? "true" : "false",
(sqlite3_column_int(_sGetNetworkById,2) > 0) ? "true" : "false",
(sqlite3_column_int(_sGetNetworkById,3) > 0) ? "true" : "false",
_jsonEscape((const char *)sqlite3_column_text(_sGetNetworkById,4)).c_str(),
_jsonEscape((const char *)sqlite3_column_text(_sGetNetworkById,5)).c_str(),
sqlite3_column_int(_sGetNetworkById,6),
(unsigned long long)sqlite3_column_int64(_sGetNetworkById,7),
(unsigned long long)sqlite3_column_int64(_sGetNetworkById,8));
responseBody = json;
sqlite3_reset(_sListNetworkMembers);
sqlite3_bind_text(_sListNetworkMembers,1,nwids,16,SQLITE_STATIC);
bool firstMember = true;
while (sqlite3_step(_sListNetworkMembers) == SQLITE_ROW) {
if (!firstMember)
responseBody.push_back(',');
responseBody.push_back('"');
responseBody.append((const char *)sqlite3_column_text(_sListNetworkMembers,0));
responseBody.push_back('"');
firstMember = false;
}
responseBody.append("],\n\trelays: [");
sqlite3_reset(_sGetRelays);
sqlite3_bind_text(_sGetRelays,1,nwids,16,SQLITE_STATIC);
bool firstRelay = true;
while (sqlite3_step(_sGetRelays) == SQLITE_ROW) {
responseBody.append(firstRelay ? "\n\t\t" : ",\n\t\t");
firstRelay = false;
responseBody.append("{address:\"");
responseBody.append((const char *)sqlite3_column_text(_sGetRelays,0));
responseBody.append("\",phyAddress:\"");
responseBody.append(_jsonEscape((const char *)sqlite3_column_text(_sGetRelays,1)));
responseBody.append("\"}");
}
responseBody.append("],\n\tipAssignmentPools: [");
sqlite3_reset(_sGetIpAssignmentPools2);
sqlite3_bind_text(_sGetIpAssignmentPools2,1,nwids,16,SQLITE_STATIC);
bool firstIpAssignmentPool = true;
while (sqlite3_step(_sGetIpAssignmentPools2) == SQLITE_ROW) {
responseBody.append(firstIpAssignmentPool ? "\n\t\t" : ",\n\t\t");
firstIpAssignmentPool = false;
InetAddress ipp((const void *)sqlite3_column_blob(_sGetIpAssignmentPools2,0),(sqlite3_column_int(_sGetIpAssignmentPools2,2) == 6) ? 16 : 4,(unsigned int)sqlite3_column_int(_sGetIpAssignmentPools2,1));
Utils::snprintf(json,sizeof(json),"{network:\"%s\",netmaskBits:%u}",
_jsonEscape(ipp.toIpString()).c_str(),
ipp.netmaskBits());
responseBody.append(json);
}
responseBody.append("],\n\trules: [");
sqlite3_reset(_sListRules);
sqlite3_bind_text(_sListRules,1,nwids,16,SQLITE_STATIC);
bool firstRule = true;
while (sqlite3_step(_sListRules) == SQLITE_ROW) {
responseBody.append(firstRule ? "\n\t{\n" : ",{\n");
Utils::snprintf(json,sizeof(json),"\t\truleId: %lld,\n",sqlite3_column_int64(_sListRules,0));
responseBody.append(json);
if (sqlite3_column_type(_sListRules,1) != SQLITE_NULL) {
Utils::snprintf(json,sizeof(json),"\t\tnodeId: \"%s\",\n",(const char *)sqlite3_column_text(_sListRules,1));
responseBody.append(json);
}
if (sqlite3_column_type(_sListRules,2) != SQLITE_NULL) {
Utils::snprintf(json,sizeof(json),"\t\tvlanId: %d,\n",sqlite3_column_int(_sListRules,2));
responseBody.append(json);
}
if (sqlite3_column_type(_sListRules,3) != SQLITE_NULL) {
Utils::snprintf(json,sizeof(json),"\t\tvlanPcp: %d,\n",sqlite3_column_int(_sListRules,3));
responseBody.append(json);
}
if (sqlite3_column_type(_sListRules,4) != SQLITE_NULL) {
Utils::snprintf(json,sizeof(json),"\t\tetherType: %d,\n",sqlite3_column_int(_sListRules,4));
responseBody.append(json);
}
if (sqlite3_column_type(_sListRules,5) != SQLITE_NULL) {
Utils::snprintf(json,sizeof(json),"\t\tmacSource: \"%s\",\n",MAC((const char *)sqlite3_column_text(_sListRules,5)).toString().c_str());
responseBody.append(json);
}
if (sqlite3_column_type(_sListRules,6) != SQLITE_NULL) {
Utils::snprintf(json,sizeof(json),"\t\tmacDest: \"%s\",\n",MAC((const char *)sqlite3_column_text(_sListRules,6)).toString().c_str());
responseBody.append(json);
}
if (sqlite3_column_type(_sListRules,7) != SQLITE_NULL) {
Utils::snprintf(json,sizeof(json),"\t\tipSource: \"%s\",\n",_jsonEscape((const char *)sqlite3_column_text(_sListRules,7)).c_str());
responseBody.append(json);
}
if (sqlite3_column_type(_sListRules,8) != SQLITE_NULL) {
Utils::snprintf(json,sizeof(json),"\t\tipDest: \"%s\",\n",_jsonEscape((const char *)sqlite3_column_text(_sListRules,8)).c_str());
responseBody.append(json);
}
if (sqlite3_column_type(_sListRules,9) != SQLITE_NULL) {
Utils::snprintf(json,sizeof(json),"\t\tipTos: %d,\n",sqlite3_column_int(_sListRules,9));
responseBody.append(json);
}
if (sqlite3_column_type(_sListRules,10) != SQLITE_NULL) {
Utils::snprintf(json,sizeof(json),"\t\tipProtocol: %d,\n",sqlite3_column_int(_sListRules,10));
responseBody.append(json);
}
if (sqlite3_column_type(_sListRules,11) != SQLITE_NULL) {
Utils::snprintf(json,sizeof(json),"\t\tipSourcePort: %d,\n",sqlite3_column_int(_sListRules,11));
responseBody.append(json);
}
if (sqlite3_column_type(_sListRules,12) != SQLITE_NULL) {
Utils::snprintf(json,sizeof(json),"\t\tipDestPort: %d,\n",sqlite3_column_int(_sListRules,12));
responseBody.append(json);
}
responseBody.append("\t\taction: \"");
responseBody.append(_jsonEscape((const char *)sqlite3_column_text(_sListRules,13)));
responseBody.append("\"\n\t}");
}
responseBody.append("]\n}\n");
responseContentType = "application/json";
return 200;
} // else 404
}
} else if (path.size() == 1) {
// list networks
sqlite3_reset(_sListNetworks);
responseContentType = "application/json";
responseBody = "[";
bool first = true;
while (sqlite3_step(_sListNetworks) == SQLITE_ROW) {
if (first) {
first = false;
responseBody.push_back('"');
} else responseBody.append(",\"");
responseBody.append((const char *)sqlite3_column_text(_sListNetworks,0));
responseBody.push_back('"');
}
responseBody.push_back(']');
return 200;
} // else 404
} else {
// GET /controller returns status and API version if controller is supported
Utils::snprintf(json,sizeof(json),"{\n\tcontroller: true,\n\tapiVersion: %d\n\tclock: %llu\n}",ZT_NETCONF_CONTROLLER_API_VERSION,(unsigned long long)OSUtils::now());
responseBody = json;
responseContentType = "applicaiton/json";
return 200;
}
return 404;
return _doCPGet(path,urlArgs,headers,body,responseBody,responseContentType);
}
unsigned int SqliteNetworkController::handleControlPlaneHttpPOST(
@ -902,20 +625,25 @@ unsigned int SqliteNetworkController::handleControlPlaneHttpPOST(
if (j) {
if (j->type == json_object) {
for(unsigned int k=0;k<j->u.object.length;++k) {
sqlite3_reset(_sUpdateMemberField);
sqlite3_bind_int64(_sUpdateMemberField,3,memberRowId);
if (!strcmp(j->u.object.values[k].name,"authorized")) {
if (j->u.object.values[k].value->type == json_boolean) {
sqlite3_bind_text(_sUpdateMemberField,1,"authorized",-1,SQLITE_STATIC);
sqlite3_bind_int(_sUpdateMemberField,2,(j->u.object.values[k].value->u.boolean == 0) ? 0 : 1);
sqlite3_step(_sUpdateMemberField);
sqlite3_stmt *stmt = (sqlite3_stmt *)0;
if (sqlite3_prepare_v2(_db,"UPDATE Member SET authorized = ? WHERE rowid = ?",-1,&stmt,(const char **)0) == SQLITE_OK)
sqlite3_bind_int(stmt,1,(j->u.object.values[k].value->u.boolean == 0) ? 0 : 1);
sqlite3_bind_int64(stmt,2,memberRowId);
sqlite3_step(stmt);
sqlite3_finalize(stmt);
}
} else if (!strcmp(j->u.object.values[k].name,"activeBridge")) {
if (j->u.object.values[k].value->type == json_boolean) {
sqlite3_bind_text(_sUpdateMemberField,1,"activeBridge",-1,SQLITE_STATIC);
sqlite3_bind_int(_sUpdateMemberField,2,(j->u.object.values[k].value->u.boolean == 0) ? 0 : 1);
sqlite3_step(_sUpdateMemberField);
sqlite3_stmt *stmt = (sqlite3_stmt *)0;
if (sqlite3_prepare_v2(_db,"UPDATE Member SET activeBridge = ? WHERE rowid = ?",-1,&stmt,(const char **)0) == SQLITE_OK) {
sqlite3_bind_int(stmt,1,(j->u.object.values[k].value->u.boolean == 0) ? 0 : 1);
sqlite3_bind_int64(stmt,2,memberRowId);
sqlite3_step(stmt);
sqlite3_finalize(stmt);
}
}
} else if (!strcmp(j->u.object.values[k].name,"ipAssignments")) {
if (j->u.object.values[k].value->type == json_array) {
@ -957,12 +685,13 @@ unsigned int SqliteNetworkController::handleControlPlaneHttpPOST(
}
}
}
}
}
json_value_free(j);
}
return handleControlPlaneHttpGET(path,urlArgs,headers,body,responseBody,responseContentType);
return _doCPGet(path,urlArgs,headers,body,responseBody,responseContentType);
} // else 404
} else {
@ -980,50 +709,42 @@ unsigned int SqliteNetworkController::handleControlPlaneHttpPOST(
if (j) {
if (j->type == json_object) {
for(unsigned int k=0;k<j->u.object.length;++k) {
sqlite3_reset(_sUpdateNetworkField);
sqlite3_bind_text(_sUpdateNetworkField,3,nwids,16,SQLITE_STATIC);
sqlite3_stmt *stmt = (sqlite3_stmt *)0;
if (!strcmp(j->u.object.values[k].name,"name")) {
if ((j->u.object.values[k].value->type == json_string)&&(j->u.object.values[k].value->u.string.ptr[0])) {
sqlite3_bind_text(_sUpdateNetworkField,1,"name",-1,SQLITE_STATIC);
sqlite3_bind_text(_sUpdateNetworkField,2,j->u.object.values[k].value->u.string.ptr,-1,SQLITE_STATIC);
sqlite3_step(_sUpdateNetworkField);
if (sqlite3_prepare_v2(_db,"UPDATE Network SET name = ? WHERE id = ?",-1,&stmt,(const char **)0) == SQLITE_OK)
sqlite3_bind_text(stmt,1,j->u.object.values[k].value->u.string.ptr,-1,SQLITE_STATIC);
}
} else if (!strcmp(j->u.object.values[k].name,"private")) {
if (j->u.object.values[k].value->type == json_boolean) {
sqlite3_bind_text(_sUpdateNetworkField,1,"private",-1,SQLITE_STATIC);
sqlite3_bind_int(_sUpdateNetworkField,2,(j->u.object.values[k].value->u.boolean == 0) ? 0 : 1);
sqlite3_step(_sUpdateNetworkField);
if (sqlite3_prepare_v2(_db,"UPDATE Network SET private = ? WHERE id = ?",-1,&stmt,(const char **)0) == SQLITE_OK)
sqlite3_bind_int(stmt,1,(j->u.object.values[k].value->u.boolean == 0) ? 0 : 1);
}
} else if (!strcmp(j->u.object.values[k].name,"enableBroadcast")) {
if (j->u.object.values[k].value->type == json_boolean) {
sqlite3_bind_text(_sUpdateNetworkField,1,"enableBroadcast",-1,SQLITE_STATIC);
sqlite3_bind_int(_sUpdateNetworkField,2,(j->u.object.values[k].value->u.boolean == 0) ? 0 : 1);
sqlite3_step(_sUpdateNetworkField);
if (sqlite3_prepare_v2(_db,"UPDATE Network SET enableBroadcast = ? WHERE id = ?",-1,&stmt,(const char **)0) == SQLITE_OK)
sqlite3_bind_int(stmt,1,(j->u.object.values[k].value->u.boolean == 0) ? 0 : 1);
}
} else if (!strcmp(j->u.object.values[k].name,"allowPassiveBridging")) {
if (j->u.object.values[k].value->type == json_boolean) {
sqlite3_bind_text(_sUpdateNetworkField,1,"allowPassiveBridging",-1,SQLITE_STATIC);
sqlite3_bind_int(_sUpdateNetworkField,2,(j->u.object.values[k].value->u.boolean == 0) ? 0 : 1);
sqlite3_step(_sUpdateNetworkField);
if (sqlite3_prepare_v2(_db,"UPDATE Network SET allowPassiveBridging = ? WHERE id = ?",-1,&stmt,(const char **)0) == SQLITE_OK)
sqlite3_bind_int(stmt,1,(j->u.object.values[k].value->u.boolean == 0) ? 0 : 1);
}
} else if (!strcmp(j->u.object.values[k].name,"v4AssignMode")) {
if (j->u.object.values[k].value->type == json_string) {
sqlite3_bind_text(_sUpdateNetworkField,1,"v4AssignMode",-1,SQLITE_STATIC);
sqlite3_bind_text(_sUpdateNetworkField,2,j->u.object.values[k].value->u.string.ptr,-1,SQLITE_STATIC);
sqlite3_step(_sUpdateNetworkField);
if (sqlite3_prepare_v2(_db,"UPDATE Network SET v4AssignMode = ? WHERE id = ?",-1,&stmt,(const char **)0) == SQLITE_OK)
sqlite3_bind_text(stmt,1,j->u.object.values[k].value->u.string.ptr,-1,SQLITE_STATIC);
}
} else if (!strcmp(j->u.object.values[k].name,"v6AssignMode")) {
if (j->u.object.values[k].value->type == json_string) {
sqlite3_bind_text(_sUpdateNetworkField,1,"v6AssignMode",-1,SQLITE_STATIC);
sqlite3_bind_text(_sUpdateNetworkField,2,j->u.object.values[k].value->u.string.ptr,-1,SQLITE_STATIC);
sqlite3_step(_sUpdateNetworkField);
if (sqlite3_prepare_v2(_db,"UPDATE Network SET v6AssignMode = ? WHERE id = ?",-1,&stmt,(const char **)0) == SQLITE_OK)
sqlite3_bind_text(stmt,1,j->u.object.values[k].value->u.string.ptr,-1,SQLITE_STATIC);
}
} else if (!strcmp(j->u.object.values[k].name,"multicastLimit")) {
if (j->u.object.values[k].value->type == json_integer) {
sqlite3_bind_text(_sUpdateNetworkField,1,"multicastLimit",-1,SQLITE_STATIC);
sqlite3_bind_int(_sUpdateNetworkField,2,(int)j->u.object.values[k].value->u.integer);
sqlite3_step(_sUpdateNetworkField);
if (sqlite3_prepare_v2(_db,"UPDATE Network SET multicastLimit = ? WHERE id = ?",-1,&stmt,(const char **)0) == SQLITE_OK)
sqlite3_bind_int(stmt,1,(int)j->u.object.values[k].value->u.integer);
}
} else if (!strcmp(j->u.object.values[k].name,"relays")) {
if (j->u.object.values[k].value->type == json_array) {
@ -1185,6 +906,12 @@ unsigned int SqliteNetworkController::handleControlPlaneHttpPOST(
}
}
}
if (stmt) {
sqlite3_bind_text(stmt,2,nwids,16,SQLITE_STATIC);
sqlite3_step(stmt);
sqlite3_finalize(stmt);
}
}
}
json_value_free(j);
@ -1195,7 +922,7 @@ unsigned int SqliteNetworkController::handleControlPlaneHttpPOST(
sqlite3_bind_text(_sSetNetworkRevision,2,nwids,16,SQLITE_STATIC);
sqlite3_step(_sSetNetworkRevision);
return handleControlPlaneHttpGET(path,urlArgs,headers,body,responseBody,responseContentType);
return _doCPGet(path,urlArgs,headers,body,responseBody,responseContentType);
}
} // else 404
@ -1260,4 +987,287 @@ unsigned int SqliteNetworkController::handleControlPlaneHttpDELETE(
return 404;
}
unsigned int SqliteNetworkController::_doCPGet(
const std::vector<std::string> &path,
const std::map<std::string,std::string> &urlArgs,
const std::map<std::string,std::string> &headers,
const std::string &body,
std::string &responseBody,
std::string &responseContentType)
{
// Assumes _lock is locked
char json[16384];
if ((path.size() > 0)&&(path[0] == "network")) {
if ((path.size() >= 2)&&(path[1].length() == 16)) {
uint64_t nwid = Utils::hexStrToU64(path[1].c_str());
char nwids[24];
Utils::snprintf(nwids,sizeof(nwids),"%.16llx",(unsigned long long)nwid);
if (path.size() >= 3) {
if ((path.size() == 4)&&(path[2] == "member")&&(path[3].length() == 10)) {
uint64_t address = Utils::hexStrToU64(path[3].c_str());
char addrs[24];
Utils::snprintf(addrs,sizeof(addrs),"%.10llx",address);
sqlite3_reset(_sGetMember2);
sqlite3_bind_text(_sGetMember2,1,nwids,16,SQLITE_STATIC);
sqlite3_bind_text(_sGetMember2,2,addrs,10,SQLITE_STATIC);
if (sqlite3_step(_sGetMember2) == SQLITE_ROW) {
Utils::snprintf(json,sizeof(json),
"{\n"
"\t\"nwid\": \"%s\",\n"
"\t\"address\": \"%s\",\n"
"\t\"authorized\": %s,\n"
"\t\"activeBridge\": %s,\n"
"\t\"lastAt\": \"%s\",\n"
"\t\"lastSeen\": %llu,\n"
"\t\"firstSeen\": %llu,\n"
"\t\"identity\": \"%s\",\n"
"\t\"ipAssignments\": [",
nwids,
addrs,
(sqlite3_column_int(_sGetMember2,0) > 0) ? "true" : "false",
(sqlite3_column_int(_sGetMember2,1) > 0) ? "true" : "false",
_jsonEscape((const char *)sqlite3_column_text(_sGetMember2,3)).c_str(),
(unsigned long long)sqlite3_column_int64(_sGetMember2,4),
(unsigned long long)sqlite3_column_int64(_sGetMember2,5),
_jsonEscape((const char *)sqlite3_column_text(_sGetMember2,2)).c_str());
responseBody = json;
sqlite3_reset(_sGetIpAssignmentsForNode2);
sqlite3_bind_text(_sGetIpAssignmentsForNode2,1,nwids,16,SQLITE_STATIC);
sqlite3_bind_text(_sGetIpAssignmentsForNode2,2,addrs,10,SQLITE_STATIC);
bool firstIp = true;
while (sqlite3_step(_sGetIpAssignmentPools2) == SQLITE_ROW) {
InetAddress ip((const void *)sqlite3_column_blob(_sGetIpAssignmentsForNode2,0),(sqlite3_column_int(_sGetIpAssignmentsForNode2,2) == 6) ? 16 : 4,(unsigned int)sqlite3_column_int(_sGetIpAssignmentPools2,1));
responseBody.append(firstIp ? "\"" : ",\"");
firstIp = false;
responseBody.append(_jsonEscape(ip.toString()));
responseBody.push_back('"');
}
responseBody.append("]");
/* It's possible to get the actual netconf dictionary by including these
* three URL arguments. The member identity must be the string
* serialized identity of this member, and the signing identity must be
* the full secret identity of this network controller. The have revision
* is optional but would designate the revision our hypothetical client
* already has.
*
* This is primarily for testing and is not used in production. It makes
* it easy to test the entire network controller via its JSON API.
*
* If these arguments are included, three more object fields are returned:
* 'netconf', 'netconfResult', and 'netconfResultMessage'. These are all
* string fields and contain the actual netconf dictionary, the query
* result code, and any verbose message e.g. an error description. */
std::map<std::string,std::string>::const_iterator memids(urlArgs.find("memberIdentity"));
std::map<std::string,std::string>::const_iterator sigids(urlArgs.find("signingIdentity"));
std::map<std::string,std::string>::const_iterator hrs(urlArgs.find("haveRevision"));
if ((memids != urlArgs.end())&&(sigids != urlArgs.end())) {
Dictionary netconf;
Identity memid,sigid;
try {
if (memid.fromString(memids->second)&&sigid.fromString(sigids->second)&&sigid.hasPrivate()) {
uint64_t hr = 0;
if (hrs != urlArgs.end())
hr = Utils::strToU64(hrs->second.c_str());
const char *result = "";
switch(this->doNetworkConfigRequest(InetAddress(),sigid,memid,nwid,Dictionary(),hr,netconf)) {
case NetworkController::NETCONF_QUERY_OK: result = "OK"; break;
case NetworkController::NETCONF_QUERY_OK_BUT_NOT_NEWER: result = "OK_BUT_NOT_NEWER"; break;
case NetworkController::NETCONF_QUERY_OBJECT_NOT_FOUND: result = "OBJECT_NOT_FOUND"; break;
case NetworkController::NETCONF_QUERY_ACCESS_DENIED: result = "ACCESS_DENIED"; break;
case NetworkController::NETCONF_QUERY_INTERNAL_SERVER_ERROR: result = "INTERNAL_SERVER_ERROR"; break;
default: result = "(unrecognized result code)"; break;
}
responseBody.append(",\n\t\"netconf\": \"");
responseBody.append(_jsonEscape(netconf.toString().c_str()));
responseBody.append("\",\n\t\"netconfResult\": \"");
responseBody.append(result);
responseBody.append("\",\n\t\"netconfResultMessage\": \"");
responseBody.append(_jsonEscape(netconf["error"].c_str()));
responseBody.append("\"");
} else {
responseBody.append(",\n\t\"netconf\": \"\",\n\t\"netconfResult\": \"INTERNAL_SERVER_ERROR\",\n\t\"netconfResultMessage\": \"invalid member or signing identity\"");
}
} catch ( ... ) {
responseBody.append(",\n\t\"netconf\": \"\",\n\t\"netconfResult\": \"INTERNAL_SERVER_ERROR\",\n\t\"netconfResultMessage\": \"unexpected exception\"");
}
}
responseBody.append("\n}\n");
responseContentType = "application/json";
return 200;
} // else 404
} // else 404
} else {
// get network info
sqlite3_reset(_sGetNetworkById);
sqlite3_bind_text(_sGetNetworkById,1,nwids,16,SQLITE_STATIC);
if (sqlite3_step(_sGetNetworkById) == SQLITE_ROW) {
Utils::snprintf(json,sizeof(json),
"{\n"
"\t\"nwid\": \"%s\",\n"
"\t\"name\": \"%s\",\n"
"\t\"private\": %s,\n"
"\t\"enableBroadcast\": %s,\n"
"\t\"allowPassiveBridging\": %s,\n"
"\t\"v4AssignMode\": \"%s\",\n"
"\t\"v6AssignMode\": \"%s\",\n"
"\t\"multicastLimit\": %d,\n"
"\t\"creationTime\": %llu,\n"
"\t\"revision\": %llu,\n"
"\t\"members\": [",
nwids,
_jsonEscape((const char *)sqlite3_column_text(_sGetNetworkById,0)).c_str(),
(sqlite3_column_int(_sGetNetworkById,1) > 0) ? "true" : "false",
(sqlite3_column_int(_sGetNetworkById,2) > 0) ? "true" : "false",
(sqlite3_column_int(_sGetNetworkById,3) > 0) ? "true" : "false",
_jsonEscape((const char *)sqlite3_column_text(_sGetNetworkById,4)).c_str(),
_jsonEscape((const char *)sqlite3_column_text(_sGetNetworkById,5)).c_str(),
sqlite3_column_int(_sGetNetworkById,6),
(unsigned long long)sqlite3_column_int64(_sGetNetworkById,7),
(unsigned long long)sqlite3_column_int64(_sGetNetworkById,8));
responseBody = json;
sqlite3_reset(_sListNetworkMembers);
sqlite3_bind_text(_sListNetworkMembers,1,nwids,16,SQLITE_STATIC);
bool firstMember = true;
while (sqlite3_step(_sListNetworkMembers) == SQLITE_ROW) {
if (!firstMember)
responseBody.push_back(',');
responseBody.push_back('"');
responseBody.append((const char *)sqlite3_column_text(_sListNetworkMembers,0));
responseBody.push_back('"');
firstMember = false;
}
responseBody.append("],\n\t\"relays\": [");
sqlite3_reset(_sGetRelays);
sqlite3_bind_text(_sGetRelays,1,nwids,16,SQLITE_STATIC);
bool firstRelay = true;
while (sqlite3_step(_sGetRelays) == SQLITE_ROW) {
responseBody.append(firstRelay ? "\n\t\t" : ",\n\t\t");
firstRelay = false;
responseBody.append("{\"address\":\"");
responseBody.append((const char *)sqlite3_column_text(_sGetRelays,0));
responseBody.append("\",\"phyAddress\":\"");
responseBody.append(_jsonEscape((const char *)sqlite3_column_text(_sGetRelays,1)));
responseBody.append("\"}");
}
responseBody.append("],\n\t\"ipAssignmentPools\": [");
sqlite3_reset(_sGetIpAssignmentPools2);
sqlite3_bind_text(_sGetIpAssignmentPools2,1,nwids,16,SQLITE_STATIC);
bool firstIpAssignmentPool = true;
while (sqlite3_step(_sGetIpAssignmentPools2) == SQLITE_ROW) {
responseBody.append(firstIpAssignmentPool ? "\n\t\t" : ",\n\t\t");
firstIpAssignmentPool = false;
InetAddress ipp((const void *)sqlite3_column_blob(_sGetIpAssignmentPools2,0),(sqlite3_column_int(_sGetIpAssignmentPools2,2) == 6) ? 16 : 4,(unsigned int)sqlite3_column_int(_sGetIpAssignmentPools2,1));
Utils::snprintf(json,sizeof(json),"{\"network\":\"%s\",\"netmaskBits\":%u}",
_jsonEscape(ipp.toIpString()).c_str(),
ipp.netmaskBits());
responseBody.append(json);
}
responseBody.append("],\n\t\"rules\": [");
sqlite3_reset(_sListRules);
sqlite3_bind_text(_sListRules,1,nwids,16,SQLITE_STATIC);
bool firstRule = true;
while (sqlite3_step(_sListRules) == SQLITE_ROW) {
responseBody.append(firstRule ? "\n\t{\n" : ",{\n");
Utils::snprintf(json,sizeof(json),"\t\t\"ruleId\": %lld,\n",sqlite3_column_int64(_sListRules,0));
responseBody.append(json);
if (sqlite3_column_type(_sListRules,1) != SQLITE_NULL) {
Utils::snprintf(json,sizeof(json),"\t\t\"nodeId\": \"%s\",\n",(const char *)sqlite3_column_text(_sListRules,1));
responseBody.append(json);
}
if (sqlite3_column_type(_sListRules,2) != SQLITE_NULL) {
Utils::snprintf(json,sizeof(json),"\t\t\"vlanId\": %d,\n",sqlite3_column_int(_sListRules,2));
responseBody.append(json);
}
if (sqlite3_column_type(_sListRules,3) != SQLITE_NULL) {
Utils::snprintf(json,sizeof(json),"\t\t\"vlanPcp\": %d,\n",sqlite3_column_int(_sListRules,3));
responseBody.append(json);
}
if (sqlite3_column_type(_sListRules,4) != SQLITE_NULL) {
Utils::snprintf(json,sizeof(json),"\t\t\"etherType\": %d,\n",sqlite3_column_int(_sListRules,4));
responseBody.append(json);
}
if (sqlite3_column_type(_sListRules,5) != SQLITE_NULL) {
Utils::snprintf(json,sizeof(json),"\t\t\"macSource\": \"%s\",\n",MAC((const char *)sqlite3_column_text(_sListRules,5)).toString().c_str());
responseBody.append(json);
}
if (sqlite3_column_type(_sListRules,6) != SQLITE_NULL) {
Utils::snprintf(json,sizeof(json),"\t\t\"macDest\": \"%s\",\n",MAC((const char *)sqlite3_column_text(_sListRules,6)).toString().c_str());
responseBody.append(json);
}
if (sqlite3_column_type(_sListRules,7) != SQLITE_NULL) {
Utils::snprintf(json,sizeof(json),"\t\t\"ipSource\": \"%s\",\n",_jsonEscape((const char *)sqlite3_column_text(_sListRules,7)).c_str());
responseBody.append(json);
}
if (sqlite3_column_type(_sListRules,8) != SQLITE_NULL) {
Utils::snprintf(json,sizeof(json),"\t\t\"ipDest\": \"%s\",\n",_jsonEscape((const char *)sqlite3_column_text(_sListRules,8)).c_str());
responseBody.append(json);
}
if (sqlite3_column_type(_sListRules,9) != SQLITE_NULL) {
Utils::snprintf(json,sizeof(json),"\t\t\"ipTos\": %d,\n",sqlite3_column_int(_sListRules,9));
responseBody.append(json);
}
if (sqlite3_column_type(_sListRules,10) != SQLITE_NULL) {
Utils::snprintf(json,sizeof(json),"\t\t\"ipProtocol\": %d,\n",sqlite3_column_int(_sListRules,10));
responseBody.append(json);
}
if (sqlite3_column_type(_sListRules,11) != SQLITE_NULL) {
Utils::snprintf(json,sizeof(json),"\t\t\"ipSourcePort\": %d,\n",sqlite3_column_int(_sListRules,11));
responseBody.append(json);
}
if (sqlite3_column_type(_sListRules,12) != SQLITE_NULL) {
Utils::snprintf(json,sizeof(json),"\t\t\"ipDestPort\": %d,\n",sqlite3_column_int(_sListRules,12));
responseBody.append(json);
}
responseBody.append("\t\t\"action\": \"");
responseBody.append(_jsonEscape((const char *)sqlite3_column_text(_sListRules,13)));
responseBody.append("\"\n\t}");
}
responseBody.append("]\n}\n");
responseContentType = "application/json";
return 200;
} // else 404
}
} else if (path.size() == 1) {
// list networks
sqlite3_reset(_sListNetworks);
responseContentType = "application/json";
responseBody = "[";
bool first = true;
while (sqlite3_step(_sListNetworks) == SQLITE_ROW) {
if (first) {
first = false;
responseBody.push_back('"');
} else responseBody.append(",\"");
responseBody.append((const char *)sqlite3_column_text(_sListNetworks,0));
responseBody.push_back('"');
}
responseBody.push_back(']');
return 200;
} // else 404
} else {
// GET /controller returns status and API version if controller is supported
Utils::snprintf(json,sizeof(json),"{\n\t\"controller\": true,\n\t\"apiVersion\": %d,\n\t\"clock\": %llu\n}",ZT_NETCONF_CONTROLLER_API_VERSION,(unsigned long long)OSUtils::now());
responseBody = json;
responseContentType = "applicaiton/json";
return 200;
}
return 404;
}
} // namespace ZeroTier

View file

@ -40,17 +40,14 @@
#include "../node/NetworkController.hpp"
#include "../node/Mutex.hpp"
#include "../service/ControlPlaneSubsystem.hpp"
namespace ZeroTier {
class SqliteNetworkController : public NetworkController,public ControlPlaneSubsystem
class SqliteNetworkController : public NetworkController
{
public:
SqliteNetworkController(const char *dbPath);
virtual ~SqliteNetworkController();
// NetworkController
virtual NetworkController::ResultCode doNetworkConfigRequest(
const InetAddress &fromAddr,
const Identity &signingId,
@ -60,22 +57,21 @@ public:
uint64_t haveRevision,
Dictionary &netconf);
// ControlPlaneSubsystem
virtual unsigned int handleControlPlaneHttpGET(
unsigned int handleControlPlaneHttpGET(
const std::vector<std::string> &path,
const std::map<std::string,std::string> &urlArgs,
const std::map<std::string,std::string> &headers,
const std::string &body,
std::string &responseBody,
std::string &responseContentType);
virtual unsigned int handleControlPlaneHttpPOST(
unsigned int handleControlPlaneHttpPOST(
const std::vector<std::string> &path,
const std::map<std::string,std::string> &urlArgs,
const std::map<std::string,std::string> &headers,
const std::string &body,
std::string &responseBody,
std::string &responseContentType);
virtual unsigned int handleControlPlaneHttpDELETE(
unsigned int handleControlPlaneHttpDELETE(
const std::vector<std::string> &path,
const std::map<std::string,std::string> &urlArgs,
const std::map<std::string,std::string> &headers,
@ -84,6 +80,14 @@ public:
std::string &responseContentType);
private:
unsigned int _doCPGet(
const std::vector<std::string> &path,
const std::map<std::string,std::string> &urlArgs,
const std::map<std::string,std::string> &headers,
const std::string &body,
std::string &responseBody,
std::string &responseContentType);
std::string _dbPath;
sqlite3 *_db;
@ -110,7 +114,6 @@ private:
sqlite3_stmt *_sListRules;
sqlite3_stmt *_sCreateRule;
sqlite3_stmt *_sCreateNetwork;
sqlite3_stmt *_sUpdateNetworkField;
sqlite3_stmt *_sGetNetworkRevision;
sqlite3_stmt *_sSetNetworkRevision;
sqlite3_stmt *_sGetIpAssignmentsForNode2;
@ -119,7 +122,6 @@ private:
sqlite3_stmt *_sDeleteIpAssignmentPoolsForNetwork;
sqlite3_stmt *_sDeleteRulesForNetwork;
sqlite3_stmt *_sCreateIpAssignmentPool;
sqlite3_stmt *_sUpdateMemberField;
sqlite3_stmt *_sDeleteMember;
sqlite3_stmt *_sDeleteNetworkAndRelated;

View file

@ -30,10 +30,6 @@
"CREATE TABLE Member (\n"\
" networkId char(16) NOT NULL,\n"\
" nodeId char(10) NOT NULL,\n"\
" cachedNetconf blob(4096),\n"\
" cachedNetconfRevision integer NOT NULL DEFAULT(0),\n"\
" cachedNetconfTimestamp integer NOT NULL DEFAULT(0),\n"\
" clientReportedRevision integer NOT NULL DEFAULT(0),\n"\
" authorized integer NOT NULL DEFAULT(0),\n"\
" activeBridge integer NOT NULL DEFAULT(0)\n"\
");\n"\

137
nodejs-zt1-client/index.js Normal file
View file

@ -0,0 +1,137 @@
'use strict'
var request = require('request');
function ZT1Client(url,authToken)
{
this.url = url;
this.authToken = authToken;
}
ZT1Client.prototype._jsonGet = function(getPath,callback)
{
request({
url: this.url + getPath,
method: 'GET',
headers: {
'X-ZT1-Auth': this.authToken
}
},function(error,response,body) {
if (error)
return callback(error,null);
if (response.statusCode !== 200)
return callback(new Error('server responded with error: '+response.statusCode),null);
return callback(null,(typeof body === 'string') ? JSON.parse(body) : null);
});
};
ZT1Client.prototype.status = function(callback)
{
request({
url: this.url + 'controller',
method: 'GET',
headers: {
'X-ZT1-Auth': this.authToken
}
},function(error,response,body) {
if (error)
return callback(error,{});
var controllerStatus = {};
if (typeof body === 'string')
controllerStatus = JSON.parse(body);
request({
url: this.url + 'status',
method: 'GET',
headers: {
'X-ZT1-Auth': this.authToken
}
},function(error,response,body) {
if (error)
return callback(error,{});
if (response.statusCode !== 200)
return callback(new Error('server responded with '+response.statusCode),{});
var nodeStatus = JSON.parse(body);
for(var k in controllerStatus)
nodeStatus[k] = controllerStatus[k];
return callback(null,nodeStatus);
}.bind(this));
}.bind(this));
};
ZT1Client.prototype.getNetworks = function(callback)
{
this._jsonGet('network',callback);
};
ZT1Client.prototype.getPeers = function(callback)
{
this._jsonGet('peer',callback);
};
ZT1Client.prototype.listControllerNetworks = function(callback)
{
this._jsonGet('controller/network',callback);
};
ZT1Client.prototype.getControllerNetwork = function(nwid,callback)
{
this._jsonGet('controller/network/' + nwid,callback);
};
ZT1Client.prototype.saveControllerNetwork = function(network,callback)
{
if ((typeof network.nwid !== 'string')||(network.nwid.length !== 16))
return callback(new Error('Missing required field: nwid'),null);
// The ZT1 service is type variation intolerant, so recreate our submission with the correct types
var n = {
nwid: network.nwid
};
if (network.name)
n.name = network.name.toString();
if ('private' in network)
n.private = (network.private) ? true : false;
if ('enableBroadcast' in network)
n.enableBroadcast = (network.enableBroadcast) ? true : false;
if ('allowPassiveBridging' in network)
n.allowPassiveBridging = (network.allowPassiveBridging) ? true : false;
if ('v4AssignMode' in network) {
if (network.v4AssignMode)
n.v4AssignMode = network.v4AssignMode.toString();
else n.v4AssignMode = 'none';
}
if ('v6AssignMode' in network) {
if (network.v6AssignMode)
n.v6AssignMode = network.v6AssignMode.toString();
else n.v4AssignMode = 'none';
}
if ('multicastLimit' in network) {
if (typeof network.multicastLimit === 'number')
n.multicastLimit = network.multicastLimit;
else n.multicastLimit = parseInt(network.multicastLimit.toString());
}
if (Array.isArray(network.relays))
n.relays = network.relays;
if (Array.isArray(network.ipAssignmentPools))
n.ipAssignmentPools = network.ipAssignmentPools;
if (Array.isArray(network.rules))
n.rules = network.rules;
request({
url: this.url + 'controller/network/' + n.nwid,
method: 'POST',
json: true,
body: n,
headers: {
'X-ZT1-Auth': this.authToken
}
},function(err,response,body) {
if (err)
return callback(err,null);
if (response.statusCode !== 200)
return callback(new Error('server responded with error: '+response.statusCode),null);
return callback(null,(typeof body === 'string') ? JSON.parse(body) : body);
});
};
exports.ZT1Client = ZT1Client;

View file

@ -0,0 +1,14 @@
{
"name": "nodejs-zt1-client",
"version": "1.0.0",
"description": "ZeroTier One Network Virtualization Service JSON API Client",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "ZeroTier, Inc.",
"license": "BSD",
"dependencies": {
"request": "^2.55.0"
}
}

33
nodejs-zt1-client/test.js Normal file
View file

@ -0,0 +1,33 @@
var ZT1Client = require('./index.js').ZT1Client;
var zt1c = new ZT1Client('http://127.0.0.1:9993/','5d6181b71fae2684f9cc64ed');
zt1c.status(function(err,status) {
if (err)
console.log(err);
else console.log(status);
zt1c.getNetworks(function(err,networks) {
if (err)
console.log(err);
else console.log(networks);
zt1c.getPeers(function(err,peers) {
if (err)
console.log(err);
else console.log(peers);
if (status.controller) {
zt1c.saveControllerNetwork({
nwid: status.address + 'dead01',
name: 'test network',
private: true
},function(err,network) {
if (err)
console.log(err);
else console.log(network);
});
}
});
});
});

18
one.cpp
View file

@ -881,6 +881,7 @@ static void printHelp(const char *cn,FILE *out)
fprintf(out,"Available switches:"ZT_EOL_S);
fprintf(out," -h - Display this help"ZT_EOL_S);
fprintf(out," -v - Show version"ZT_EOL_S);
fprintf(out," -U - Run as unprivileged user (skip privilege check)"ZT_EOL_S);
fprintf(out," -p<port> - Port for UDP and TCP/HTTP (default: 9993)"ZT_EOL_S);
//fprintf(out," -T<path> - Override root topology, do not authenticate or update"ZT_EOL_S);
#ifdef __UNIX_LIKE__
@ -945,6 +946,7 @@ int main(int argc,char **argv)
std::string overrideRootTopology;
std::string homeDir;
unsigned int port = ZT1_DEFAULT_PORT;
bool skipRootCheck = false;
for(int i=1;i<argc;++i) {
if (argv[i][0] == '-') {
@ -964,6 +966,10 @@ int main(int argc,char **argv)
break;
#endif // __UNIX_LIKE__
case 'U':
skipRootCheck = true;
break;
case 'T': // Override root topology
if (argv[i][2]) {
if (!OSUtils::readFile(argv[i] + 2,overrideRootTopology)) {
@ -1082,11 +1088,10 @@ int main(int argc,char **argv)
}
#ifdef __UNIX_LIKE__
if (getuid() != 0) {
if ((!skipRootCheck)&&(getuid() != 0)) {
fprintf(stderr,"%s: must be run as root (uid 0)"ZT_EOL_S,argv[0]);
return 1;
}
if (runAsDaemon) {
long p = (long)fork();
if (p < 0) {
@ -1102,10 +1107,13 @@ int main(int argc,char **argv)
if (winRunFromCommandLine) {
// Running in "interactive" mode (mostly for debugging)
if (IsCurrentUserLocalAdministrator() != TRUE) {
fprintf(stderr,"%s: must be run as a local administrator."ZT_EOL_S,argv[0]);
return 1;
if (!skipRootCheck) {
fprintf(stderr,"%s: must be run as a local administrator."ZT_EOL_S,argv[0]);
return 1;
}
} else {
_winPokeAHole();
}
_winPokeAHole();
SetConsoleCtrlHandler(&_winConsoleCtrlHandler,TRUE);
// continues on to ordinary command line execution code below...
} else {

View file

@ -26,7 +26,6 @@
*/
#include "ControlPlane.hpp"
#include "ControlPlaneSubsystem.hpp"
#include "OneService.hpp"
#include "../version.h"
@ -34,6 +33,8 @@
#include "../ext/http-parser/http_parser.h"
#include "../controller/SqliteNetworkController.hpp"
#include "../node/InetAddress.hpp"
#include "../node/Node.hpp"
#include "../node/Utils.hpp"
@ -444,7 +445,7 @@ unsigned int ControlPlane::handleRequest(
responseContentType = "text/plain";
scode = 200;
} else {
std::map<std::string,ControlPlaneSubsystem *>::const_iterator ss(_subsystems.find(ps[0]));
std::map<std::string,SqliteNetworkController *>::const_iterator ss(_subsystems.find(ps[0]));
if (ss != _subsystems.end())
scode = ss->second->handleControlPlaneHttpGET(std::vector<std::string>(ps.begin()+1,ps.end()),urlArgs,headers,body,responseBody,responseContentType);
else scode = 404;
@ -477,7 +478,7 @@ unsigned int ControlPlane::handleRequest(
} else scode = 500;
}
} else {
std::map<std::string,ControlPlaneSubsystem *>::const_iterator ss(_subsystems.find(ps[0]));
std::map<std::string,SqliteNetworkController *>::const_iterator ss(_subsystems.find(ps[0]));
if (ss != _subsystems.end())
scode = ss->second->handleControlPlaneHttpPOST(std::vector<std::string>(ps.begin()+1,ps.end()),urlArgs,headers,body,responseBody,responseContentType);
else scode = 404;
@ -509,7 +510,7 @@ unsigned int ControlPlane::handleRequest(
_node->freeQueryResult((void *)nws);
} else scode = 500;
} else {
std::map<std::string,ControlPlaneSubsystem *>::const_iterator ss(_subsystems.find(ps[0]));
std::map<std::string,SqliteNetworkController *>::const_iterator ss(_subsystems.find(ps[0]));
if (ss != _subsystems.end())
scode = ss->second->handleControlPlaneHttpDELETE(std::vector<std::string>(ps.begin()+1,ps.end()),urlArgs,headers,body,responseBody,responseContentType);
else scode = 404;

View file

@ -40,7 +40,7 @@ namespace ZeroTier {
class OneService;
class Node;
class ControlPlaneSubsystem;
class SqliteNetworkController;
struct InetAddress;
/**
@ -72,7 +72,7 @@ public:
* @param prefix First element in URI path
* @param subsys Object to call for results of GET and POST/PUT operations
*/
inline void mount(const char *prefix,ControlPlaneSubsystem *subsys)
inline void mount(const char *prefix,SqliteNetworkController *subsys)
{
Mutex::Lock _l(_lock);
_subsystems[std::string(prefix)] = subsys;
@ -104,7 +104,7 @@ private:
Node *const _node;
std::string _uiStaticPath;
std::set<std::string> _authTokens;
std::map<std::string,ControlPlaneSubsystem *> _subsystems;
std::map<std::string,SqliteNetworkController *> _subsystems;
Mutex _lock;
};

View file

@ -1,76 +0,0 @@
/*
* ZeroTier One - Network Virtualization Everywhere
* Copyright (C) 2011-2015 ZeroTier, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* --
*
* ZeroTier may be used and distributed under the terms of the GPLv3, which
* are available at: http://www.gnu.org/licenses/gpl-3.0.html
*
* If you would like to embed ZeroTier into a commercial application or
* redistribute it in a modified binary form, please contact ZeroTier Networks
* LLC. Start here: http://www.zerotier.com/
*/
#ifndef ZT_CONTROLPLANESUBSYSTEM_HPP
#define ZT_CONTROLPLANESUBSYSTEM_HPP
#include <map>
#include <vector>
#include <string>
namespace ZeroTier {
/**
* Base class for subsystems that can be mounted under the HTTP control plane
*
* Handlers should fill in responseBody and responseContentType and return
* a HTTP status code or 0 on other errors.
*/
class ControlPlaneSubsystem
{
public:
ControlPlaneSubsystem() {}
virtual ~ControlPlaneSubsystem() {}
virtual unsigned int handleControlPlaneHttpGET(
const std::vector<std::string> &path,
const std::map<std::string,std::string> &urlArgs,
const std::map<std::string,std::string> &headers,
const std::string &body,
std::string &responseBody,
std::string &responseContentType) = 0;
virtual unsigned int handleControlPlaneHttpPOST(
const std::vector<std::string> &path,
const std::map<std::string,std::string> &urlArgs,
const std::map<std::string,std::string> &headers,
const std::string &body,
std::string &responseBody,
std::string &responseContentType) = 0;
virtual unsigned int handleControlPlaneHttpDELETE(
const std::vector<std::string> &path,
const std::map<std::string,std::string> &urlArgs,
const std::map<std::string,std::string> &headers,
const std::string &body,
std::string &responseBody,
std::string &responseContentType) = 0;
};
} // namespace ZeroTier
#endif

View file

@ -233,7 +233,7 @@ public:
_controlPlane = new ControlPlane(this,_node,(_homePath + ZT_PATH_SEPARATOR_S + "ui").c_str());
_controlPlane->addAuthToken(authToken.c_str());
if (_master)
_controlPlane->mount("controller",reinterpret_cast<ControlPlaneSubsystem *>(_master));
_controlPlane->mount("controller",reinterpret_cast<SqliteNetworkController *>(_master));
{ // Remember networks from previous session
std::vector<std::string> networksDotD(OSUtils::listDirectory((_homePath + ZT_PATH_SEPARATOR_S + "networks.d").c_str()));

View file

@ -11,7 +11,7 @@ The JSON API supports GET, POST/PUT, and DELETE. PUT is treated as a synonym for
Values POSTed to the JSON API are *extremely* type sensitive. Things *must* be of the indicated type, otherwise they will be ignored or will generate an error. Anything quoted is a string so booleans and integers must lack quotes. Booleans must be *true* or *false* and nothing else. Integers cannot contain decimal points or they are floats (and vice versa). If something seems to be getting ignored or set to a strange value, or if you receive errors, check the type of all JSON fields you are submitting against the types listed below. Unrecognized fields in JSON objects are also ignored.
API requests must be authenticated via an authentication token. ZeroTier One saves this token in the *authtoken.secret* file in its working directory. This token may be supplied via the *authToken* URL parameter (e.g. '?authToken=...') or via the *X-ZT1-Auth* HTTP request header. Static UI pages are the only thing the server will allow without authentication.
API requests must be authenticated via an authentication token. ZeroTier One saves this token in the *authtoken.secret* file in its working directory. This token may be supplied via the *auth* URL parameter (e.g. '?auth=...') or via the *X-ZT1-Auth* HTTP request header. Static UI pages are the only thing the server will allow without authentication.
A *jsonp* URL argument may be supplied to request JSONP encapsulation. A JSONP response is sent as a script with its JSON response payload wrapped in a call to the function name supplied as the argument to *jsonp*.