|  | @@ -51,7 +51,7 @@ static std::string _jsonEscape(const char *s)
 | 
	
		
			
				|  |  |  			case '"':  buf.append("\\\""); break;
 | 
	
		
			
				|  |  |  			case '\\': buf.append("\\\\"); break;
 | 
	
		
			
				|  |  |  			case '/':  buf.append("\\/");  break;
 | 
	
		
			
				|  |  | -			default:   buf.push_back(*s);  break;
 | 
	
		
			
				|  |  | +			default:   buf.push_back(*p);  break;
 | 
	
		
			
				|  |  |  		}
 | 
	
		
			
				|  |  |  	}
 | 
	
		
			
				|  |  |  	return buf;
 | 
	
	
		
			
				|  | @@ -92,6 +92,22 @@ static std::string _jsonEnumerate(const struct sockaddr_storage *ss,unsigned int
 | 
	
		
			
				|  |  |  	buf.push_back(']');
 | 
	
		
			
				|  |  |  	return buf;
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  | +static std::string _jsonEnumerate(const ZT1_PeerPhysicalPath *pp,unsigned int count)
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +	char tmp[1024];
 | 
	
		
			
				|  |  | +	std::string buf;
 | 
	
		
			
				|  |  | +	buf.push_back('[');
 | 
	
		
			
				|  |  | +	for(unsigned int i=0;i<count;++i) {
 | 
	
		
			
				|  |  | +		if (i > 0)
 | 
	
		
			
				|  |  | +			buf.push_back(',');
 | 
	
		
			
				|  |  | +		buf.append("{\"address\":\"");
 | 
	
		
			
				|  |  | +		buf.append(_jsonEscape(reinterpret_cast<const InetAddress *>(&(pp[i].address))->toString()));
 | 
	
		
			
				|  |  | +		Utils::snprintf(tmp,sizeof(tmp),"\",\"lastSend\":%llu,\"lastReceive\":%llu,\"fixed\":%s}",pp[i].lastSend,pp[i].lastReceive,(pp[i].fixed == 0) ? "falase" : "true");
 | 
	
		
			
				|  |  | +		buf.append(tmp);
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +	buf.push_back(']');
 | 
	
		
			
				|  |  | +	return buf;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  |  static void _jsonAppend(std::string &buf,const ZT1_VirtualNetworkConfig *nc)
 | 
	
		
			
				|  |  |  {
 | 
	
		
			
				|  |  |  	char json[65536];
 | 
	
	
		
			
				|  | @@ -141,6 +157,33 @@ static void _jsonAppend(std::string &buf,const ZT1_VirtualNetworkConfig *nc)
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  static void _jsonAppend(std::string &buf,const ZT1_Peer *peer)
 | 
	
		
			
				|  |  |  {
 | 
	
		
			
				|  |  | +	char json[65536];
 | 
	
		
			
				|  |  | +	const char *prole = "";
 | 
	
		
			
				|  |  | +	switch(peer->role) {
 | 
	
		
			
				|  |  | +		case ZT1_PEER_ROLE_LEAF:      prole = "LEAF"; break;
 | 
	
		
			
				|  |  | +		case ZT1_PEER_ROLE_HUB:       prole = "HUB"; break;
 | 
	
		
			
				|  |  | +		case ZT1_PEER_ROLE_SUPERNODE: prole = "SUPERNODE"; break;
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +	Utils::snprintf(json,sizeof(json),
 | 
	
		
			
				|  |  | +		"{"
 | 
	
		
			
				|  |  | +		"\"address\": \"%.10llx\","
 | 
	
		
			
				|  |  | +		"\"versionMajor\": %d,"
 | 
	
		
			
				|  |  | +		"\"versionMinor\": %d,"
 | 
	
		
			
				|  |  | +		"\"versionRev\": %d,"
 | 
	
		
			
				|  |  | +		"\"version\": \"%d.%d.%d\","
 | 
	
		
			
				|  |  | +		"\"latency\": %u,"
 | 
	
		
			
				|  |  | +		"\"role\": \"%s\","
 | 
	
		
			
				|  |  | +		"\"paths\": %s"
 | 
	
		
			
				|  |  | +		"}",
 | 
	
		
			
				|  |  | +		peer->address,
 | 
	
		
			
				|  |  | +		peer->versionMajor,
 | 
	
		
			
				|  |  | +		peer->versionMinor,
 | 
	
		
			
				|  |  | +		peer->versionRev,
 | 
	
		
			
				|  |  | +		peer->versionMajor,peer->versionMinor,peer->versionRev,
 | 
	
		
			
				|  |  | +		peer->latency,
 | 
	
		
			
				|  |  | +		prole,
 | 
	
		
			
				|  |  | +		_jsonEnumerate(peer->paths,peer->pathCount).c_str());
 | 
	
		
			
				|  |  | +	buf.append(json);
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  ControlPlane::ControlPlane(Node *n,const std::set<std::string> atoks) :
 | 
	
	
		
			
				|  | @@ -192,6 +235,7 @@ unsigned int ControlPlane::handleRequest(
 | 
	
		
			
				|  |  |  		if (ps[0] == "index") {
 | 
	
		
			
				|  |  |  			responseContentType = "text/html";
 | 
	
		
			
				|  |  |  			responseBody = "<html><body>Hello World!</body></html>";
 | 
	
		
			
				|  |  | +			scode = 200;
 | 
	
		
			
				|  |  |  		} else if (ps[0] == "status") {
 | 
	
		
			
				|  |  |  			responseContentType = "application/json";
 | 
	
		
			
				|  |  |  			ZT1_NodeStatus status;
 | 
	
	
		
			
				|  | @@ -203,7 +247,7 @@ unsigned int ControlPlane::handleRequest(
 | 
	
		
			
				|  |  |  				"\"online\":%s,"
 | 
	
		
			
				|  |  |  				"\"versionMajor\":%d,"
 | 
	
		
			
				|  |  |  				"\"versionMinor\":%d,"
 | 
	
		
			
				|  |  | -				"\"versionRevision\":%d,"
 | 
	
		
			
				|  |  | +				"\"versionRev\":%d,"
 | 
	
		
			
				|  |  |  				"\"version\":\"%d.%d.%d\""
 | 
	
		
			
				|  |  |  				"}",
 | 
	
		
			
				|  |  |  				status.address,
 | 
	
	
		
			
				|  | @@ -214,9 +258,11 @@ unsigned int ControlPlane::handleRequest(
 | 
	
		
			
				|  |  |  				ZEROTIER_ONE_VERSION_REVISION,
 | 
	
		
			
				|  |  |  				ZEROTIER_ONE_VERSION_MAJOR,ZEROTIER_ONE_VERSION_MINOR,ZEROTIER_ONE_VERSION_REVISION);
 | 
	
		
			
				|  |  |  			responseBody = json;
 | 
	
		
			
				|  |  | +			scode = 200;
 | 
	
		
			
				|  |  |  		} else if (ps[0] == "config") {
 | 
	
		
			
				|  |  |  			responseContentType = "application/json";
 | 
	
		
			
				|  |  |  			responseBody = "{}"; // TODO
 | 
	
		
			
				|  |  | +			scode = 200;
 | 
	
		
			
				|  |  |  		} else if (ps[0] == "network") {
 | 
	
		
			
				|  |  |  			ZT1_VirtualNetworkList *nws = _node->networks();
 | 
	
		
			
				|  |  |  			if (nws) {
 | 
	
	
		
			
				|  | @@ -230,27 +276,53 @@ unsigned int ControlPlane::handleRequest(
 | 
	
		
			
				|  |  |  						_jsonAppend(responseBody,&(nws->networks[i]));
 | 
	
		
			
				|  |  |  					}
 | 
	
		
			
				|  |  |  					responseBody.push_back(']');
 | 
	
		
			
				|  |  | +					scode = 200;
 | 
	
		
			
				|  |  |  				} else if (ps.size() == 2) {
 | 
	
		
			
				|  |  |  					// Return a single network by ID or 404 if not found
 | 
	
		
			
				|  |  |  					uint64_t wantnw = Utils::hexStrToU64(ps[1].c_str());
 | 
	
		
			
				|  |  | -					bool got = false;
 | 
	
		
			
				|  |  |  					for(unsigned long i=0;i<nws->networkCount;++i) {
 | 
	
		
			
				|  |  |  						if (nws->networks[i].nwid == wantnw) {
 | 
	
		
			
				|  |  | -							got = true;
 | 
	
		
			
				|  |  |  							responseContentType = "application/json";
 | 
	
		
			
				|  |  |  							_jsonAppend(responseBody,&(nws->networks[i]));
 | 
	
		
			
				|  |  | +							scode = 200;
 | 
	
		
			
				|  |  |  							break;
 | 
	
		
			
				|  |  |  						}
 | 
	
		
			
				|  |  |  					}
 | 
	
		
			
				|  |  | -					if (!got)
 | 
	
		
			
				|  |  | -						scode = 404;
 | 
	
		
			
				|  |  | -				} else {
 | 
	
		
			
				|  |  | -					// Not sure what they want here, 404
 | 
	
		
			
				|  |  | -					scode = 404;
 | 
	
		
			
				|  |  | -				}
 | 
	
		
			
				|  |  | +				} // else 404
 | 
	
		
			
				|  |  |  				_node->freeQueryResult((void *)nws);
 | 
	
		
			
				|  |  | -			} else scode = 500;
 | 
	
		
			
				|  |  | +			} else {
 | 
	
		
			
				|  |  | +				scode = 500;
 | 
	
		
			
				|  |  | +			}
 | 
	
		
			
				|  |  |  		} else if (ps[0] == "peer") {
 | 
	
		
			
				|  |  | +			ZT1_PeerList *pl = _node->peers();
 | 
	
		
			
				|  |  | +			if (pl) {
 | 
	
		
			
				|  |  | +				if (ps.size() == 1) {
 | 
	
		
			
				|  |  | +					// Return [array] of all peers
 | 
	
		
			
				|  |  | +					responseContentType = "application/json";
 | 
	
		
			
				|  |  | +					responseBody = "[";
 | 
	
		
			
				|  |  | +					for(unsigned long i=0;i<pl->peerCount;++i) {
 | 
	
		
			
				|  |  | +						if (i > 0)
 | 
	
		
			
				|  |  | +							responseBody.push_back(',');
 | 
	
		
			
				|  |  | +						_jsonAppend(responseBody,&(pl->peers[i]));
 | 
	
		
			
				|  |  | +					}
 | 
	
		
			
				|  |  | +					responseBody.push_back(']');
 | 
	
		
			
				|  |  | +					scode = 200;
 | 
	
		
			
				|  |  | +				} else if (ps.size() == 2) {
 | 
	
		
			
				|  |  | +					// Return a single peer by ID or 404 if not found
 | 
	
		
			
				|  |  | +					uint64_t wantp = Utils::hexStrToU64(ps[1].c_str());
 | 
	
		
			
				|  |  | +					for(unsigned long i=0;i<pl->peerCount;++i) {
 | 
	
		
			
				|  |  | +						if (pl->peers[i].address == wantp) {
 | 
	
		
			
				|  |  | +							responseContentType = "application/json";
 | 
	
		
			
				|  |  | +							_jsonAppend(responseBody,&(pl->peers[i]));
 | 
	
		
			
				|  |  | +							scode = 200;
 | 
	
		
			
				|  |  | +							break;
 | 
	
		
			
				|  |  | +						}
 | 
	
		
			
				|  |  | +					}
 | 
	
		
			
				|  |  | +				} // else 404
 | 
	
		
			
				|  |  | +				_node->freeQueryResult((void *)pl);
 | 
	
		
			
				|  |  | +			} else {
 | 
	
		
			
				|  |  | +				scode = 500;
 | 
	
		
			
				|  |  | +			}
 | 
	
		
			
				|  |  |  		}
 | 
	
		
			
				|  |  |  	} else if ((httpMethod == HTTP_POST)||(httpMethod == HTTP_PUT)) { // PUT is weird in REST but we'll take it anyway
 | 
	
		
			
				|  |  |  	} else if (httpMethod == HTTP_DELETE) {
 |