1 /*** This Programs/Libraries are (C)opyright by Sebastian Krahmer.
2 *** You may use it under the terms of the GPL. You should have
3 *** already received the file COPYING that shows you your rights. If not,
4 *** you can get it at http://www.cs.uni-potsdam.de/homepages/students/linuxer
5 *** the logit-package. You will also find some other nice utillities there.
6 ***
7 *** THERE IS ABSOLUTELY NO WARRANTY. SO YOU USE IT AT YOUR OWN RISK.
8 *** IT WAS WRITTEN IN THE HOPE THAT IT WILL BE USEFULL. I AM NOT RESPONSIBLE
9 *** FOR ANY DAMAGE YOU MAYBE GET DUE TO USING MY PROGRAMS.
10 ***/
11
12
13 #include "usi++/usi-structs.h"
14 #include "usi++/tcp.h"
15
16 #include <string.h>
17 #include <stdlib.h>
18 #include <errno.h>
19
20 namespace usipp {
21
TCP(void)22 TCP::TCP(void): IP("0.0.0.0", IPPROTO_TCP) {
23 return;
24 }
TCP(const char * host)25 TCP::TCP(const char *host)
26 #ifndef IPPROTO_TCP
27 #define IPPROTO_TCP 6
28 #endif
29 : IP(host, IPPROTO_TCP)
30 {
31 srand(time(NULL));
32 memset(&tcph, 0, sizeof(tcph));
33 memset(&pseudo, 0, sizeof(pseudo));
34 memset(tcpOptions, 0, sizeof(tcpOptions));
35
36 tcph.th_off = 5;
37 opt_offset = 0;
38
39 tcph.th_ack = rand();
40 tcph.th_seq = rand();
41 }
42
43 /* Get the sourceport in human-readable form.
44 */
get_srcport() const45 u_int16_t TCP::get_srcport() const
46 {
47 return ntohs(tcph.th_sport);
48 }
49
~TCP()50 TCP::~TCP()
51 {
52 }
53
TCP(const TCP & rhs)54 TCP::TCP(const TCP &rhs)
55 : IP(rhs)
56 {
57 if (this == &rhs)
58 return;
59 tcph = rhs.tcph;
60 memcpy(tcpOptions, rhs.tcpOptions, sizeof(tcpOptions));
61 pseudo = rhs.pseudo;
62 }
63
operator =(const TCP & rhs)64 TCP &TCP::operator=(const TCP &rhs)
65 {
66 if (this == &rhs)
67 return *this;
68 IP::operator=(rhs);
69 tcph = rhs.tcph;
70 memcpy(tcpOptions, rhs.tcpOptions, sizeof(tcpOptions));
71 pseudo = rhs.pseudo;
72 return *this;
73 }
74
75 /* Get the destinationport in human-readable form.
76 */
get_dstport() const77 u_int16_t TCP::get_dstport() const
78 {
79 return ntohs(tcph.th_dport);
80 }
81
82 /* Get TCP-sequencenumber
83 */
get_seq() const84 u_int32_t TCP::get_seq() const
85 {
86 return ntohl(tcph.th_seq);
87 }
88
89 /* Get the actual achnkowledge-number from the TCP-header.
90 */
get_ack() const91 u_int32_t TCP::get_ack() const
92 {
93 return ntohl(tcph.th_ack);
94 }
95
96 /* Get TCP data offset.
97 */
get_off() const98 u_int8_t TCP::get_off() const
99 {
100 return tcph.th_off;
101 }
102
103 /* Set TCP-flags
104 */
get_flags() const105 u_int8_t TCP::get_flags() const
106 {
107 return tcph.th_flags;
108 }
109
get_win() const110 u_int16_t TCP::get_win() const
111 {
112 return ntohs(tcph.th_win);
113 }
114
115 /* Get TCP-header checksum
116 */
get_tcpsum() const117 u_int16_t TCP::get_tcpsum() const
118 {
119 return tcph.th_sum;
120 }
121
get_urg() const122 u_int16_t TCP::get_urg() const
123 {
124 return ntohs(tcph.th_urp);
125 }
126
get_wscale() const127 u_int32_t TCP::get_wscale() const
128 {
129 return wscale;
130 }
131
132 /* Set TCP sourceport
133 */
set_srcport(u_int16_t sp)134 int TCP::set_srcport(u_int16_t sp)
135 {
136 tcph.th_sport = htons(sp);
137 return 0;
138 }
139
140 /* Set TCP destination port.
141 */
set_dstport(u_int16_t dp)142 int TCP::set_dstport(u_int16_t dp)
143 {
144 tcph.th_dport = htons(dp);
145 return 0;
146 }
147
148 /* Set the sequencenumber-filed in the TCP-header.
149 */
set_seq(u_int32_t s)150 int TCP::set_seq(u_int32_t s)
151 {
152 tcph.th_seq = htonl(s);
153 return 0;
154 }
155
156 /* Set the acknowledgenumber-filed in the TCP-header.
157 * This is only monitored by the target-kernel, if TH_ACK
158 * is set in the TCP-flags.
159 */
set_ack(u_int32_t a)160 int TCP::set_ack(u_int32_t a)
161 {
162 tcph.th_ack = htonl(a);
163 return 0;
164 }
165
166 /* Set TCP data offset.
167 */
set_off(u_int8_t o)168 int TCP::set_off(u_int8_t o)
169 {
170 tcph.th_off = o;
171 return 0;
172 }
173
174 /* Set TCP-flags
175 */
set_flags(u_int8_t f)176 int TCP::set_flags(u_int8_t f)
177 {
178 tcph.th_flags = f;
179 return 0;
180 }
181
set_win(u_int16_t w)182 int TCP::set_win(u_int16_t w)
183 {
184 tcph.th_win = htons(w);
185 return 0;
186 }
187
188 /* Set TCP-checksum. Calling this function with s != 0
189 * will prevent sendpack from calculating the checksum!!!
190 */
set_tcpsum(u_int16_t s)191 int TCP::set_tcpsum(u_int16_t s)
192 {
193 tcph.th_sum = s;
194 return 0;
195 }
196
set_urg(u_int16_t u)197 int TCP::set_urg(u_int16_t u)
198 {
199 tcph.th_urp = htons(u);
200 return 0;
201 }
202
203 /* experimental */
get_tcphdr() const204 tcphdr TCP::get_tcphdr() const
205 {
206 return tcph;
207 }
208
set_tcphdr(struct tcphdr _tcph)209 int TCP::set_tcphdr(struct tcphdr _tcph) {
210 tcph = _tcph;
211 return 0;
212 }
213 /* Send a TCP-packet
214 */
sendpack(void * buf,size_t paylen)215 int TCP::sendpack(void *buf, size_t paylen)
216 {
217 /* XXX: move to here from set_tcpopts()
218 */
219 while (opt_offset % 4 && opt_offset < sizeof(tcpOptions))
220 tcpOptions[opt_offset++] = TCPOPT_NOP;
221
222 tcph.th_off = ((opt_offset+sizeof(tcph))>>2);
223
224 unsigned int len = paylen + (tcph.th_off<<2) + sizeof(pseudo);
225 char *tmp = new char[len+1+20]; // +1 for padding if necessary
226 memset(tmp, 0, len+1);
227
228 // build a pseudoheader for IP-checksum, as
229 // required per RFC 793
230 pseudo.saddr = get_src(); // sourceaddress
231 pseudo.daddr = get_dst(); // destinationaddress
232 pseudo.zero = 0;
233 pseudo.proto = IPPROTO_TCP;
234 pseudo.len = htons((tcph.th_off<<2) + paylen);
235
236 // copy pseudohdr+header+data to buffer
237 memcpy(tmp, &pseudo, sizeof(pseudo));
238 memcpy(tmp + sizeof(pseudo), &tcph, sizeof(tcph));
239
240 // options, might be 0-length
241 if ((tcph.th_off<<2) > (int)sizeof(tcph))
242 memcpy(tmp + sizeof(pseudo) + sizeof(tcph), tcpOptions, (tcph.th_off<<2)-sizeof(tcph));
243
244 // data
245 memcpy(tmp + sizeof(pseudo) + (tcph.th_off<<2), buf, paylen);
246
247 // calc checksum over it
248 struct tcphdr *t = (struct tcphdr*)(tmp + sizeof(pseudo));
249
250 if (tcph.th_sum == 0) {
251 t->th_sum = in_cksum((unsigned short*)tmp, len, 1);
252 tcph.th_sum = t->th_sum;
253 }
254
255 IP::sendpack(tmp + sizeof(pseudo), len - sizeof(pseudo));
256
257 delete [] tmp;
258 return 0;
259 }
260
261
sendpack(char * s)262 int TCP::sendpack(char *s)
263 {
264 return sendpack(s, strlen(s));
265 }
266
267
268 /* Sniff a TCP-packet.
269 */
sniffpack(void * buf,size_t len)270 int TCP::sniffpack(void *buf, size_t len)
271 {
272 size_t xlen = len + sizeof(tcph) + sizeof(tcpOptions);
273
274 char *tmp = new char[xlen];
275 int r = 0;
276
277 memset(tmp, 0, xlen);
278 memset(buf, 0, len);
279 memset(&tcph, 0, sizeof(tcph));
280
281 r = IP::sniffpack(tmp, xlen);
282
283 if (r == 0 && Layer2::timeout()) { // timeout
284 delete[] tmp;
285 return 0;
286 }
287
288 // Copy TCP-header without options
289 memcpy(&tcph, tmp, sizeof(tcph));
290
291 unsigned int tcplen = tcph.th_off<<2;
292
293 if (tcplen > sizeof(tcph)) {
294 opt_offset = tcplen - sizeof(tcph);
295 if (opt_offset < sizeof(tcpOptions)) {
296 memcpy(tcpOptions, tmp+sizeof(tcph), opt_offset);
297 } else {
298 opt_offset = 0;
299 }
300
301 }
302
303 if (buf)
304 memcpy(buf, tmp + tcplen, len);
305
306 delete [] tmp;
307 return r - tcplen;
308 }
309
310
311 /* Initialize a device ("eth0" for example) for packet-
312 * capturing. It MUST be called before sniffpack() is launched.
313 * Set 'promisc' to 1 if you want the device running in promiscous mode.
314 * Fetch at most 'snaplen' bytes per call.
315 */
init_device(char * dev,int promisc,size_t snaplen)316 int TCP::init_device(char *dev, int promisc, size_t snaplen)
317 {
318 int r = Layer2::init_device(dev, promisc, snaplen);
319 if (r < 0)
320 die("TCP::init_device", STDERR, 1);
321 r = Layer2::setfilter("tcp");
322 if (r < 0)
323 die("TCP::init_device::setfilter", STDERR, 1);
324 return r;
325 }
326
327
328 /* Implementation of TCP-options
329 */
set_tcpopt(char kind,unsigned char len,union tcp_options to)330 int TCP::set_tcpopt(char kind, unsigned char len, union tcp_options to)
331 {
332 //int mss;
333 // calculate end of option-list
334 /* XXX meder: we move padding and th_off calculation to
335 * sendpack() since we want to be able to construct
336 * any options, in any order.
337 * also added TCPOPT_SACK_PERMITTED
338 */
339 //int opt_offset = (tcph.th_off<<2) - sizeof(tcph);
340
341 if (opt_offset < 0 || opt_offset >= (int)sizeof(tcpOptions))
342 return -1;
343
344 tcpOptions[opt_offset++] = kind;
345 if (kind > 1)
346 tcpOptions[opt_offset++] = len;
347 switch (kind) {
348 case TCPOPT_SACK_PERMITTED:
349 case TCPOPT_EOL:
350 case TCPOPT_NOP:
351 break;
352 case TCPOPT_MAXSEG:
353 *((short*)&tcpOptions[opt_offset]) = htons(to.one_word);
354 opt_offset += sizeof(short);
355 break;
356 case TCPOPT_WINDOW:
357 tcpOptions[opt_offset++] = to.one_byte;
358 break;
359 case TCPOPT_TIMESTAMP:
360
361 // XXX: htonl() ?
362 *((int*)&tcpOptions[opt_offset]) = htonl(to.two_dwords[0]);
363 opt_offset += sizeof(int);
364 *((int*)&tcpOptions[opt_offset]) = htonl(to.two_dwords[1]);
365 opt_offset += sizeof(int);
366 break;
367 // if unknown, just copy len bytes to optionbuffer
368 // this could be used for generic usage
369 default:
370 int xl = len < sizeof(tcpOptions)-opt_offset?len:sizeof(tcpOptions)-opt_offset;
371 memcpy(&tcpOptions[opt_offset], to.unknown, xl);
372 opt_offset += xl;
373 break;
374 } // switch
375 //opt_offset--;
376
377 // padding for align of 4
378 /*
379 * XXX: moved to sendpack()
380 *
381 while (opt_offset % 4)
382 tcpOptions[opt_offset++] = TCPOPT_NOP;
383
384 opt_offset += sizeof(tcph); tcph.th_off = (opt_offset>>2);
385 */
386 return 0;
387 }
388
389 // we assume a buffer of at least 40 bytes
get_tcpopt(char * buf)390 int TCP::get_tcpopt(char *buf)
391 {
392 memcpy(buf, tcpOptions, 40);
393 return tcph.th_off<<2;
394 }
395
set_tcpopt(char * buf,unsigned int len)396 int TCP::set_tcpopt(char *buf, unsigned int len) {
397 opt_offset = sizeof(tcpOptions) < len ? sizeof(tcpOptions) : len;
398 memset(tcpOptions, 0, sizeof(tcpOptions));
399 memcpy(tcpOptions, buf, opt_offset);
400 return 0;
401 }
402
reset_tcpopt()403 int TCP::reset_tcpopt()
404 {
405 /* XXX: changed here also
406 */
407 tcph.th_off = 5;
408 opt_offset = 0;
409 memset(tcpOptions, 0, sizeof(tcpOptions));
410 return 0;
411 }
412
413 //bool TCP::operator==(const TCP &left, const TCP &right) {
414 // return equals_operator(left, right);
415 //}
416
417 /*
418 bool TCP::equals_operator(const TCP &left, const TCP &right);
419 return (left.get_flags() == right.get_flags() &&
420 left.get_win() == right.get_win() &&
421 left.get_off() == right.get_off() &&
422 left.get_urg() == right.get_urg());
423 }
424 */
425
get_parsed_tcpopt(char * opt_order,unsigned int buflen) const426 int TCP::get_parsed_tcpopt(char *opt_order, unsigned int buflen) const {
427 unsigned int lenparsed, optlen= 0, k=0;
428
429 // Parse TCP options, like OpenBSD does in /sys/netinet/tcp_input.c
430 memset(opt_order, 0, buflen);
431 for (lenparsed = 0; lenparsed < opt_offset; lenparsed += optlen) {
432 if (tcpOptions[lenparsed] == TCPOPT_NOP) {
433 optlen=1;
434 if (k < buflen)
435 opt_order[k++]='N';
436 continue;
437 } else if (tcpOptions[lenparsed] == TCPOPT_EOL) {
438 if (opt_offset - lenparsed > 1)
439 // something fucked up, we have end of list
440 // but we are not done yet
441 return 0;
442 } else {
443 // avoid evil packets that only have
444 // option w/o lenght
445 if (lenparsed + 1 < opt_offset)
446 optlen = tcpOptions[lenparsed+1];
447 else
448 // something is really fucked
449 // we have option but do not have
450 // its length
451 return 0;
452 }
453 // alrighty, check for a fucked up packs
454 // make sure that len reported in the pack
455 // fits into our buffer
456 if (optlen > opt_offset - lenparsed) {
457 return 0;
458 }
459
460 // at this point have optlen bytes in tcp_options;
461 // if optlen for some particular option is fucked up
462 // we assign it correct value and try to parse further,
463 // however neither data is parsed, nor we add option to
464 // opt_order
465 switch(tcpOptions[lenparsed]) {
466 case TCPOPT_WINDOW:
467 if (optlen != TCPOLEN_WINDOW) {
468 optlen = TCPOLEN_WINDOW;
469 continue;
470 } else {
471 wscale = tcpOptions[lenparsed+2];
472 if (k < buflen)
473 opt_order[k++]='W';
474 }
475 break;
476 case TCPOPT_TIMESTAMP:
477 if (optlen != TCPOLEN_TIMESTAMP) {
478 optlen = TCPOLEN_TIMESTAMP;
479 continue;
480 }
481 // we are guaranteed to have 8 bytes of option data at tcp_options+lenparsed
482 memcpy(×tamps[0], tcpOptions+lenparsed+2, 4);
483 memcpy(×tamps[1], tcpOptions+lenparsed+6, 4);
484 timestamps[0] = ntohl(timestamps[0]);
485 timestamps[1] = ntohl(timestamps[1]);
486
487 if (k < buflen)
488 opt_order[k++]='T';
489 break;
490 case TCPOPT_MAXSEG:
491 if (optlen != TCPOLEN_MAXSEG) {
492 optlen = TCPOLEN_MAXSEG;
493 continue;
494 }
495 if (k < buflen)
496 opt_order[k++] = 'M';
497 break;
498 case TCPOPT_SACK_PERMITTED:
499 if (optlen != TCPOLEN_SACK_PERMITTED) {
500 optlen = TCPOLEN_SACK_PERMITTED;
501 continue;
502 }
503 if (k < buflen)
504 opt_order[k++] = 'S';
505 break;
506 }
507 }
508 return k;
509 }
510
to_string(void)511 std::string TCP::to_string(void) {
512 char buf[4096], tcp_opt[40];
513 string retval = IP::to_string();
514
515 memset(buf, 0, sizeof(buf));
516 memset(tcp_opt, 0, sizeof(tcp_opt));
517 get_parsed_tcpopt(tcp_opt, sizeof(tcp_opt)-1);
518 snprintf(buf, sizeof(buf), "+--------------------------------[ TCP ]\n| sport=%d dport=%d seq=0x%x ack=0x%x win=0x%x off=%d urg=%d flags=0x%x options=%s\n+--------------------------------\n", get_srcport(), get_dstport(), get_seq(), get_ack(), get_win(), get_off(), get_urg(), get_flags(), tcp_opt);
519 retval.append(buf);
520 return retval;
521 }
522
523 } // namespace usipp
524
525