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 	private void parse()
99 	{
100 		foreach(string item; nodeInfoJSON.object().keys)
101 		{
102 			if(cmp(item, "name") == 0)
103 			{
104 				nodeName = nodeInfoJSON["name"].str();
105 			}
106 			else if(cmp(item, "operator") == 0)
107 			{
108 				operatorBlock = nodeInfoJSON["operator"];
109 			}
110 			else if(cmp(item, "group") == 0)
111 			{
112 				groupName = nodeInfoJSON["group"].str();
113 			}
114 			else if(cmp(item, "donaldtrumpispapi") == 0)
115 			{
116 				country = nodeInfoJSON["donaldtrumpispapi"].str();
117 			}
118 		}
119 	
120 	}
121 
122 	public override string toString()
123 	{
124 		return key~" (Name: "~nodeName~", Group: "~groupName~", Operator: "~to!(string)(operatorBlock)~")";
125 	}
126 }
127 
128 /**
129 * YggdrasilNode
130 *
131 * Given a key
132 */
133 public class YggdrasilNode
134 {
135 	public static YggdrasilPeer peer;
136 	
137 	private string key;
138 	private JSONValue selfInfo;
139 	private JSONValue nodeInfo;
140 	
141 	
142 	this(string key)
143 	{
144 		this.key = key;
145 	}
146 
147 	public NodeInfo getNodeInfo()
148 	{
149 		YggdrasilRequest req = new YggdrasilRequest(RequestType.NODEINFO, key);
150 		return new NodeInfo(sillyWillyRequest(peer, req));
151 	}
152 
153 	public YggdrasilNode[] getPeers()
154 	{
155 		YggdrasilNode[] peers;
156 
157 		YggdrasilRequest req = new YggdrasilRequest(RequestType.GETPEERS, key);
158 		JSONValue resp = sillyWillyRequest(peer, req);
159 
160 		if(resp.type != JSONType.null_)
161 		{
162 			foreach(JSONValue ckey; resp[resp.object().keys[0]]["keys"].array())
163 			{
164 				string ckeyStr = ckey.str();
165 				peers ~= new YggdrasilNode(ckeyStr);
166 			}
167 		}
168 
169 		return peers;
170 	}
171 
172 	public string getKey()
173 	{
174 		return key;
175 	}
176 
177 	public override string toString()
178 	{
179 		/* TODO: Fetch getNodeInfo, if possible, else leave key */
180 				
181 				return getNodeInfo().toString();
182 	}
183 }
184 
185 /**
186 * YggdrasilNode
187 *
188 * This represents a peer of which we can
189 * connect to its control socket using TCP
190 */
191 public class YggdrasilPeer
192 {
193 	private Address yggdrasilAddress;
194 
195 	this(Address yggdrasilAddress)
196 	{
197 		this.yggdrasilAddress = yggdrasilAddress;
198 
199 		YggdrasilNode.peer = this;
200 		/* Fetch data over socket and set */
201 		
202 	}
203 
204 	public Address getAddress()
205 	{
206 		return yggdrasilAddress;
207 	}
208 
209 	private void initData()
210 	{
211 		/* TODO: Add exception throwing here */
212 
213 			
214 	}
215 
216 	public YggdrasilNode fetchNode(string key)
217 	{
218 		return new YggdrasilNode(key);		
219 	}
220 }
221 
222 public enum RequestType
223 {
224 	NODEINFO, GETDHT, GETPEERS, GETSELF
225 }
226 
227 public final class YggdrasilRequest
228 {
229 	private RequestType requestType;
230 	private string key;
231 
232 	this(RequestType requestType, string key)
233 	{
234 		this.requestType = requestType;
235 		this.key = key;
236 	} 
237 
238 
239 	public JSONValue generateJSON()
240 	{
241 		JSONValue requestBlock;
242 
243 		/* Set the key of the node to request from */
244 		requestBlock["key"] = key;
245 
246 		if(requestType == RequestType.NODEINFO)
247 		{
248 			requestBlock["request"] = "getnodeinfo";
249 		}
250 		else if(requestType == RequestType.GETSELF)
251 		{
252 			requestBlock["request"] = "debug_remotegetself";
253 		}
254 		else if(requestType == RequestType.GETPEERS)
255 		{
256 			requestBlock["request"] = "debug_remotegetpeers";
257 		}
258 		else if(requestType == RequestType.GETDHT)
259 		{
260 			requestBlock["request"] = "debug_remotegetdht";
261 		}
262 		
263 
264 
265 		return requestBlock;
266 	}
267 }
268 
269 /* TODO: Fix read here */
270 public JSONValue sillyWillyRequest(YggdrasilPeer peer, YggdrasilRequest request)
271 {
272 	Socket controlSocket = new Socket(AddressFamily.INET6, SocketType.STREAM, ProtocolType.TCP);
273 	controlSocket.connect(peer.getAddress());
274 
275 	JSONValue requestBlock = request.generateJSON();
276 	controlSocket.send(cast(byte[])toJSON(requestBlock));
277 
278 
279 	// TODO: Add loop reader here
280 	byte[] buffer;
281 	buffer.length = 100000;
282 	controlSocket.receive(buffer);
283 
284 
285 	writeln(parseJSON(cast(string)buffer));
286 	JSONValue responseBlock;
287 
288 	if(cmp((parseJSON(cast(string)buffer)["status"]).str(), "success") == 0)
289 	{
290 		responseBlock = parseJSON(cast(string)buffer)["response"];
291 	}
292 
293 	
294 	
295 	return responseBlock;
296 }