1 module libyggdrasil;
2 
3 import std.stdio;
4 
5 import std.json;
6 import std.socket;
7 import std.string;
8 
9 private string[] getKeys()
10 {
11 	string url = "http://[21e:e795:8e82:a9e2:ff48:952d:55f2:f0bb]/static/current";
12 	string[] keys;
13 	
14 	import std.net.curl;
15 	string k = cast(string)get(url);
16 
17 	//writeln(k);
18 
19 	JSONValue json = parseJSON(k);
20 	json = json["yggnodes"];
21 
22 	keys = json.object().keys;
23 //	writeln(keys);
24 
25 	return keys;
26 }
27 
28 void main()
29 {
30 
31 
32 	
33 	
34 	Address testNode = parseAddress("201:6c56:f9d5:b7a5:8f42:b1ab:9e0e:5169", 9090);
35 	YggdrasilPeer yggNode = new YggdrasilPeer(testNode);
36 
37 	string[] keys = ["a1b0169eae0b6fb808c60fe82f29855a01e173b3a16bb286cfcfc0ed45a28afb",
38 					"b563daa08870d769e7871f581afb9ee339b8a10c2baa45f334beb1d74b0700d7"];
39 
40 	keys = getKeys();
41 	YggdrasilNode[] nodes;
42 	foreach(string k; keys)
43 	{
44 		nodes ~= yggNode.fetchNode(k);
45 	}
46 
47 	foreach(YggdrasilNode node; nodes)
48 	{
49 		writeln(node.getNodeInfo());
50 		writeln("Peers: "~to!(string)(node.getPeers()));
51 		
52 		YggdrasilRequest req = new YggdrasilRequest(RequestType.GETDHT, node.getKey());
53 		writeln(sillyWillyRequest(yggNode, req).toPrettyString());
54 		req = new YggdrasilRequest(RequestType.GETDHT, node.getKey());
55 		writeln(sillyWillyRequest(yggNode, req).toPrettyString());
56 		req = new YggdrasilRequest(RequestType.GETPEERS, node.getKey());
57 		writeln(sillyWillyRequest(yggNode, req).toPrettyString());
58 		req = new YggdrasilRequest(RequestType.GETSELF, node.getKey());
59 		writeln(sillyWillyRequest(yggNode, req).toPrettyString());
60 		
61 	}
62 
63 }
64 
65 import std.conv;
66 
67 public class NodeInfo
68 {
69 	private JSONValue nodeInfoJSON;
70 	private JSONValue operatorBlock;
71 	private string nodeName = "<no name>";
72 	private string groupName = "<no name>";
73 	private string country = "<no name>";
74 	private string key;
75 	/* TODO: Standardise */
76 	/**
77 	* Name
78 	* Owner
79 	* Contact
80 	* Group
81 	*/
82 	this(JSONValue nodeInfoJSON)
83 	{
84 		/* We only do one query (so it will be first) */
85 		writeln(nodeInfoJSON);
86 		if(nodeInfoJSON.type == JSONType.null_)
87 		{
88 			return;
89 		}
90 		key = nodeInfoJSON.object().keys[0];
91 		this.nodeInfoJSON = nodeInfoJSON[key];
92 
93 		
94 		parse();
95 		
96 	}
97 
98 	public string getName()
99 	{
100 		return nodeName;
101 	}
102 
103 	public string getGroupName()
104 	{
105 		return groupName;
106 	}
107 
108 	public string getCountry()
109 	{
110 		return country;
111 	}
112 
113 	public string getAddress()
114 	{
115 		return "unavailable";
116 	}
117 
118 	public JSONValue getOperatorBlock()
119 	{
120 		return operatorBlock;
121 	}
122 
123 	private void parse()
124 	{
125 		foreach(string item; nodeInfoJSON.object().keys)
126 		{
127 			if(cmp(item, "name") == 0)
128 			{
129 				nodeName = nodeInfoJSON["name"].str();
130 			}
131 			else if(cmp(item, "operator") == 0)
132 			{
133 				operatorBlock = nodeInfoJSON["operator"];
134 			}
135 			else if(cmp(item, "group") == 0)
136 			{
137 				groupName = nodeInfoJSON["group"].str();
138 			}
139 			else if(cmp(item, "donaldtrumpispapi") == 0)
140 			{
141 				country = nodeInfoJSON["donaldtrumpispapi"].str();
142 			}
143 		}
144 	
145 	}
146 
147 	public override string toString()
148 	{
149 		return key~" (Name: "~nodeName~", Group: "~groupName~", Operator: "~to!(string)(operatorBlock)~")";
150 	}
151 }
152 
153 /**
154 * YggdrasilNode
155 *
156 * Given a key
157 */
158 public class YggdrasilNode
159 {
160 	public static YggdrasilPeer peer;
161 	
162 	private string key;
163 	private JSONValue selfInfo;
164 	private JSONValue nodeInfo;
165 	
166 	
167 	this(string key)
168 	{
169 		this.key = key;
170 	}
171 
172 	public NodeInfo getNodeInfo()
173 	{
174 		YggdrasilRequest req = new YggdrasilRequest(RequestType.NODEINFO, key);
175 		return new NodeInfo(sillyWillyRequest(peer, req));
176 	}
177 
178 	public YggdrasilNode[] getPeers()
179 	{
180 		YggdrasilNode[] peers;
181 
182 		YggdrasilRequest req = new YggdrasilRequest(RequestType.GETPEERS, key);
183 		JSONValue resp = sillyWillyRequest(peer, req);
184 
185 		if(resp.type != JSONType.null_)
186 		{
187 			foreach(JSONValue ckey; resp[resp.object().keys[0]]["keys"].array())
188 			{
189 				string ckeyStr = ckey.str();
190 				peers ~= new YggdrasilNode(ckeyStr);
191 			}
192 		}
193 
194 		return peers;
195 	}
196 
197 	public string getKey()
198 	{
199 		return key;
200 	}
201 
202 	public override string toString()
203 	{
204 		/* TODO: Fetch getNodeInfo, if possible, else leave key */
205 				
206 				return getNodeInfo().toString();
207 	}
208 }
209 
210 /**
211 * YggdrasilNode
212 *
213 * This represents a peer of which we can
214 * connect to its control socket using TCP
215 */
216 public class YggdrasilPeer
217 {
218 	private Address yggdrasilAddress;
219 
220 	this(Address yggdrasilAddress)
221 	{
222 		this.yggdrasilAddress = yggdrasilAddress;
223 
224 		YggdrasilNode.peer = this;
225 		/* Fetch data over socket and set */
226 		
227 	}
228 
229 	public Address getAddress()
230 	{
231 		return yggdrasilAddress;
232 	}
233 
234 	private void initData()
235 	{
236 		/* TODO: Add exception throwing here */
237 
238 			
239 	}
240 
241 	public YggdrasilNode fetchNode(string key)
242 	{
243 		return new YggdrasilNode(key);		
244 	}
245 }
246 
247 public enum RequestType
248 {
249 	NODEINFO, GETDHT, GETPEERS, GETSELF
250 }
251 
252 public final class YggdrasilRequest
253 {
254 	private RequestType requestType;
255 	private string key;
256 
257 	this(RequestType requestType, string key)
258 	{
259 		this.requestType = requestType;
260 		this.key = key;
261 	} 
262 
263 
264 	public JSONValue generateJSON()
265 	{
266 		JSONValue requestBlock;
267 
268 		/* Set the key of the node to request from */
269 		requestBlock["key"] = key;
270 
271 		if(requestType == RequestType.NODEINFO)
272 		{
273 			requestBlock["request"] = "getnodeinfo";
274 		}
275 		else if(requestType == RequestType.GETSELF)
276 		{
277 			requestBlock["request"] = "debug_remotegetself";
278 		}
279 		else if(requestType == RequestType.GETPEERS)
280 		{
281 			requestBlock["request"] = "debug_remotegetpeers";
282 		}
283 		else if(requestType == RequestType.GETDHT)
284 		{
285 			requestBlock["request"] = "debug_remotegetdht";
286 		}
287 		
288 
289 
290 		return requestBlock;
291 	}
292 }
293 
294 /* TODO: Fix read here */
295 public JSONValue sillyWillyRequest(YggdrasilPeer peer, YggdrasilRequest request)
296 {
297 	Socket controlSocket = new Socket(AddressFamily.INET6, SocketType.STREAM, ProtocolType.TCP);
298 	controlSocket.connect(peer.getAddress());
299 
300 	JSONValue requestBlock = request.generateJSON();
301 	controlSocket.send(cast(byte[])toJSON(requestBlock));
302 
303 
304 	// TODO: Add loop reader here
305 	byte[] buffer;
306 	buffer.length = 100000;
307 	controlSocket.receive(buffer);
308 
309 
310 	writeln(parseJSON(cast(string)buffer));
311 	JSONValue responseBlock;
312 
313 	if(cmp((parseJSON(cast(string)buffer)["status"]).str(), "success") == 0)
314 	{
315 		responseBlock = parseJSON(cast(string)buffer)["response"];
316 	}
317 
318 	
319 	
320 	return responseBlock;
321 }