1 /* 2 * Copyright (c) 2018-2020 sel-project 3 * 4 * Permission is hereby granted, free of charge, to any person obtaining a copy 5 * of this software and associated documentation files (the "Software"), to deal 6 * in the Software without restriction, including without limitation the rights 7 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 * copies of the Software, and to permit persons to whom the Software is 9 * furnished to do so, subject to the following conditions: 10 * 11 * The above copyright notice and this permission notice shall be included in all 12 * copies or substantial portions of the Software. 13 * 14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 * SOFTWARE. 21 * 22 */ 23 /** 24 * Copyright: Copyright (c) 2018-2020 sel-project 25 * License: MIT 26 * Authors: Kripth 27 * Source: $(HTTP github.com/sel-project/sel-util/raknet/sel/raknet/handler.d, sel/raknet/handler.d) 28 */ 29 module sel.raknet.handler; 30 31 import std.algorithm : min; 32 import std.bitmanip : nativeToLittleEndian, littleEndianToNative; 33 import std.conv : to; 34 import std.math : ceil; 35 import std.socket : Address; 36 import std.system : Endian; 37 38 import kiss.net.UdpSocket : UdpSocket; 39 40 import sel.raknet.packet : Ack, Nack; 41 42 import xbuffer : Buffer; 43 44 import std.stdio; 45 46 class RaknetHandler { 47 48 private UdpSocket socket; 49 private Address address; 50 public immutable ushort mtu; 51 52 private Buffer buffer; 53 54 public bool acceptSplit = true; 55 private ubyte[][][ushort] splits; 56 private size_t[ushort] splitsCount; 57 58 private int send_count = -1; 59 private ushort split_id = 0; 60 61 private ubyte[][int] sent; 62 63 public this(UdpSocket socket, Address address, ushort mtu) { 64 this.socket = socket; 65 this.address = address; 66 this.mtu = mtu; 67 this.buffer = new Buffer(mtu + 128); 68 } 69 70 public void send(ubyte[] _buffer) { 71 if(_buffer.length > this.mtu) { 72 immutable count = to!uint(ceil(_buffer.length.to!float / this.mtu)); 73 immutable sizes = to!uint(ceil(_buffer.length.to!float / count)); 74 foreach(order ; 0..count) { 75 immutable c = ++this.send_count; 76 ubyte[] current = _buffer[order*sizes..min((order+1)*sizes, $)]; 77 ubyte[3] _count = nativeToLittleEndian(c)[0..3]; 78 buffer.data = [ubyte(140)]; 79 buffer.write(_count); 80 buffer.write(ubyte(64 | 16)); // info 81 buffer.write!(Endian.bigEndian)(cast(ushort)(current.length * 8)); 82 buffer.write(_count); // message index 83 buffer.write!(Endian.bigEndian)(count); 84 buffer.write!(Endian.bigEndian)(this.split_id); 85 buffer.write!(Endian.bigEndian)(order); 86 buffer.write(current); 87 this.socket.sendTo(buffer.data, this.address); 88 this.sent[c] = buffer.data!ubyte; 89 } 90 this.split_id++; 91 } else { 92 immutable c = ++this.send_count; 93 ubyte[3] count = nativeToLittleEndian(c)[0..3]; 94 buffer.data = [ubyte(132)]; 95 buffer.write(count); 96 buffer.write(ubyte(64)); // info 97 buffer.write!(Endian.bigEndian)(cast(ushort)(_buffer.length * 8)); 98 buffer.write(count); // message index 99 buffer.write(_buffer); 100 this.sent[c] = buffer.data!ubyte; 101 this.socket.sendTo(buffer.data, this.address); 102 } 103 } 104 105 public ubyte[] handle(in ubyte[] buffer) { 106 if(buffer.length) { 107 switch(buffer[0]) { 108 case Ack.ID: 109 Ack packet = new Ack(); 110 packet.decode(buffer); 111 //writeln("Ack: ", packet.packets); 112 foreach(ack ; packet.packets) { 113 if(ack.unique) { 114 this.sent.remove(ack.first); 115 } else { 116 foreach(id ; ack.first..ack.last) this.sent.remove(id); 117 } 118 } 119 break; 120 case Nack.ID: 121 Nack packet = new Nack(); 122 packet.decode(buffer); 123 //writeln("Nack: ", packet.packets); 124 void send(uint id) { 125 auto sent = id in this.sent; 126 if(sent) this.socket.sendTo(*sent, this.address); 127 } 128 foreach(nack ; packet.packets) { 129 if(nack.unique) { 130 send(nack.first); 131 } else { 132 foreach(id ; nack.first..nack.last) send(id); 133 } 134 } 135 break; 136 case 128:..case 143: 137 if(buffer.length > 7) { 138 ubyte[4] _count = buffer[1..4] ~ ubyte(0); 139 immutable count = littleEndianToNative!int(_count); 140 // send ack 141 // id, length (2), unique, from (3), to (3) 142 this.socket.sendTo([ubyte(192), ubyte(0), ubyte(1), ubyte(true)] ~ buffer[1..4], this.address); 143 // handle packet 144 this.buffer.data = buffer[4..$]; 145 size_t index = 4; 146 immutable info = this.buffer.read!ubyte(); 147 this.buffer.read!ushort(); // length / 8 148 if((info & 0x7F) >= 64) { 149 this.buffer.readData(3); // message index 150 if((info & 0x7F) >= 96) { 151 this.buffer.readData(3); // order index 152 this.buffer.readData(1); // order channel 153 } 154 } 155 if(info & 0x10) { 156 if(this.buffer.canRead(10) && this.acceptSplit) { 157 return this.handleSplit(this.buffer.read!(Endian.bigEndian, uint)(), this.buffer.read!(Endian.bigEndian, ushort)(), this.buffer.read!(Endian.bigEndian, uint)(), this.buffer.data!ubyte); 158 } 159 } else { 160 return this.buffer.data!ubyte; 161 } 162 } 163 break; 164 default: 165 break; 166 } 167 } 168 return []; 169 } 170 171 private ubyte[] handleSplit(uint count, ushort id, uint order, ubyte[] buffer) { 172 auto split = id in this.splits; 173 if(split is null) { 174 //TODO limit count 175 this.splits[id].length = count; 176 split = id in this.splits; 177 } 178 if(count == (*split).length && order < count) { 179 (*split)[order] = buffer.dup; 180 if(++this.splitsCount[id] == count) { 181 ubyte[] ret; 182 foreach(b ; *split) { 183 ret ~= b.dup; 184 } 185 this.splits.remove(id); 186 this.splitsCount.remove(id); 187 return ret; 188 } 189 } 190 return []; 191 } 192 193 private static int readTriad(ubyte[] data) { 194 ubyte[4] bytes = data ~ ubyte(0); 195 return littleEndianToNative!int(bytes); 196 } 197 198 private static int[] getAck(ubyte[] buffer) { 199 int[] ret; 200 size_t index = 1; 201 foreach(i ; 0..buffer[index++]) { 202 if(buffer[index++]) { 203 ret ~= readTriad(buffer[index..index+=3]); 204 } else { 205 foreach(num ; readTriad(buffer[index..index+=3])..readTriad(buffer[index..index+=3])+1) { 206 ret ~= num; 207 } 208 } 209 } 210 return ret; 211 } 212 213 }