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