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