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(&timestamps[0], tcpOptions+lenparsed+2, 4);
483                 memcpy(&timestamps[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