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 }