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 }