1 module libsweatyballs.zwitch.core; 2 3 import libsweatyballs.engine.core : Engine; 4 import core.thread : Thread; 5 import crypto.rsa : RSA; 6 import libsweatyballs.zwitch.neighbor : Neighbor; 7 import core.sync.mutex : Mutex; 8 import std.string : cmp; 9 import std.conv : to; 10 import gogga; 11 import std.socket; 12 import libsweatyballs.router.table : Route; 13 import libsweatyballs.link.message.core; 14 import std.array : array; 15 import google.protobuf; 16 17 /** 18 * Switch 19 * 20 * Manages session (far) and neighbour comms 21 * 22 * You send and receive data here. The switch asks router for routes 23 * and then sends stuff on the next-hop to them (if direct) or via them 24 * (indirect/routed-through) 25 */ 26 public final class Switch : Thread 27 { 28 private Engine engine; 29 private Session[] sessions; 30 31 /** 32 * Neighboring nodes 33 */ 34 private Neighbor[] neighbors; 35 private Mutex neighborsLock; 36 37 /** 38 * Neighbor communications 39 * 40 * Node-to-node 41 */ 42 private Socket neighborSocket; 43 44 this(Engine engine) 45 { 46 /* Set the thread's worker function */ 47 super(&worker); 48 49 this.engine = engine; 50 51 /* Initialize locks */ 52 initMutexes(); 53 } 54 55 /** 56 * Initializes all the mutexes 57 */ 58 private void initMutexes() 59 { 60 neighborsLock = new Mutex(); 61 } 62 63 public Neighbor[] getNeighbors() 64 { 65 Neighbor[] copy; 66 67 neighborsLock.lock(); 68 foreach(Neighbor neighbor; neighbors) 69 { 70 copy ~= neighbor; 71 } 72 neighborsLock.unlock(); 73 74 return copy; 75 } 76 77 public void addNeighbor(Neighbor neighbor) 78 { 79 /* Lock the neighbors table */ 80 neighborsLock.lock(); 81 82 /* Add the route (only if it doesn't already exist) */ 83 foreach(Neighbor cNeighbour; neighbors) 84 { 85 /* If the neighbor is already known of */ 86 /* TODO: Add neighbor expirations */ 87 if(cmp(cNeighbour.getIdentity(), neighbor.getIdentity()) == 0) 88 { 89 goto no_add_neighbor; 90 } 91 } 92 93 neighbors ~= neighbor; 94 gprintln("NeighborDB: Added a new neighbor "~to!(string)(neighbor)); 95 96 no_add_neighbor: 97 98 /* Unlock the neighbors table */ 99 neighborsLock.unlock(); 100 } 101 102 private void initSockets() 103 { 104 neighborSocket = new Socket(AddressFamily.INET6, SocketType.DGRAM, ProtocolType.UDP); 105 } 106 107 private void worker() 108 { 109 /* TODO: Implement me */ 110 while(true) 111 { 112 113 } 114 } 115 116 public void launch() 117 { 118 start(); 119 } 120 121 public Neighbor isNeighbour(string address) 122 { 123 Neighbor match; 124 125 Neighbor[] neighbors = getNeighbors(); 126 foreach(Neighbor neighbor; neighbors) 127 { 128 if(cmp(neighbor.getIdentity(), address) == 0) 129 { 130 match = neighbor; 131 break; 132 } 133 } 134 135 return match; 136 } 137 138 139 private Packet constructPacket(string toAddress, string fromAddress, byte[] data) 140 { 141 /* The final message */ 142 Packet packet = new Packet(); 143 packet.fromKey = fromAddress; 144 packet.toKey = toAddress; 145 packet.siganture = "not yet implemented"; 146 packet.ttl = 64; 147 /* TODO: Generate signature */ 148 149 /* Encrypt the payload to `toAddress` (final destination) */ 150 ubyte[] encryptedPayload = RSA.encrypt(toAddress, cast(ubyte[])data); 151 packet.payload = encryptedPayload; 152 153 return packet; 154 } 155 156 // private LinkMessage constructLinkMessage(byte[] data, LinkMessageType type, string ) 157 158 /** 159 * Send a packet 160 * 161 * Send a packet containing `data` to node at `address` 162 * from `us` 163 */ 164 public void sendPacket(string address, byte[] data) 165 { 166 /* Construct the packet */ 167 Packet packet = constructPacket(address, engine.getRouter().getIdentity().getKeys().publicKey, data); 168 169 /* LinkMessage */ 170 LinkMessage linkMsg = new LinkMessage(); 171 linkMsg.type = LinkMessageType.PACKET; 172 linkMsg.publicKey = engine.getRouter().getIdentity().getKeys().publicKey; 173 linkMsg.signature = "not yet implemented"; 174 linkMsg.payload = cast(ubyte[])array(toProtobuf(packet)); 175 176 /* Next-hop (for delivery), this is either a router or destination direct */ 177 Neighbor nextHop; 178 179 180 /* Find out whether `address` is local (a neighbour) or not */ 181 Neighbor possibleNeighbor = isNeighbour(address); 182 if(possibleNeighbor) 183 { 184 gprintln("sendPacket: We are sending to a neighor", DebugType.WARNING); 185 nextHop = possibleNeighbor; 186 } 187 else 188 { 189 gprintln("sendPacket: We are sending to a node VIA router", DebugType.WARNING); 190 191 /* Make sure there is a route entry for it */ 192 Route routeToHost = engine.getRouter().getTable().lookup(address); 193 if(routeToHost) 194 { 195 /* Set the next hop to the neighbor with the address in the route entry */ 196 nextHop = routeToHost.getNexthop(); 197 gprintln("sendPacket: Next-hop (indirect): "~to!(string)(routeToHost)); 198 } 199 else 200 { 201 gprintln("sendPacket: No route to "~address, DebugType.ERROR); 202 return; 203 } 204 } 205 206 207 208 /* TODO: Validate key */ 209 210 /* TODO: Add signature */ 211 212 import libsweatyballs.link.message.core; 213 214 215 /* Set neighbor port depending on which link it goes out on */ 216 linkMsg.neighborPort = to!(string)(nextHop.getLink().getR2RPort()); 217 218 /* ProtoBuf encoded message */ 219 byte[] message = cast(byte[])array(toProtobuf(linkMsg)); 220 221 /* TODO: Open socket to Neighbor and send the ProtoBuf-encoded and encrypted payload packet */ 222 bool status = sendNBR(message, nextHop.getAddress()); 223 gprintln("SendNBR Status: "~to!(string)(status)); 224 /* TODO: Handle status */ 225 } 226 227 /** 228 * Opens a socket and sends `data` to `address` 229 * 230 * Returns status 231 */ 232 private bool sendNBR(byte[] data, Address address) 233 { 234 try 235 { 236 Socket neighborSocket = new Socket(AddressFamily.INET6, SocketType.DGRAM, ProtocolType.UDP); 237 long status = neighborSocket.sendTo(data, address); 238 239 return status > 0; 240 } 241 catch(SocketOSException) 242 { 243 return false; 244 } 245 } 246 247 public void forward(Packet packet) 248 { 249 /* Decrement ttl */ 250 packet.ttl--; 251 if(packet.ttl == 0) 252 { 253 gprintln("TTL REACHED, DAAI DING IS DOOD!", DebugType.ERROR); 254 return; 255 } 256 257 string address = packet.toKey; 258 259 /* Next-hop (for delivery), this is either a router or destination direct */ 260 Neighbor nextHop; 261 262 /* Make sure there is a route entry for it */ 263 Route routeToHost = engine.getRouter().getTable().lookup(address); 264 if(routeToHost) 265 { 266 /* Set the next hop to the neighbor with the address in the route entry */ 267 nextHop = routeToHost.getNexthop(); 268 gprintln("foward(): Next-hop (router): "~to!(string)(routeToHost)); 269 } 270 else 271 { 272 gprintln("foward(): No route to "~address, DebugType.ERROR); 273 return; 274 } 275 276 /* LinkMessage */ 277 LinkMessage linkMsg = new LinkMessage(); 278 linkMsg.type = LinkMessageType.PACKET; 279 linkMsg.publicKey = engine.getRouter().getIdentity().getKeys().publicKey; 280 linkMsg.signature = "not yet implemented"; 281 linkMsg.payload = cast(ubyte[])array(toProtobuf(packet)); 282 283 /* Set neighbor port depending on which link it goes out on */ 284 linkMsg.neighborPort = to!(string)(nextHop.getLink().getR2RPort()); 285 286 /* ProtoBuf encoded message */ 287 byte[] message = cast(byte[])array(toProtobuf(linkMsg)); 288 289 /* TODO: Open socket to Neighbor and send the ProtoBuf-encoded and encrypted payload packet */ 290 bool status = sendNBR(message, nextHop.getAddress()); 291 gprintln("SendNBR Status: "~to!(string)(status)); 292 /* TODO: Handle status */ 293 } 294 295 /* TODO: Move this elsewhere */ 296 public class Session : Thread 297 { 298 private string aesKey; 299 private string sessionID; 300 301 private Socket neighborSock; 302 303 this(Socket clientSocket) 304 { 305 super(&worker); 306 this.neighborSock = neighborSock; 307 } 308 309 private void worker() 310 { 311 312 } 313 } 314 315 private Session fetchSession(string address) 316 { 317 return null; 318 } 319 320 private Session createSession(string address) 321 { 322 /* TODO: Generate random AES key */ 323 324 return null; 325 } 326 327 private bool isSessionExists(string address) 328 { 329 /* Lock sessions */ 330 331 /* Find the session */ 332 foreach(Session session; sessions) 333 { 334 335 } 336 337 338 /* Unlockk sessions */ 339 340 return true; 341 } 342 }