summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-x.gitignore1
-rw-r--r--controller/SqliteNetworkController.cpp638
-rw-r--r--controller/SqliteNetworkController.hpp22
-rw-r--r--controller/schema.sql.c4
-rw-r--r--nodejs-zt1-client/index.js137
-rw-r--r--nodejs-zt1-client/package.json14
-rw-r--r--nodejs-zt1-client/test.js33
-rw-r--r--one.cpp18
-rw-r--r--service/ControlPlane.cpp9
-rw-r--r--service/ControlPlane.hpp6
-rw-r--r--service/ControlPlaneSubsystem.hpp76
-rw-r--r--service/OneService.cpp2
-rw-r--r--service/README.md2
13 files changed, 544 insertions, 418 deletions
diff --git a/.gitignore b/.gitignore
index 09f906b8..5bfbd1ee 100755
--- a/.gitignore
+++ b/.gitignore
@@ -52,3 +52,4 @@ java/build_win32/
/ui/.module-cache
/windows/WebUIWrapper/bin
/windows/WebUIWrapper/obj
+node_modules
diff --git a/controller/SqliteNetworkController.cpp b/controller/SqliteNetworkController.cpp
index 92b8c32c..4ac40fc4 100644
--- a/controller/SqliteNetworkController.cpp
+++ b/controller/SqliteNetworkController.cpp
@@ -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
diff --git a/controller/SqliteNetworkController.hpp b/controller/SqliteNetworkController.hpp
index c5d4c51a..5c92cc0b 100644
--- a/controller/SqliteNetworkController.hpp
+++ b/controller/SqliteNetworkController.hpp
@@ -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;
diff --git a/controller/schema.sql.c b/controller/schema.sql.c
index f606b607..01b7c949 100644
--- a/controller/schema.sql.c
+++ b/controller/schema.sql.c
@@ -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"\
diff --git a/nodejs-zt1-client/index.js b/nodejs-zt1-client/index.js
new file mode 100644
index 00000000..f61e3b54
--- /dev/null
+++ b/nodejs-zt1-client/index.js
@@ -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;
diff --git a/nodejs-zt1-client/package.json b/nodejs-zt1-client/package.json
new file mode 100644
index 00000000..c840d8ae
--- /dev/null
+++ b/nodejs-zt1-client/package.json
@@ -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"
+ }
+}
diff --git a/nodejs-zt1-client/test.js b/nodejs-zt1-client/test.js
new file mode 100644
index 00000000..347ca1a1
--- /dev/null
+++ b/nodejs-zt1-client/test.js
@@ -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);
+ });
+ }
+ });
+ });
+});
diff --git a/one.cpp b/one.cpp
index 54145d03..d6da3a26 100644
--- a/one.cpp
+++ b/one.cpp
@@ -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 {
diff --git a/service/ControlPlane.cpp b/service/ControlPlane.cpp
index 69c5d48d..1d9f9b4a 100644
--- a/service/ControlPlane.cpp
+++ b/service/ControlPlane.cpp
@@ -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;
diff --git a/service/ControlPlane.hpp b/service/ControlPlane.hpp
index b7b0a857..fc8a0182 100644
--- a/service/ControlPlane.hpp
+++ b/service/ControlPlane.hpp
@@ -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;
};
diff --git a/service/ControlPlaneSubsystem.hpp b/service/ControlPlaneSubsystem.hpp
deleted file mode 100644
index 9de875ca..00000000
--- a/service/ControlPlaneSubsystem.hpp
+++ /dev/null
@@ -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
diff --git a/service/OneService.cpp b/service/OneService.cpp
index 99aaf77d..13108aa1 100644
--- a/service/OneService.cpp
+++ b/service/OneService.cpp
@@ -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()));
diff --git a/service/README.md b/service/README.md
index c347931f..ecab6e75 100644
--- a/service/README.md
+++ b/service/README.md
@@ -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*.