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 }