Guy Harris wrote: > > On Jun 29, 2005, at 1:20 PM, Guy Harris wrote: > >> Use "isprint()" rather than "isascii()" in "print_payload()". > > > ...and, while you're at it, print the payload in hex, as well as > ASCII, to emphasize that there's *no* guarantee that TCP data is text. > A format such as > > 00000000 00 00 48 65 6c 6c 6f 2c 20 73 61 69 7c 6f 72 21 > ..Hello, sailor! > > with an offset (perhaps hex), a sequence of hex byte values (perhaps > with an extra space separating the first 8 from the second 8 values), > and the same byte values as text (with values that don't correspond to > printable ASCII characters shown as ".") might be reasonable.
Thanks for the help and suggestions. They are implemented in the updated attached "sniffex.c" source file, v0.1.1.
The offset counter I printed in decimal, since the total payload is printed in decimal and it just seemed more intuitive, at least for me (I have enough trouble with base10 :). I also added a "Comments" section and referenced these comments in the code with footnotes (e.g. [1]). I think this is a good compromise between readability and explanation and not having to give up one for the other.
Please let me know if you think of additional things that might help. -Nathan ----------------------------------------- This e-mail and any attachments may contain CONFIDENTIAL information, including PROTECTED HEALTH INFORMATION. If you are not the intended recipient, any use or disclosure of this information is STRICTLY PROHIBITED; you are requested to delete this e-mail and any attachments, notify the sender immediately, and notify the LabCorp Privacy Officer at [EMAIL PROTECTED] or call (877) 23-HIPAA / (877) 234-4722.
/* * sniffex.c * * Sniffer example of TCP/IP packet capture using libpcap. * * Version 0.1.1 (2005-07-05) * Copyright (c) 2005 The Tcpdump Group * * This software is intended to be used as a practical example and * demonstration of the libpcap library; available at: * http://www.tcpdump.org/ * **************************************************************************** * * This software is a modification of Tim Carstens' "sniffer.c" * demonstration source code, released as follows: * * sniffer.c * Copyright (c) 2002 Tim Carstens * 2002-01-07 * Demonstration of using libpcap * timcarst -at- yahoo -dot- com * * "sniffer.c" is distributed under these terms: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 4. The name "Tim Carstens" may not be used to endorse or promote * products derived from this software without prior written permission * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * <end of "sniffer.c" terms> * * This software, "sniffex.c", is a derivative work of "sniffer.c" and is * covered by the following terms: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Because this is a derivative work, you must comply with the "sniffer.c" * terms reproduced above. * 2. Redistributions of source code must retain the Tcpdump Group copyright * notice at the top of this source file, this list of conditions and the * following disclaimer. * 3. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 4. The names "tcpdump" or "libpcap" may not be used to endorse or promote * products derived from this software without prior written permission. * * THERE IS ABSOLUTELY NO WARRANTY FOR THIS PROGRAM. * BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY * FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN * OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES * PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED * OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS * TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE * PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, * REPAIR OR CORRECTION. * * IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING * WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR * REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, * INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING * OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED * TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY * YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER * PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE * POSSIBILITY OF SUCH DAMAGES. * <end of "sniffex.c" terms> * **************************************************************************** * * Below is an excerpt from an email from Guy Harris on the tcpdump-workers * mail list when someone asked, "How do I get the length of the TCP * payload?" Guy Harris' slightly snipped response is reproduced below: * * The Ethernet size is always 14 bytes. * * <snip>...</snip> * * In fact, you *MUST* assume the Ethernet header is 14 bytes, *and*, if * you're using structures, you must use structures where the members * always have the same size on all platforms, because the sizes of the * fields in Ethernet - and IP, and TCP, and... - headers are defined by * the protocol specification, not by the way a particular platform's C * compiler works.) * * The IP header size, in bytes, is the value of the "ip_hl" field of * "struct sniff_ip" times 4 ("times 4" because it's in units of 4-byte * words). If that value is less than 20 - i.e., if ip_hl is less than * 5 - you have a malformed IP datagram. * * The TCP header size, in bytes, is the value of the "th_off" field of * "struct sniff_tcp" times 4 (for the same reason - 4-byte words). If * that value is less than 20 - i.e., if th_off is less than 5 - you * have a malformed TCP segment. * * So, to find the IP header in an Ethernet packet, look 14 bytes after * the beginning of the packet data. To find the TCP header, look * "ip_hl*4" bytes after the beginning of the IP header. To find the * TCP payload, look "th_off*4" bytes after the beginning of the TCP * header. * * To find out how much payload there is: * * Take the IP *total* length field - "ip_len" in "struct sniff_ip" * - and, first, check whether it's less than ip_hl*4 (after you've * checked whether ip_hl is >= 5). If it is, you have a malformed IP * datagram. * * Otherwise, subtract ip_hl*4 from it; that gives you the length * of the TCP segment, including the TCP header. If that's less than * th_off*4 (after you've checked wheteher th_off is >= 5), you have a * malformed TCP segment. * * Otherwise, subtract th_off*4 from it; that gives you the length * of the TCP payload. * * Note that you also need to make sure that you don't go past the end * of the captured data in the packet - you might, for example, have a * 15-byte Ethernet packet that claims to contain an IP datagram, but if * it's 15 bytes, it has only one byte of Ethernet payload, which is too * small for an IP header. The length of the captured data is given in * the "caplen" field in the "struct pcap_pkthdr"; it might be less than * the length of the packet, if you're capturing with a snapshot length * other than a value >= the maximum packet size. * <end of response> * **************************************************************************** * * Example compiler command-line for GCC: * gcc -Wall -o sniffex sniffex.c -lpcap * **************************************************************************** * * Code Comments * * This section contains additional information and explanations regarding * comments in the source code. It serves as documentaion and rationale * for why the code is written as it is without hindering readability, as it * might if it were placed along with the actual code inline. References in * the code appear as footnote notation (e.g. [1]). * * 1. Ethernet headers are always exactly 14 bytes, so we define this * explicitly with "#define". Since some compilers might pad structures to a * multiple of 4 bytes - some versions of GCC for ARM may do this - * "sizeof (struct sniff_ethernet)" isn't used. * * 2. Check the link-layer type of the device that's being opened to make * sure it's Ethernet, since that's all we handle in this example. Other * link-layer types may have different length headers (see [1]). * * 3. This is the filter expression that tells libpcap which packets we're * interested in (i.e. which packets to capture). Since this source example * focuses on IP and TCP, we use the expression "ip", so we know we'll only * encounter IP packets. The capture filter syntax, along with some * examples, is documented in the tcpdump man page under "expression." * Below are a few simple examples: * * Expression Description * ---------- ----------- * ip Capture all IP packets. * tcp Capture only TCP packets. * tcp port 80 Capture only TCP packets with a port equal to 80. * ip host 10.1.2.3 Capture all IP packets to or from host 10.1.2.3. * **************************************************************************** * */ #define APP_NAME "sniffex" #define APP_DESC "Sniffer example using libpcap" #define APP_COPYRIGHT "Copyright (c) 2005 The Tcpdump Group" #define APP_DISCLAIMER "THERE IS ABSOLUTELY NO WARRANTY FOR THIS PROGRAM." #define _BSD_SOURCE 1 #include <pcap.h> #include <stdio.h> #include <string.h> #include <stdlib.h> #include <ctype.h> #include <errno.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <net/if.h> #include <netinet/if_ether.h> #include <netinet/tcp.h> /* default snap length (maximum bytes per packet to capture) */ #define SNAP_LEN 1518 /* ethernet headers are always exactly 14 bytes [1] */ #define SIZE_ETHERNET 14 /* Ethernet header */ struct sniff_ethernet { u_char ether_dhost[ETHER_ADDR_LEN]; /* destination host address */ u_char ether_shost[ETHER_ADDR_LEN]; /* source host address */ u_short ether_type; /* IP? ARP? RARP? etc */ }; /* IP header */ struct sniff_ip { #if BYTE_ORDER == LITTLE_ENDIAN u_int ip_hl:4, /* header length */ ip_v:4; /* version */ #endif #if BYTE_ORDER == BIG_ENDIAN u_int ip_v:4, /* version */ ip_hl:4; /* header length */ #endif u_char ip_tos; /* type of service */ u_short ip_len; /* total length */ u_short ip_id; /* identification */ u_short ip_off; /* fragment offset field */ #define IP_RF 0x8000 /* reserved fragment flag */ #define IP_DF 0x4000 /* dont fragment flag */ #define IP_MF 0x2000 /* more fragments flag */ #define IP_OFFMASK 0x1fff /* mask for fragmenting bits */ u_char ip_ttl; /* time to live */ u_char ip_p; /* protocol */ u_short ip_sum; /* checksum */ struct in_addr ip_src,ip_dst; /* source and dest address */ }; /* TCP header */ struct sniff_tcp { u_short th_sport; /* source port */ u_short th_dport; /* destination port */ tcp_seq th_seq; /* sequence number */ tcp_seq th_ack; /* acknowledgement number */ #if BYTE_ORDER == LITTLE_ENDIAN u_int th_x2:4, /* (unused) */ th_off:4; /* data offset */ #endif #if BYTE_ORDER == BIG_ENDIAN u_int th_off:4, /* data offset */ th_x2:4; /* (unused) */ #endif u_char th_flags; #define TH_FIN 0x01 #define TH_SYN 0x02 #define TH_RST 0x04 #define TH_PUSH 0x08 #define TH_ACK 0x10 #define TH_URG 0x20 #define TH_ECE 0x40 #define TH_CWR 0x80 #define TH_FLAGS (TH_FIN|TH_SYN|TH_RST|TH_ACK|TH_URG|TH_ECE|TH_CWR) u_short th_win; /* window */ u_short th_sum; /* checksum */ u_short th_urp; /* urgent pointer */ }; void got_packet(u_char *args, const struct pcap_pkthdr *header, const u_char *packet); void print_payload(const u_char *payload, int len); void print_hex_ascii_line(const u_char *payload, int len, int offset); void print_app_banner(void); void print_app_usage(void); /* * app name/banner */ void print_app_banner(void) { printf("%s - %s\n", APP_NAME, APP_DESC); printf("%s\n", APP_COPYRIGHT); printf("%s\n", APP_DISCLAIMER); printf("\n"); return; } /* * print help text */ void print_app_usage(void) { printf("Usage: %s [interface]\n", APP_NAME); printf("\n"); printf("Options:\n"); printf(" interface Listen on <interface> for packets.\n"); printf("\n"); return; } /* * print data in rows of 16 bytes: offset hex ascii * * 00000 47 45 54 20 2f 20 48 54 54 50 2f 31 2e 31 0d 0a GET / HTTP/1.1.. */ void print_hex_ascii_line(const u_char *payload, int len, int offset) { int i; int gap; const u_char *ch; /* offset */ printf("%05d ", offset); /* hex */ ch = payload; for(i = 0; i < len; i++) { printf("%02x ", *ch); ch++; /* print extra space after 8th byte for visual aid */ if (i == 7) printf(" "); } /* print space to handle line less than 8 bytes */ if (len < 8) printf(" "); /* fill hex gap with spaces if not full line */ if (len < 16) { gap = 16 - len; for (i = 0; i < gap; i++) { printf(" "); } } printf(" "); /* ascii (if printable) */ ch = payload; for(i = 0; i < len; i++) { if (isprint(*ch)) printf("%c", *ch); else printf("."); ch++; } printf("\n"); return; } /* * print packet payload data (avoid printing binary data) */ void print_payload(const u_char *payload, int len) { int len_rem = len; int line_width = 16; /* number of bytes per line */ int line_len; int offset = 0; /* zero-based offset counter */ const u_char *ch = payload; if (len <= 0) return; /* data fits on one line */ if (len <= line_width) { print_hex_ascii_line(ch, len, offset); return; } /* data spans multiple lines */ for ( ;; ) { /* compute current line length */ line_len = line_width % len_rem; /* print line */ print_hex_ascii_line(ch, line_len, offset); /* compute total remaining */ len_rem = len_rem - line_len; /* shift pointer to remaining bytes to print */ ch = ch + line_len; /* add offset */ offset = offset + line_width; /* check if we have line width chars or less */ if (len_rem <= line_width) { /* print last line and get out */ print_hex_ascii_line(ch, len_rem, offset); break; } } return; } /* * dissect/print packet */ void got_packet(u_char *args, const struct pcap_pkthdr *header, const u_char *packet) { static int count = 1; /* packet counter */ /* declare pointers to packet headers */ const struct sniff_ethernet *ethernet; /* ethernet header [1] */ const struct sniff_ip *ip; /* IP header */ const struct sniff_tcp *tcp; /* TCP header */ const char *payload; /* payload */ int size_ip; int size_tcp; int size_payload; printf("\nPacket number %d:\n", count); count++; /* define ethernet header */ ethernet = (struct sniff_ethernet*)(packet); /* define/compute ip header offset */ ip = (struct sniff_ip*)(packet + SIZE_ETHERNET); size_ip = ip->ip_hl*4; if (size_ip < 20) { printf(" * Invalid IP header length: %u bytes\n", size_ip); return; } /* print source and destination IP addresses */ printf(" From: %s\n", inet_ntoa(ip->ip_src)); printf(" To: %s\n", inet_ntoa(ip->ip_dst)); /* determine protocol */ switch(ip->ip_p) { case IPPROTO_TCP: printf(" Protocol: TCP\n"); break; case IPPROTO_UDP: printf(" Protocol: UDP\n"); return; case IPPROTO_ICMP: printf(" Protocol: ICMP\n"); return; case IPPROTO_IP: printf(" Protocol: IP\n"); return; default: printf(" Protocol: unknown\n"); return; } /* * OK, this packet is TCP. */ /* define/compute tcp header offset */ tcp = (struct sniff_tcp*)(packet + SIZE_ETHERNET + size_ip); size_tcp = tcp->th_off*4; if (size_tcp < 20) { printf(" * Invalid TCP header length: %u bytes\n", size_tcp); return; } printf(" Src port: %d\n", ntohs(tcp->th_sport)); printf(" Dst port: %d\n", ntohs(tcp->th_dport)); /* define/compute tcp payload (segment) offset */ payload = (u_char *)(packet + SIZE_ETHERNET + size_ip + size_tcp); /* compute tcp payload (segment) size */ size_payload = ntohs(ip->ip_len) - (size_ip + size_tcp); /* XXX - printf below will not handle binary data (assumes null term) */ /*printf(" Payload (%d bytes): %s\n", size_payload, payload);*/ /* deal with printing binary payload data */ printf(" Payload (%d bytes):\n", size_payload); print_payload(payload, size_payload); return; } int main(int argc, char **argv) { char *dev = NULL; /* capture device name */ char errbuf[PCAP_ERRBUF_SIZE+1]; /* error buffer */ pcap_t *descr; /* packet capture descriptor */ char filter_exp[] = "ip"; /* filter expression [3] */ struct bpf_program fp; /* compiled filter program (expression) */ bpf_u_int32 maskp; /* subnet mask */ bpf_u_int32 netp; /* ip */ int num_packets = 10; /* number of packets to capture */ print_app_banner(); /* check for capture device name on command-line */ if (argc == 2) { dev = argv[1]; } else if (argc > 2) { printf("error: unrecognized command-line options\n\n"); print_app_usage(); exit(EXIT_FAILURE); } else { /* find a capture device if not specified on command-line */ dev = pcap_lookupdev(errbuf); if (dev == NULL) { printf("pcap_lookupdev error: %s\n", errbuf); exit(EXIT_FAILURE); } } /* get network number and mask associated with capture device */ if (pcap_lookupnet(dev, &netp, &maskp, errbuf) == -1) { printf("pcap_lookupnet error: %s\n", errbuf); exit(EXIT_FAILURE); } /* print capture info */ printf("Device: %s\n", dev); printf("Number of packets: %d\n", num_packets); printf("Filter expression: %s\n", filter_exp); /* open capture device */ descr = pcap_open_live(dev, SNAP_LEN, 1, 0, errbuf); if (descr == NULL) { printf("pcap_open_live failed: %s\n", errbuf); exit(EXIT_FAILURE); } /* make sure we're capturing on an Ethernet device [2] */ if (pcap_datalink(descr) != DLT_EN10MB) { printf("error: device is not an Ethernet\n"); exit(EXIT_FAILURE); } /* compile the filter expression */ if (pcap_compile(descr, &fp, filter_exp, 0, netp) == -1) { printf("pcap_compile failed: %s\n", pcap_geterr(descr)); exit(EXIT_FAILURE); } /* apply the compiled filter */ if (pcap_setfilter(descr, &fp) == -1) { printf("pcap_setfilter failed: %s\n", pcap_geterr(descr)); exit(EXIT_FAILURE); } /* now we can set our callback function */ pcap_loop(descr, num_packets, got_packet, NULL); /* cleanup */ pcap_freecode(&fp); pcap_close(descr); printf("\nCapture complete.\n"); return 0; }
- This is the tcpdump-workers list. Visit https://lists.sandelman.ca/ to unsubscribe.