1 /*
2  * This file is part of the libusi++ packet capturing/sending framework.
3  *
4  * (C) 2000-2017 by Sebastian Krahmer,
5  *                  sebastian [dot] krahmer [at] gmail [dot] com
6  *
7  * libusi++ is free software: you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation, either version 3 of the License, or
10  * (at your option) any later version.
11  *
12  * libusi++ is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with libusi++.  If not, see <http://www.gnu.org/licenses/>.
19  */
20 
21 
22 #include "usi++/usi++.h"
23 #include "usi++/tcp.h"
24 #include "usi++/RX.h"
25 #include "usi++/TX.h"
26 
27 #include <cstring>
28 #include <string>
29 #include <stdlib.h>
30 #include <errno.h>
31 #include <stdint.h>
32 #include <arpa/inet.h>
33 
34 
35 namespace usipp {
36 
37 
38 using namespace std;
39 
40 
41 /*! create a TCP object destined to 'host' */
42 template<typename T>
TCP(const string & host,RX * rx,TX * tx)43 TCP<T>::TCP(const string &host, RX *rx, TX *tx)
44      : T(host, numbers::ipproto_tcp, rx, tx)
45 {
46 	srand(time(nullptr));
47 	memset(&tcph, 0, sizeof(tcph));
48 	memset(tcpOptions, 0, sizeof(tcpOptions));
49 
50 	tcph.th_off = 5;
51 	tcph.th_seq = rand();
52 
53 	calc_tsum = 1;
54 }
55 
56 /*! get the sourceport in human-readable form
57  */
58 template<typename T>
get_srcport()59 uint16_t TCP<T>::get_srcport()
60 {
61 	return ntohs(tcph.th_sport);
62 }
63 
64 
65 template<typename T>
~TCP()66 TCP<T>::~TCP()
67 {
68 }
69 
70 
71 template<typename T>
TCP(const TCP<T> & rhs)72 TCP<T>::TCP(const TCP<T> &rhs)
73 	: T(rhs)
74 {
75 	if (this == &rhs)
76 		return;
77 	tcph = rhs.tcph;
78 	memcpy(tcpOptions, rhs.tcpOptions, sizeof(tcpOptions));
79 	calc_tsum = rhs.calc_tsum;
80 }
81 
82 
83 template<typename T>
operator =(const TCP<T> & rhs)84 TCP<T> &TCP<T>::operator=(const TCP<T> &rhs)
85 {
86 	if (this == &rhs)
87 		return *this;
88 	T::operator=(rhs);
89 	tcph = rhs.tcph;
90 	memcpy(tcpOptions, rhs.tcpOptions, sizeof(tcpOptions));
91 	calc_tsum = rhs.calc_tsum;
92 	return *this;
93 }
94 
95 
96 
97 /*! get the destinationport in human-readable form
98  */
99 template<typename T>
get_dstport()100 uint16_t TCP<T>::get_dstport()
101 {
102 	return ntohs(tcph.th_dport);
103 }
104 
105 /*! get TCP sequence number
106  */
107 template<typename T>
get_seq()108 uint32_t TCP<T>::get_seq()
109 {
110 	return ntohl(tcph.th_seq);
111 }
112 
113 /*! get the actual achnkowledge number from the TCP-header
114  */
115 template<typename T>
get_ack()116 uint32_t TCP<T>::get_ack()
117 {
118 	return ntohl(tcph.th_ack);
119 }
120 
121 /*! get TCP data offset.
122  */
123 template<typename T>
get_off()124 uint8_t TCP<T>::get_off()
125 {
126 	return tcph.th_off;
127 }
128 
129 /*! set TCP-flags
130  */
131 template<typename T>
get_flags()132 uint16_t TCP<T>::get_flags()
133 {
134 	return tcph.th_flags|tcph.th_x2<<8;
135 }
136 
137 
138 template<typename T>
get_win()139 uint16_t TCP<T>::get_win()
140 {
141 	return ntohs(tcph.th_win);
142 }
143 
144 
145 /*! get TCP checksum
146  */
147 template<typename T>
get_tcpsum()148 uint16_t TCP<T>::get_tcpsum()
149 {
150 	return tcph.th_sum;
151 }
152 
153 
154 template<typename T>
get_urg()155 uint16_t TCP<T>::get_urg()
156 {
157 	return ntohs(tcph.th_urp);
158 }
159 
160 
161 /*! set TCP sourceport
162  */
163 template<typename T>
set_srcport(uint16_t sp)164 uint16_t TCP<T>::set_srcport(uint16_t sp)
165 {
166 	tcph.th_sport = htons(sp);
167 	return sp;
168 }
169 
170 
171 /* Set TCP destination port.
172  */
173 template<typename T>
set_dstport(uint16_t dp)174 uint16_t TCP<T>::set_dstport(uint16_t dp)
175 {
176 	tcph.th_dport = htons(dp);
177 	return dp;
178 }
179 
180 
181 /* Set the sequencenumber-filed in the TCP-header.
182  */
183 template<typename T>
set_seq(uint32_t s)184 uint32_t TCP<T>::set_seq(uint32_t s)
185 {
186 	tcph.th_seq = htonl(s);
187 	return s;
188 }
189 
190 
191 /*! Set the acknowledgenumber-field in the TCP-header.
192  *  This is only monitored by the target-kernel, if TH_ACK
193  *  is set in the TCP-flags.
194  */
195 template<typename T>
set_ack(uint32_t a)196 uint32_t TCP<T>::set_ack(uint32_t a)
197 {
198 	tcph.th_ack = htonl(a);
199 	return a;
200 }
201 
202 
203 /*! set TCP data offset.
204  */
205 template<typename T>
set_off(uint8_t o)206 uint8_t TCP<T>::set_off(uint8_t o)
207 {
208 	return tcph.th_off = o;
209 }
210 
211 
212 /*! set TCP-flags
213  */
214 template<typename T>
set_flags(uint16_t f)215 uint16_t TCP<T>::set_flags(uint16_t f)
216 {
217 	// flags only have 8 bits, if more, it sets the unused bits in the header
218 	tcph.th_flags = f & 0xff;
219 	tcph.th_x2 = f>>8;
220 
221 	return f;
222 }
223 
224 /*! set TCP window */
225 template<typename T>
set_win(uint16_t w)226 uint16_t TCP<T>::set_win(uint16_t w)
227 {
228 	tcph.th_win = htons(w);
229 	return w;
230 }
231 
232 
233 /*! set TCP-checksum and prevent
234  *  sendpack from calculating the checksum.
235  */
236 template<typename T>
set_tcpsum(uint16_t s)237 uint16_t TCP<T>::set_tcpsum(uint16_t s)
238 {
239 	tcph.th_sum = s;
240 	calc_tsum = 0;
241 	return s;
242 }
243 
244 
245 template<typename T>
tchecksum(bool cs)246 void TCP<T>::tchecksum(bool cs)
247 {
248 	calc_tsum = cs;
249 }
250 
251 /*! set TCP urgent pointer for OOB data */
252 template<typename T>
set_urg(uint16_t u)253 uint16_t TCP<T>::set_urg(uint16_t u)
254 {
255 	tcph.th_urp = htons(u);
256 	return u;
257 }
258 
259 
260 /*! get raw TCP header */
261 template<typename T>
get_tcphdr()262 tcphdr &TCP<T>::get_tcphdr()
263 {
264 	return tcph;
265 }
266 
267 
268 /*!  send a TCP-packet containing 'buf' data of 'paylen' bytes
269  */
270 template<typename T>
sendpack(const void * buf,size_t paylen)271 int TCP<T>::sendpack(const void *buf, size_t paylen)
272 {
273 	if (paylen > max_packet_size || paylen + sizeof(T::d_pseudo) + sizeof(tcph) + sizeof(tcpOptions) + 1 > max_packet_size)
274 		return T::die("TCP::sendpack: Packet payload too large.", STDERR, -1);
275 
276 	unsigned int len = paylen + (tcph.th_off<<2) + sizeof(T::d_pseudo);
277 	int r = 0;
278 	char tmp[max_packet_size] = {0};
279 
280 	// build a pseudoheader for IP-checksum
281 	T::d_pseudo.saddr = T::get_src();	// source address
282 	T::d_pseudo.daddr = T::get_dst();	// destination address
283 	T::d_pseudo.proto = numbers::ipproto_tcp;
284 
285 	if (T::d_ipversion == 4)
286 		T::d_pseudo.len = htons((tcph.th_off<<2) + paylen);
287 	else {
288 		T::d_pseudo.len = htonl((tcph.th_off<<2) + paylen);
289 
290 		// For routing extension header, the csum is calculated with the real
291 		// destination
292 
293 		if (T::get_proto() == numbers::ipproto6_routing) {
294 			if (T::e_hdrs_len >= 24 && T::e_hdrs.begin() != T::e_hdrs.end())
295 				memcpy(&T::d_pseudo.daddr, T::e_hdrs.begin()->c_str() + T::e_hdrs.begin()->size() - 16, 16);
296 		}
297 
298 		for (auto i = T::e_hdrs.begin(); i != T::e_hdrs.end(); ++i) {
299 			if (i->size() >= 24 && (*i)[0] == numbers::ipproto6_routing)
300 				memcpy(&T::d_pseudo.daddr, i->c_str() + i->size() - 16, 16);
301 		}
302 	}
303 
304 	// copy pseudohdr + header + data to buffer
305 	memcpy(tmp, &this->d_pseudo, sizeof(T::d_pseudo));
306 	memcpy(tmp + sizeof(T::d_pseudo), &tcph, sizeof(tcph));
307 
308 	// options, might be 0-length
309 	if ((tcph.th_off<<2) > (int)sizeof(tcph))
310 		memcpy(tmp + sizeof(T::d_pseudo) + sizeof(tcph), tcpOptions, (tcph.th_off<<2)-sizeof(tcph));
311 
312 	// data
313 	memcpy(tmp + sizeof(T::d_pseudo) + (tcph.th_off<<2), buf, paylen);
314 
315 	// calc checksum over i
316 	struct tcphdr *t = reinterpret_cast<struct tcphdr *>(tmp + sizeof(T::d_pseudo));
317 
318 	if (calc_tsum) {
319 		t->th_sum = 0;
320 		t->th_sum = in_cksum(reinterpret_cast<unsigned short *>(tmp), len, 1);
321 	}
322 
323 	r = T::sendpack(tmp + sizeof(T::d_pseudo), len - sizeof(T::d_pseudo));
324 
325 	return r;
326 }
327 
328 
329 /*! send a TCP packet containing string 's' */
330 template<typename T>
sendpack(const string & s)331 int TCP<T>::sendpack(const string &s)
332 {
333 	return sendpack(s.c_str(), s.length());
334 }
335 
336 /*! sniff a TCP packet */
337 template<typename T>
sniffpack(string & s)338 string &TCP<T>::sniffpack(string &s)
339 {
340 	int off = 0;
341 	s = "";
342 	char buf[max_packet_size];
343 	int r = this->sniffpack(buf, sizeof(buf), off);
344 	if (r > off)
345 		s = string(buf + off, r - off);
346 	return s;
347 }
348 
349 
350 /*! sniff a TCP-packet.
351  */
352 template<typename T>
sniffpack(void * s,size_t len)353 int TCP<T>::sniffpack(void *s, size_t len)
354 {
355 	int off = 0;
356 	int r = sniffpack(s, len, off);
357 	if (r <= 0)
358 		return r;
359 	if (r <= off)
360 		return 0;
361 	if (off > 0)
362 		memmove(s, reinterpret_cast<char *>(s) + off, r - off);
363 	return r - off;
364 }
365 
366 
367 /*! sniff a TCP-packet.
368  */
369 template<typename T>
sniffpack(void * buf,size_t len,int & off)370 int TCP<T>::sniffpack(void *buf, size_t len, int &off)
371 {
372 	off = 0;
373 	int r = T::sniffpack(buf, len, off);
374 
375 	if (r == 0 && Layer2::timeout())
376 		return 0;
377 	else if (r < 0)
378 		return -1;
379 	else if (r < off + (int)sizeof(tcph))
380 		return T::die("TCP::sniffpack: short packet", STDERR, -1);
381 
382 	// Copy TCP-header without options
383 	memcpy(&tcph, reinterpret_cast<char *>(buf) + off, sizeof(tcph));
384 
385 	unsigned int tcplen = tcph.th_off<<2;
386 	if (tcplen < (int)sizeof(tcph)) {
387 		off += sizeof(tcph);
388 		return r;
389 	}
390 
391 	// cant happen: only 4bit tcp offset, -> max of 40byte options == sizeof(tcpOptions)
392 	//if (tcplen > sizeof(tcph) + sizeof(tcpOptions))
393 
394 	// copy TCP options itself
395 	if (tcplen > (int)sizeof(tcph) && off + (int)tcplen <= r) {
396 		memcpy(tcpOptions, reinterpret_cast<char *>(buf) + off + sizeof(tcph), tcplen - sizeof(tcph));
397 		off += tcplen;
398 		return r;
399 	}
400 
401 	// must be short packet
402 	if (tcplen != sizeof(tcph)) {
403 		memset(tcpOptions, 0, sizeof(tcpOptions));
404 		return 0;
405 	}
406 
407 	off += sizeof(tcph);
408        	return r;
409 }
410 
411 
412 /*! Initialize a device ("eth0" for example) for packet-
413  *  capturing. It MUST be called before sniffpack() is launched.
414  *  Set 'promisc' to 1 if you want the device running in promiscous mode.
415  *  Fetch at most 'snaplen' bytes per call.
416  */
417 template<typename T>
init_device(const string & dev,int promisc,size_t snaplen)418 int TCP<T>::init_device(const string &dev, int promisc, size_t snaplen)
419 {
420 	int r = Layer2::init_device(dev, promisc, snaplen);
421 	if (r < 0)
422 		return r;
423 	r = Layer2::setfilter("tcp");
424 	return r;
425 }
426 
427 
428 template<typename T>
get_options(string & op)429 string &TCP<T>::get_options(string &op)
430 {
431 	if (tcph.th_off<<2 <= (int)sizeof(tcph)) {
432 		op = "";
433 		return op;
434 	}
435 	op = string(tcpOptions, (tcph.th_off<<2) - sizeof(tcpOptions));
436 	return op;
437 }
438 
439 
440 template<typename T>
set_options(const string & op)441 int TCP<T>::set_options(const string &op)
442 {
443 	// too large or not aligned?
444 	if (op.length() > sizeof(tcpOptions) || op.length() % 4)
445 		return -1;
446 	memcpy(tcpOptions, op.c_str(), op.length());
447 
448 	tcph.th_off = (sizeof(tcph) + op.length())>>2;
449 	return 0;
450 }
451 
452 
453 template class TCP<IP>;
454 template class TCP<IP6>;
455 
456 } // namespace usipp
457 
458