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