fluffy

Network Archaeology tools for Unix
git clone https://git.woozle.org/neale/fluffy.git

Neale Pickett  ·  2020-09-21

pcat.c

  1/*
  2 * pcat.c -- pcap to text
  3 *
  4 * Reads pcap on stdin, dumps text representation to stdout.
  5 * This discards or ignores some parts of packets. If fidelity is a concern,
  6 * deal directly with the original pcap.
  7 *
  8 * Output lines are of the form:
  9 *
 10 * 	PROTO SRC DST OPTS PAYLOAD
 11 *
 12 
 13 */
 14
 15#include <stdio.h>
 16#include <stdint.h>
 17#include "pcap.h"
 18#include "stream.h"
 19
 20#define IPPROTO_TCP 6
 21#define IPPROTO_UDP 17
 22#define IPPROTO_ICMP 1
 23
 24#define TH_FIN 0x01
 25#define TH_SYN 0x02
 26#define TH_RST 0x04
 27#define TH_PSH 0x08
 28#define TH_ACK 0x10
 29#define TH_URG 0x20
 30
 31void
 32ip4_addr(char *addr_s, uint32_t addr)
 33{
 34	snprintf(addr_s, 16, "%u.%u.%u.%u", (addr >> 24) & 0xff, (addr >> 16) & 0xff, (addr >> 8) & 0xff, (addr >> 0) & 0xff);
 35}
 36
 37void
 38print_nybble(uint8_t nybble)
 39{
 40	if (nybble < 10) {
 41		putchar(nybble + '0');
 42	} else {
 43		putchar(nybble - 10 + 'a');
 44	}
 45}
 46
 47/* About 3x faster than printf("%02x", octet); */
 48void
 49printx(uint8_t octet)
 50{
 51	print_nybble(octet >> 4);
 52	print_nybble(octet & 0xf);
 53}
 54
 55void
 56print_payload(struct stream *s)
 57{
 58	while (s->len) {
 59		printx(read_uint8(s));
 60	}
 61}
 62
 63void
 64process_tcp(struct stream *s, char *saddr_s, char *daddr_s)
 65{
 66	uint16_t sport = read_uint16(s);
 67	uint16_t dport = read_uint16(s);
 68	uint32_t seq = read_uint32(s);
 69	uint32_t ack = read_uint32(s);
 70	uint8_t off = read_uint8(s);
 71	uint8_t hlen = (off >> 4) * 4;
 72	uint8_t flags = read_uint8(s);
 73	uint16_t window = read_uint16(s);
 74	uint16_t chksum = read_uint16(s);
 75	uint16_t urgent = read_uint16(s);
 76
 77	if (hlen < 20) {
 78		printf("!");
 79	}
 80
 81	printf("TCP\t%s,%u\t%s,%u\t%u,%u,%d\t", saddr_s, sport, daddr_s, dport, seq, ack, flags);
 82}
 83
 84void
 85process_udp(struct stream *s, char *saddr_s, char *daddr_s)
 86{
 87	uint16_t sport = read_uint16(s);
 88	uint16_t dport = read_uint16(s);
 89	uint16_t len = read_uint16(s);
 90	uint16_t chksum = read_uint16(s);
 91
 92	printf("UDP\t%s,%u\t%s,%u\t0\t", saddr_s, sport, daddr_s, dport);
 93}
 94
 95void
 96process_icmp(struct stream *s, char *saddr_s, char *daddr_s)
 97{
 98	uint8_t type = read_uint8(s);
 99	uint8_t code = read_uint8(s);
100	uint16_t checksum = read_uint16(s);
101	
102	printf("ICMP\t%s\t%s\t%d,%d\t", saddr_s, daddr_s, type, code);
103}
104
105void
106process_ip4(struct stream *s)
107{
108	uint8_t vhl = read_uint8(s);
109	uint8_t ihl = (vhl & 0x0f) * 4;
110	uint8_t tos = read_uint8(s);
111	uint16_t length = read_uint16(s);
112	uint16_t id = read_uint16(s);
113	uint16_t off = read_uint16(s);
114	uint8_t ttl = read_uint8(s);
115	uint8_t proto = read_uint8(s);
116	uint16_t chksum = read_uint16(s);
117	uint32_t saddr = read_uint32(s);
118	uint32_t daddr = read_uint32(s);
119
120	char saddr_s[20];
121	char daddr_s[20];
122
123	ip4_addr(saddr_s, saddr);
124	ip4_addr(daddr_s, daddr);
125
126	// Ignore options
127	sskip(s, 20 - ihl);
128
129	// Force stream length to IP payload length
130	s->len = length - ihl;
131
132	switch (proto) {
133		case IPPROTO_TCP:
134			process_tcp(s, saddr_s, daddr_s);
135			break;
136		case IPPROTO_UDP:
137			process_udp(s, saddr_s, daddr_s);
138			break;
139		case IPPROTO_ICMP:
140			process_icmp(s, saddr_s, daddr_s);
141			break;
142		default:
143			printf("P%d\t%s\t%s\t", proto, saddr_s, daddr_s);
144			break;
145	}
146
147	print_payload(s);
148}
149
150
151void
152print_ethernet(struct stream *s)
153{
154	uint8_t saddr[6];
155	uint8_t daddr[6];
156	uint16_t ethertype;
157
158	sread(s, &saddr, sizeof(saddr));
159	sread(s, &daddr, sizeof(daddr));
160	ethertype = read_uint16(s);
161
162	if (ethertype == 0x8100) {
163		// VLAN 
164		read_uint16(s);
165		ethertype = read_uint16(s);
166	}
167
168	switch (ethertype) {
169		case 0x0800:	// IPv4
170			process_ip4(s);
171			break;
172	}
173}
174
175void
176print_frame(struct pcap_file *p, struct pcap_pkthdr *hdr, char const *frame)
177{
178	struct stream streambuf;
179	struct stream *s = &streambuf;
180
181	sinit(s, frame, hdr->caplen, ENDIAN_NETWORK); // pcap.c always outputs network byte order
182	printf("%u.%u\t", hdr->ts.tv_sec, hdr->ts.tv_usec);
183	switch (p->linktype) {
184		case LINKTYPE_ETHERNET:
185			print_ethernet(s);
186			break;
187		case LINKTYPE_RAW:
188			process_ip4(s);
189			break;
190	}
191	printf("\n");
192}
193
194void
195pcat(FILE * f)
196{
197	struct pcap_file p;
198	struct pcap_pkthdr hdr;
199	char frame[MAXFRAME];
200
201	if (-1 == pcap_open_in(&p, f))
202		return;
203
204	for (;;) {
205
206		if (-1 == pcap_read_pkthdr(&p, &hdr)) {
207			break;
208		}
209
210		if (1 != fread(frame, hdr.caplen, 1, f)) {
211			break;
212		}
213
214		print_frame(&p, &hdr, frame);
215	}
216}
217
218
219int
220main(int argc, char *argv[])
221{
222	pcat(stdin);
223
224	return 0;
225}