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 }