| 1 |
/*
|
| 2 |
* Copyright (C) 2001 Marco Ziech (mmz@gmx.net)
|
| 3 |
*
|
| 4 |
* This program is free software; you can redistribute it and/or
|
| 5 |
* modify it under the terms of the GNU General Public License
|
| 6 |
* as published by the Free Software Foundation; either version 2
|
| 7 |
* of the License, or (at your option) any later version.
|
| 8 |
*
|
| 9 |
* This program is distributed in the hope that it will be useful,
|
| 10 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
| 11 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
| 12 |
* GNU General Public License for more details.
|
| 13 |
*
|
| 14 |
* You should have received a copy of the GNU General Public License
|
| 15 |
* along with this program; if not, write to the Free Software
|
| 16 |
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
| 17 |
*/
|
| 18 |
#include "common/setup_before.h"
|
| 19 |
#include <stdio.h>
|
| 20 |
#include <pcap.h>
|
| 21 |
#include <errno.h>
|
| 22 |
#include <netinet/in.h>
|
| 23 |
#include <string.h>
|
| 24 |
#include "compat/strerror.h"
|
| 25 |
#include "compat/getopt.h"
|
| 26 |
#include "common/init_protocol.h"
|
| 27 |
#include "common/bnet_protocol.h"
|
| 28 |
#include "common/udp_protocol.h"
|
| 29 |
#include "common/packet.h"
|
| 30 |
#include "common/eventlog.h"
|
| 31 |
#include "common/hexdump.h"
|
| 32 |
#include "common/list.h"
|
| 33 |
#include "common/version.h"
|
| 34 |
#include "common/util.h"
|
| 35 |
#include "common/setup_after.h"
|
| 36 |
|
| 37 |
/* FIXME: everywhere: add checks for NULL pointers */
|
| 38 |
|
| 39 |
char *filename = NULL;
|
| 40 |
pcap_t *pc;
|
| 41 |
char ebuf[PCAP_ERRBUF_SIZE];
|
| 42 |
|
| 43 |
|
| 44 |
int bnpcap_dodebug = 0;
|
| 45 |
int bnpcap_beverbose = 0;
|
| 46 |
|
| 47 |
unsigned int listen_port = 6112;
|
| 48 |
|
| 49 |
/********************* CONNECTIONS ********************/
|
| 50 |
|
| 51 |
typedef enum {
|
| 52 |
tcp_state_none,
|
| 53 |
tcp_state_syn,
|
| 54 |
tcp_state_ack,
|
| 55 |
tcp_state_ok
|
| 56 |
} t_tcp_state;
|
| 57 |
|
| 58 |
typedef struct {
|
| 59 |
/* It's IPV4 */
|
| 60 |
unsigned int ip;
|
| 61 |
unsigned short port;
|
| 62 |
} t_bnpcap_addr;
|
| 63 |
|
| 64 |
/* To track connections ... */
|
| 65 |
typedef struct {
|
| 66 |
t_bnpcap_addr client;
|
| 67 |
t_bnpcap_addr server;
|
| 68 |
t_packet_class class;
|
| 69 |
t_tcp_state tcpstate;
|
| 70 |
int incomplete;
|
| 71 |
int clientoff;
|
| 72 |
t_packet *clientpkt;
|
| 73 |
int serveroff;
|
| 74 |
t_packet *serverpkt;
|
| 75 |
t_list * packets;
|
| 76 |
} t_bnpcap_conn;
|
| 77 |
|
| 78 |
typedef struct {
|
| 79 |
t_packet_dir dir;
|
| 80 |
struct timeval tv;
|
| 81 |
unsigned int id;
|
| 82 |
t_packet *p;
|
| 83 |
} t_bnpcap_packet;
|
| 84 |
|
| 85 |
t_list * conns;
|
| 86 |
t_list * udppackets;
|
| 87 |
|
| 88 |
struct timeval packettime;
|
| 89 |
|
| 90 |
static unsigned int current_packet_id = 1;
|
| 91 |
|
| 92 |
/*********************** HEADERS **********************/
|
| 93 |
|
| 94 |
/* FIXME: don't assume that's always true */
|
| 95 |
typedef unsigned char u8;
|
| 96 |
typedef unsigned short u16;
|
| 97 |
typedef unsigned int u32;
|
| 98 |
|
| 99 |
/************************** TCP ***********************/
|
| 100 |
|
| 101 |
typedef struct {
|
| 102 |
u16 sport;
|
| 103 |
u16 dport;
|
| 104 |
u32 seqno;
|
| 105 |
u32 ackno;
|
| 106 |
u16 stuff; /* Data offset, various flags */
|
| 107 |
u16 window;
|
| 108 |
u16 checksum;
|
| 109 |
u16 urgp; /* Urgent Pointer (if URG flag set) */
|
| 110 |
/* options */
|
| 111 |
} t_tcp_header_raw;
|
| 112 |
|
| 113 |
typedef struct {
|
| 114 |
unsigned short sport;
|
| 115 |
unsigned short dport;
|
| 116 |
unsigned int seqno;
|
| 117 |
unsigned int ackno;
|
| 118 |
unsigned char doffset;
|
| 119 |
unsigned short flags;
|
| 120 |
#define TCP_URG 0x20 /* Urgent pointer field significant */
|
| 121 |
#define TCP_ACK 0x10 /* Acknowlegdement field significant */
|
| 122 |
#define TCP_PSH 0x08 /* Push function */
|
| 123 |
#define TCP_RST 0x04 /* Reset connection */
|
| 124 |
#define TCP_SYN 0x02 /* Synchronize sequence numbers */
|
| 125 |
#define TCP_FIN 0x01 /* No more data from sender (finish) */
|
| 126 |
unsigned short window;
|
| 127 |
unsigned short checksum;
|
| 128 |
unsigned short urgp;
|
| 129 |
/* options */
|
| 130 |
} t_tcp_header;
|
| 131 |
|
| 132 |
/******************************** UDP ************************/
|
| 133 |
|
| 134 |
typedef struct {
|
| 135 |
u16 sport;
|
| 136 |
u16 dport;
|
| 137 |
u16 len;
|
| 138 |
u16 checksum;
|
| 139 |
} t_ip_udp_header_raw;
|
| 140 |
|
| 141 |
typedef struct {
|
| 142 |
unsigned short sport;
|
| 143 |
unsigned short dport;
|
| 144 |
unsigned short len;
|
| 145 |
unsigned short checksum;
|
| 146 |
} t_ip_udp_header;
|
| 147 |
|
| 148 |
/******************************** IP *************************/
|
| 149 |
|
| 150 |
typedef struct {
|
| 151 |
u8 versionihl;
|
| 152 |
u8 tos;
|
| 153 |
u16 len;
|
| 154 |
u16 id;
|
| 155 |
u16 flagsoffset;
|
| 156 |
u8 ttl;
|
| 157 |
u8 protocol;
|
| 158 |
u16 checksum;
|
| 159 |
u32 src;
|
| 160 |
u32 dst;
|
| 161 |
/* options */
|
| 162 |
} t_ip_header_raw;
|
| 163 |
|
| 164 |
typedef struct {
|
| 165 |
unsigned char version;
|
| 166 |
unsigned char ihl;
|
| 167 |
unsigned char tos;
|
| 168 |
unsigned short len;
|
| 169 |
unsigned short id;
|
| 170 |
unsigned char flags;
|
| 171 |
#define IP_DF 0x02 /* 1 == Don't fragment */
|
| 172 |
#define IP_MF 0x01 /* 1 == More fragments */
|
| 173 |
unsigned short offset;
|
| 174 |
unsigned char ttl;
|
| 175 |
unsigned char protocol;
|
| 176 |
unsigned short checksum;
|
| 177 |
unsigned int src;
|
| 178 |
unsigned int dst;
|
| 179 |
/* options */
|
| 180 |
} t_ip_header;
|
| 181 |
|
| 182 |
/******************************* ETHERNET *****************************/
|
| 183 |
|
| 184 |
typedef struct {
|
| 185 |
u8 dst[6]; /* Ethernet hardware address */
|
| 186 |
u8 src[6]; /* Ethernet hardware address */
|
| 187 |
u16 type; /* Ethernet_II: protocol type */
|
| 188 |
/* FIXME: Ethernet [802.2|802.3|SNAP]: maybe something else (eg. length) */
|
| 189 |
} t_ether_raw;
|
| 190 |
|
| 191 |
/************************************************************************/
|
| 192 |
/************************* CONNECTION FUNCTIONS *************************/
|
| 193 |
|
| 194 |
static t_bnpcap_conn * bnpcap_conn_new(t_bnpcap_addr const *s, t_bnpcap_addr const *d)
|
| 195 |
{
|
| 196 |
t_bnpcap_conn * c;
|
| 197 |
|
| 198 |
c = (t_bnpcap_conn *) malloc(sizeof(t_bnpcap_conn)); /* avoid warning */
|
| 199 |
if (!c) {
|
| 200 |
eventlog(eventlog_level_error,__FUNCTION__,"malloc failed: %s",pstrerror(errno));
|
| 201 |
return NULL;
|
| 202 |
}
|
| 203 |
if (d->port==listen_port || d->port==6200) { /* FIXME: That's dirty: We assume the server is on port 6112 */
|
| 204 |
memcpy(&c->client,s,sizeof(t_bnpcap_addr));
|
| 205 |
memcpy(&c->server,d,sizeof(t_bnpcap_addr));
|
| 206 |
} else {
|
| 207 |
memcpy(&c->client,d,sizeof(t_bnpcap_addr));
|
| 208 |
memcpy(&c->server,s,sizeof(t_bnpcap_addr));
|
| 209 |
}
|
| 210 |
c->class = packet_class_init;
|
| 211 |
c->packets = list_create();
|
| 212 |
c->incomplete = 0;
|
| 213 |
c->tcpstate = tcp_state_none;
|
| 214 |
c->clientoff = 0;
|
| 215 |
c->clientpkt = NULL;
|
| 216 |
c->serveroff = 0;
|
| 217 |
c->serverpkt = NULL;
|
| 218 |
return c;
|
| 219 |
}
|
| 220 |
|
| 221 |
static void bnpcap_conn_set_class(t_bnpcap_conn *c, t_packet_class class)
|
| 222 |
{
|
| 223 |
c->class = class;
|
| 224 |
}
|
| 225 |
|
| 226 |
static t_packet_class bnpcap_conn_get_class(t_bnpcap_conn *c)
|
| 227 |
{
|
| 228 |
return c->class;
|
| 229 |
}
|
| 230 |
|
| 231 |
static t_bnpcap_conn * bnpcap_conn_find(t_bnpcap_addr const *s, t_bnpcap_addr const *d)
|
| 232 |
{
|
| 233 |
t_elem * curr;
|
| 234 |
|
| 235 |
LIST_TRAVERSE(conns,curr) {
|
| 236 |
t_bnpcap_conn *c;
|
| 237 |
|
| 238 |
c = elem_get_data(curr);
|
| 239 |
if (((c->client.ip==s->ip)&&(c->client.port==s->port))&&
|
| 240 |
((c->server.ip==d->ip)&&(c->server.port==d->port))) {
|
| 241 |
return c;
|
| 242 |
} else if (((c->client.ip==d->ip)&&(c->client.port==d->port))&&
|
| 243 |
((c->server.ip==s->ip)&&(c->server.port==s->port))) {
|
| 244 |
return c;
|
| 245 |
}
|
| 246 |
}
|
| 247 |
return NULL;
|
| 248 |
}
|
| 249 |
|
| 250 |
static t_packet_dir bnpcap_conn_get_dir(t_bnpcap_conn const * c, t_bnpcap_addr const *s, t_bnpcap_addr const *d)
|
| 251 |
{
|
| 252 |
if (((c->client.ip==s->ip)&&(c->client.port==s->port))&&
|
| 253 |
((c->server.ip==d->ip)&&(c->server.port==d->port)))
|
| 254 |
return packet_dir_from_client;
|
| 255 |
else
|
| 256 |
return packet_dir_from_server;
|
| 257 |
}
|
| 258 |
|
| 259 |
static int bnpcap_conn_add_packet(t_bnpcap_conn *c, t_bnpcap_packet *bp) {
|
| 260 |
eventlog(eventlog_level_debug,__FUNCTION__,"id=%u ",bp->id);
|
| 261 |
list_append_data(c->packets,bp);
|
| 262 |
packet_add_ref(bp->p);
|
| 263 |
return 0;
|
| 264 |
}
|
| 265 |
|
| 266 |
static int bnpcap_conn_packet(unsigned int sip, unsigned short sport, unsigned int dip, unsigned short dport, unsigned char const * data, unsigned int len)
|
| 267 |
{
|
| 268 |
t_bnpcap_addr s;
|
| 269 |
t_bnpcap_addr d;
|
| 270 |
t_bnpcap_conn *c;
|
| 271 |
t_bnpcap_packet *bp;
|
| 272 |
|
| 273 |
s.ip = sip;
|
| 274 |
s.port = sport;
|
| 275 |
d.ip = dip;
|
| 276 |
d.port = dport;
|
| 277 |
|
| 278 |
if ((c = bnpcap_conn_find(&s,&d))) {
|
| 279 |
eventlog(eventlog_level_debug,__FUNCTION__,"adding packet to existing connection");
|
| 280 |
if (c->tcpstate==tcp_state_ack) {
|
| 281 |
c->tcpstate = tcp_state_ok;
|
| 282 |
} else if (c->tcpstate==tcp_state_syn) {
|
| 283 |
c->incomplete = 1; /* ACK missing */
|
| 284 |
c->tcpstate = tcp_state_ok;
|
| 285 |
}
|
| 286 |
} else {
|
| 287 |
eventlog(eventlog_level_debug,__FUNCTION__,"adding packet to incomplete connection");
|
| 288 |
c = bnpcap_conn_new(&s,&d);
|
| 289 |
bnpcap_conn_set_class(c,packet_class_raw); /* we don't know the init sequence */
|
| 290 |
c->incomplete = 1;
|
| 291 |
c->tcpstate = tcp_state_ok;
|
| 292 |
list_append_data(conns,c);
|
| 293 |
}
|
| 294 |
if (c->tcpstate!=tcp_state_ok) {
|
| 295 |
eventlog(eventlog_level_warn,__FUNCTION__,"connection got packet in wrong state!");
|
| 296 |
}
|
| 297 |
if (bnpcap_conn_get_class(c) == packet_class_init) {
|
| 298 |
if (len>1) {
|
| 299 |
eventlog(eventlog_level_warn,__FUNCTION__,"init packet larger than 1 byte");
|
| 300 |
}
|
| 301 |
switch (data[0]) {
|
| 302 |
case CLIENT_INITCONN_CLASS_BNET:
|
| 303 |
bnpcap_conn_set_class(c,packet_class_bnet);
|
| 304 |
break;
|
| 305 |
case CLIENT_INITCONN_CLASS_FILE:
|
| 306 |
bnpcap_conn_set_class(c,packet_class_file);
|
| 307 |
break;
|
| 308 |
case 0xf7: // W3 matchmaking hack
|
| 309 |
eventlog(eventlog_level_info,__FUNCTION__,"matchmaking packet");
|
| 310 |
bnpcap_conn_set_class(c,packet_class_bnet);
|
| 311 |
break;
|
| 312 |
default:
|
| 313 |
bnpcap_conn_set_class(c,packet_class_raw);
|
| 314 |
}
|
| 315 |
} else {
|
| 316 |
t_packet *p;
|
| 317 |
unsigned int off;
|
| 318 |
unsigned char const *datap = data;
|
| 319 |
int always_complete = 0;
|
| 320 |
|
| 321 |
|
| 322 |
if (bnpcap_conn_get_class(c) == packet_class_raw)
|
| 323 |
always_complete = 1; /* There is no size field */
|
| 324 |
if (bnpcap_conn_get_class(c) == packet_class_file)
|
| 325 |
always_complete = 1; /* Size field isn't always there */
|
| 326 |
|
| 327 |
if (always_complete) {
|
| 328 |
/* packet is always complete */
|
| 329 |
eventlog(eventlog_level_debug,__FUNCTION__,"packet is always complete (class=%d)",bnpcap_conn_get_class(c));
|
| 330 |
bp = (t_bnpcap_packet *) malloc(sizeof(t_bnpcap_packet)); /* avoid warning */
|
| 331 |
if (!bp) {
|
| 332 |
eventlog(eventlog_level_error,__FUNCTION__,"malloc failed: %s",pstrerror(errno));
|
| 333 |
return -1;
|
| 334 |
}
|
| 335 |
bp->dir = bnpcap_conn_get_dir(c,&s,&d);
|
| 336 |
bp->p = packet_create(bnpcap_conn_get_class(c));
|
| 337 |
bp->id = current_packet_id++;
|
| 338 |
if (!bp->p) {
|
| 339 |
eventlog(eventlog_level_error,__FUNCTION__,"packet_create failed");
|
| 340 |
return -1;
|
| 341 |
}
|
| 342 |
memcpy(&bp->tv,&packettime,sizeof(struct timeval));
|
| 343 |
packet_set_size(bp->p,len);
|
| 344 |
memcpy(packet_get_raw_data(bp->p,0),data,len);
|
| 345 |
bnpcap_conn_add_packet(c,bp);
|
| 346 |
if ((packet_get_class(bp->p)==packet_class_file)&&(packet_get_type(bp->p)==SERVER_FILE_REPLY)) {
|
| 347 |
eventlog(eventlog_level_debug,__FUNCTION__,"file transfer initiated (setting to raw)");
|
| 348 |
bnpcap_conn_set_class(c,packet_class_raw);
|
| 349 |
}
|
| 350 |
} else {
|
| 351 |
/* read out saved state */
|
| 352 |
if (bnpcap_conn_get_dir(c,&s,&d)==packet_dir_from_client) {
|
| 353 |
p = c->clientpkt;
|
| 354 |
off = c->clientoff;
|
| 355 |
} else {
|
| 356 |
p = c->serverpkt;
|
| 357 |
off = c->serveroff;
|
| 358 |
}
|
| 359 |
while ((datap-data)<(signed)len) {
|
| 360 |
if (!p) {
|
| 361 |
eventlog(eventlog_level_debug,__FUNCTION__,"creating new packet");
|
| 362 |
p = packet_create(bnpcap_conn_get_class(c));
|
| 363 |
if (!p) {
|
| 364 |
eventlog(eventlog_level_error,__FUNCTION__,"packet_create failed");
|
| 365 |
return -1;
|
| 366 |
}
|
| 367 |
packet_set_size(p,packet_get_header_size(p)); /* set it to the minimum for now */
|
| 368 |
off = 0;
|
| 369 |
}
|
| 370 |
if (off < packet_get_header_size(p)) {
|
| 371 |
unsigned int l = (packet_get_header_size(p)-off);
|
| 372 |
/* (len-(datap-data)) : remaining bytes in buffer */
|
| 373 |
if ((len-(datap-data)) < l)
|
| 374 |
l = (len-(datap-data));
|
| 375 |
eventlog(eventlog_level_debug,__FUNCTION__,"filling up header (adding %d to %d to get %d)",l,off,packet_get_header_size(p));
|
| 376 |
memcpy(packet_get_raw_data(p,off),datap,l);
|
| 377 |
datap = datap + l;
|
| 378 |
off = off + l;
|
| 379 |
} else {
|
| 380 |
unsigned int l = (packet_get_size(p)-off);
|
| 381 |
if ((len-(datap-data)) < l)
|
| 382 |
l = (len-(datap-data));
|
| 383 |
eventlog(eventlog_level_debug,__FUNCTION__,"filling up packet (0x%04x:%s) (adding %d to %d to get %d)",packet_get_type(p),packet_get_type_str(p,bnpcap_conn_get_dir(c,&s,&d)),l,off,packet_get_size(p));
|
| 384 |
memcpy(packet_get_raw_data(p,off),datap,l);
|
| 385 |
datap = datap + l;
|
| 386 |
off = off + l;
|
| 387 |
}
|
| 388 |
if ((off>=packet_get_header_size(p))&&(off>=packet_get_size(p))) {
|
| 389 |
/* packet is complete */
|
| 390 |
eventlog(eventlog_level_debug,__FUNCTION__,"packet is complete");
|
| 391 |
bp = (t_bnpcap_packet *) malloc(sizeof(t_bnpcap_packet)); /* avoid warning */
|
| 392 |
if (!bp) {
|
| 393 |
eventlog(eventlog_level_error,__FUNCTION__,"malloc failed: %s",pstrerror(errno));
|
| 394 |
return -1;
|
| 395 |
}
|
| 396 |
if ((off != packet_get_size(p))&&(bnpcap_dodebug)) {
|
| 397 |
eventlog(eventlog_level_warn,__FUNCTION__,"packet size differs (%d != %d) (offset=0x%04x)",off,packet_get_size(p),datap-data);
|
| 398 |
hexdump(stderr,data,len);
|
| 399 |
/* memcpy(packet_get_raw_data(p,0),data,packet_get_size(p)); */
|
| 400 |
}
|
| 401 |
bp->dir = bnpcap_conn_get_dir(c,&s,&d);
|
| 402 |
bp->p = p;
|
| 403 |
bp->id = current_packet_id++;
|
| 404 |
memcpy(&bp->tv,&packettime,sizeof(struct timeval));
|
| 405 |
bnpcap_conn_add_packet(c,bp);
|
| 406 |
if (packet_get_size(p)==0)
|
| 407 |
datap = data + len; /* if size is invalid, drop the rest of the stream packet */
|
| 408 |
p = NULL;
|
| 409 |
off = 0;
|
| 410 |
}
|
| 411 |
} /* while */
|
| 412 |
/* write back saved state */
|
| 413 |
if ((off>0)&&(bnpcap_dodebug)) {
|
| 414 |
eventlog(eventlog_level_debug,__FUNCTION__,"saving %d bytes in packet buffer (p=0x%08x)",off,(int)p);
|
| 415 |
}
|
| 416 |
if (bnpcap_conn_get_dir(c,&s,&d)==packet_dir_from_client) {
|
| 417 |
c->clientpkt = p;
|
| 418 |
c->clientoff = off;
|
| 419 |
} else {
|
| 420 |
c->serverpkt = p;
|
| 421 |
c->serveroff = off;
|
| 422 |
}
|
| 423 |
} /* !always_complete */
|
| 424 |
return 0;
|
| 425 |
}
|
| 426 |
return 0;
|
| 427 |
}
|
| 428 |
|
| 429 |
|
| 430 |
/************************************************************************/
|
| 431 |
/******************************** LAYERS ********************************/
|
| 432 |
|
| 433 |
/********************************* TCP **********************************/
|
| 434 |
|
| 435 |
static int bnpcap_tcp2tcp(t_tcp_header * d, t_tcp_header_raw const * s)
|
| 436 |
{
|
| 437 |
d->sport = htons(s->sport);
|
| 438 |
d->dport = htons(s->dport);
|
| 439 |
d->seqno = htonl(s->seqno);
|
| 440 |
d->ackno = htonl(s->ackno);
|
| 441 |
d->doffset = (htons(s->stuff) & 0xF000) >> 12;
|
| 442 |
d->flags = (htons(s->stuff) & 0x0FFF);
|
| 443 |
d->window = htons(s->window);
|
| 444 |
d->checksum = htons(s->checksum);
|
| 445 |
d->urgp = htons(s->urgp);
|
| 446 |
return 0;
|
| 447 |
}
|
| 448 |
|
| 449 |
static int bnpcap_process_tcp(t_ip_header const * ip, unsigned char const *data, unsigned int len)
|
| 450 |
{
|
| 451 |
t_tcp_header_raw const *raw;
|
| 452 |
t_tcp_header h;
|
| 453 |
|
| 454 |
raw = (t_tcp_header_raw const *) data; /* avoid warning */
|
| 455 |
bnpcap_tcp2tcp(&h,raw);
|
| 456 |
if (h.doffset < 5) {
|
| 457 |
eventlog(eventlog_level_warn,__FUNCTION__,"tcp header too small (%u 32-bit words)",h.doffset);
|
| 458 |
return 1;
|
| 459 |
} else {
|
| 460 |
char fstr[32] = "";
|
| 461 |
|
| 462 |
if (h.flags & TCP_URG)
|
| 463 |
strcat(fstr,"U");
|
| 464 |
if (h.flags & TCP_ACK)
|
| 465 |
strcat(fstr,"A");
|
| 466 |
if (h.flags & TCP_PSH)
|
| 467 |
strcat(fstr,"P");
|
| 468 |
if (h.flags & TCP_RST)
|
| 469 |
strcat(fstr,"R");
|
| 470 |
if (h.flags & TCP_SYN)
|
| 471 |
strcat(fstr,"S");
|
| 472 |
if (h.flags & TCP_FIN)
|
| 473 |
strcat(fstr,"F");
|
| 474 |
eventlog(eventlog_level_debug,__FUNCTION__,"tcp: sport=%u dport=%u seqno=0x%08x ackno=0x%08x window=0x%08x len=%d (%s)",h.sport,h.dport,h.seqno,h.ackno,h.window,((signed)len-(h.doffset*4)),fstr);
|
| 475 |
if (((signed)len-(h.doffset*4))<=0) {
|
| 476 |
eventlog(eventlog_level_info,__FUNCTION__,"empty packet (%d)",((signed)len-(h.doffset*4)));
|
| 477 |
/* handle sync packets */
|
| 478 |
if (h.flags & TCP_SYN) {
|
| 479 |
t_bnpcap_addr s,d;
|
| 480 |
|
| 481 |
s.ip=ip->src; s.port=h.sport;
|
| 482 |
d.ip=ip->dst; d.port=h.dport;
|
| 483 |
if (h.flags & TCP_ACK) {
|
| 484 |
t_bnpcap_conn *c = bnpcap_conn_find(&s,&d);
|
| 485 |
|
| 486 |
if (c) {
|
| 487 |
if (c->tcpstate==tcp_state_syn)
|
| 488 |
c->tcpstate=tcp_state_ack;
|
| 489 |
}
|
| 490 |
} else {
|
| 491 |
if (!bnpcap_conn_find(&s,&d)) {
|
| 492 |
t_bnpcap_conn *c;
|
| 493 |
|
| 494 |
c = bnpcap_conn_new(&s,&d);
|
| 495 |
c->tcpstate = tcp_state_syn;
|
| 496 |
list_append_data(conns,c);
|
| 497 |
eventlog(eventlog_level_debug,__FUNCTION__,"created new connection with SYN");
|
| 498 |
} else {
|
| 499 |
eventlog(eventlog_level_debug,__FUNCTION__,"got SYN in connection");
|
| 500 |
}
|
| 501 |
}
|
| 502 |
}
|
| 503 |
} else if (((h.sport!=listen_port)&&(h.dport!=listen_port)) && ((h.sport!=6200)&&(h.dport!=6200))) {
|
| 504 |
eventlog(eventlog_level_info,__FUNCTION__,"other packet (%d)",((signed)len-(h.doffset*4)));
|
| 505 |
} else {
|
| 506 |
eventlog(eventlog_level_info,__FUNCTION__,"valid packet (%d)",((signed)len-(h.doffset*4)));
|
| 507 |
bnpcap_conn_packet(ip->src,h.sport,ip->dst,h.dport,data+(h.doffset*4),len-(h.doffset*4));
|
| 508 |
}
|
| 509 |
return 0;
|
| 510 |
}
|
| 511 |
}
|
| 512 |
|
| 513 |
/************************************ UDP ********************************/
|
| 514 |
|
| 515 |
static int bnpcap_udp2udp(t_ip_udp_header *d, t_ip_udp_header_raw const *s)
|
| 516 |
{
|
| 517 |
d->sport = ntohs(s->sport);
|
| 518 |
d->dport = ntohs(s->dport);
|
| 519 |
d->len = ntohs(s->len);
|
| 520 |
d->checksum = ntohs(s->checksum);
|
| 521 |
return 0;
|
| 522 |
}
|
| 523 |
|
| 524 |
static int bnpcap_process_udp(unsigned char const *data, unsigned int len)
|
| 525 |
{
|
| 526 |
t_ip_udp_header_raw const *raw;
|
| 527 |
t_ip_udp_header h;
|
| 528 |
t_bnpcap_packet *bp;
|
| 529 |
|
| 530 |
raw = (t_ip_udp_header_raw const *) data; /* avoid warning */
|
| 531 |
bnpcap_udp2udp(&h,raw);
|
| 532 |
|
| 533 |
bp = (t_bnpcap_packet *) malloc(sizeof(t_bnpcap_packet)); /* avoid warning */
|
| 534 |
if (!bp) {
|
| 535 |
eventlog(eventlog_level_error,__FUNCTION__,"malloc failed: %s",pstrerror(errno));
|
| 536 |
return -1;
|
| 537 |
}
|
| 538 |
if (h.dport==listen_port || h.dport==6200) {
|
| 539 |
bp->dir = packet_dir_from_client;
|
| 540 |
} else {
|
| 541 |
bp->dir = packet_dir_from_server;
|
| 542 |
}
|
| 543 |
eventlog(eventlog_level_debug,__FUNCTION__,"sport=%u dport=%u len=%u(%d)",h.sport,h.dport,h.len,len);
|
| 544 |
bp->id = current_packet_id++;
|
| 545 |
memcpy(&bp->tv,&packettime,sizeof(struct timeval));
|
| 546 |
bp->p = packet_create(packet_class_udp);
|
| 547 |
if (!bp->p) {
|
| 548 |
eventlog(eventlog_level_error,__FUNCTION__,"packet_create failed");
|
| 549 |
return -1;
|
| 550 |
}
|
| 551 |
packet_set_size(bp->p,h.len-sizeof(t_ip_udp_header_raw));
|
| 552 |
memcpy(packet_get_raw_data(bp->p,0),data+sizeof(t_ip_udp_header_raw),h.len-sizeof(t_ip_udp_header_raw));
|
| 553 |
eventlog(eventlog_level_error,__FUNCTION__,"id=%u ",bp->id);
|
| 554 |
list_append_data(udppackets,bp);
|
| 555 |
return 0;
|
| 556 |
}
|
| 557 |
|
| 558 |
/************************************ IP *********************************/
|
| 559 |
|
| 560 |
static int bnpcap_ip2ip(t_ip_header * d, t_ip_header_raw const * s)
|
| 561 |
{
|
| 562 |
d->version = ((s->versionihl & 0xf0) >> 4);
|
| 563 |
d->ihl = s->versionihl & 0x0f;
|
| 564 |
d->tos = s->tos;
|
| 565 |
d->len = ntohs(s->len);
|
| 566 |
d->id = ntohs(s->id);
|
| 567 |
d->offset = ntohl(s->flagsoffset);
|
| 568 |
d->flags = ((d->offset & 0xE000)>>13);
|
| 569 |
d->offset = ((d->offset & 0x1FFF));
|
| 570 |
d->ttl = s->ttl;
|
| 571 |
d->protocol = s->protocol;
|
| 572 |
d->checksum = ntohs(s->checksum);
|
| 573 |
d->src = ntohl(s->src);
|
| 574 |
d->dst = ntohl(s->dst);
|
| 575 |
return 0;
|
| 576 |
}
|
| 577 |
|
| 578 |
|
| 579 |
static int bnpcap_process_ip(unsigned char const *data, unsigned int len)
|
| 580 |
{
|
| 581 |
/* FIXME: handle IP fragmentation */
|
| 582 |
/* FIXME: use identification field to pass the datagram in the right order */
|
| 583 |
t_ip_header_raw const *raw;
|
| 584 |
t_ip_header h;
|
| 585 |
|
| 586 |
raw = (t_ip_header_raw const *) data; /* avoid warning */
|
| 587 |
bnpcap_ip2ip(&h,raw);
|
| 588 |
if (h.version != 4) {
|
| 589 |
eventlog(eventlog_level_warn,__FUNCTION__,"ip version %u not supported (ihl=%u, raw=0x%02x)",h.version,h.ihl,raw->versionihl);
|
| 590 |
return 1;
|
| 591 |
} else if (h.ihl < 5) {
|
| 592 |
/* an IP header must be at least 5 words */
|
| 593 |
eventlog(eventlog_level_warn,__FUNCTION__,"ip header to small (%u 32-bit words)",h.ihl);
|
| 594 |
return 1;
|
| 595 |
} else if (h.len > len) {
|
| 596 |
eventlog(eventlog_level_warn,__FUNCTION__,"ip len larger than packet (%d > %d)",h.len,len);
|
| 597 |
return 1;
|
| 598 |
} else {
|
| 599 |
char fstr[32] = "";
|
| 600 |
|
| 601 |
if (h.flags & IP_DF)
|
| 602 |
strcat(fstr,"D");
|
| 603 |
if (h.flags & IP_MF)
|
| 604 |
strcat(fstr,"M");
|
| 605 |
eventlog(eventlog_level_debug,__FUNCTION__,"ip: len=%u(%u) src=%08x dst=%08x protocol=%u offset=0x%08x id=0x%08x (%s)",h.len,len,h.src,h.dst,h.protocol,h.offset,h.id,fstr);
|
| 606 |
if (h.protocol==6) {
|
| 607 |
/* This is TCP */
|
| 608 |
return bnpcap_process_tcp(&h,data+(h.ihl*4),h.len-(h.ihl*4));
|
| 609 |
} else if (h.protocol==17) {
|
| 610 |
/* This is UDP */
|
| 611 |
return bnpcap_process_udp(data+(h.ihl*4),h.len-(h.ihl*4));
|
| 612 |
}
|
| 613 |
}
|
| 614 |
return 0;
|
| 615 |
}
|
| 616 |
|
| 617 |
/************************************* ETHERNET ******************************/
|
| 618 |
|
| 619 |
static int bnpcap_process_ether(unsigned char const *data, unsigned int len)
|
| 620 |
{ /* Well, first parse the ethernet header (I hope you use Ethernet_II :) ... */
|
| 621 |
t_ether_raw const *raw;
|
| 622 |
|
| 623 |
raw = (t_ether_raw const *) data; /* avoid warning */
|
| 624 |
if (ntohs(raw->type)==0x0800) {
|
| 625 |
/* This is IP */
|
| 626 |
return bnpcap_process_ip(data+sizeof(t_ether_raw),len-sizeof(t_ether_raw));
|
| 627 |
} else {
|
| 628 |
eventlog(eventlog_level_warn,__FUNCTION__,"unsupported protocol 0x%04x",ntohs(raw->type));
|
| 629 |
return 1;
|
| 630 |
}
|
| 631 |
}
|
| 632 |
|
| 633 |
/* If you want to use other hardware protocols like PPP add them here ... */
|
| 634 |
|
| 635 |
/**************************** PACKET/PCAP *********************************/
|
| 636 |
|
| 637 |
static void bnpcap_process_packet(u_char * private, const struct pcap_pkthdr * p, u_char const * data)
|
| 638 |
{
|
| 639 |
unsigned int pl = p->len;
|
| 640 |
|
| 641 |
if(private) private = NULL; // hack to eliminate compiler warning
|
| 642 |
|
| 643 |
memcpy(&packettime,&p->ts,sizeof(struct timeval));
|
| 644 |
eventlog(eventlog_level_debug,__FUNCTION__,"packet: len=%d caplen=%d",p->len,p->caplen);
|
| 645 |
/* FIXME: check if it's ethernet */
|
| 646 |
bnpcap_process_ether(data,pl);
|
| 647 |
}
|
| 648 |
|
| 649 |
/**************************************************************************/
|
| 650 |
|
| 651 |
static void bnpcap_usage(void) {
|
| 652 |
printf("BNPCAP - A tool to convert pcap battle.net dumps to a human-readable format.\n");
|
| 653 |
printf("Version " PVPGN_VERSION " --- Copyright (c) 2001 Marco Ziech (mmz@gmx.net)\n");
|
| 654 |
printf("This software makes use of libpcap.\n\n");
|
| 655 |
printf("Usage: bnpcap [-d] [-v] [-p PORT] <pcap-filename>\n");
|
| 656 |
printf(" -d Print out debugging information\n");
|
| 657 |
printf(" -v Be more verbose\n");
|
| 658 |
printf(" -p PORT Specify port to process (Default: 6112)\n\n");
|
| 659 |
}
|
| 660 |
|
| 661 |
/******************************* MAIN *************************************/
|
| 662 |
|
| 663 |
int main (int argc, char **argv) {
|
| 664 |
t_elem * currconn;
|
| 665 |
t_elem * currudp;
|
| 666 |
int c;
|
| 667 |
|
| 668 |
while ((c=getopt(argc,argv,"dvp:"))!=-1) {
|
| 669 |
switch (c) {
|
| 670 |
case 'p':
|
| 671 |
str_to_uint(optarg, &listen_port);
|
| 672 |
break;
|
| 673 |
case 'd':
|
| 674 |
bnpcap_dodebug = 1;
|
| 675 |
break;
|
| 676 |
case 'v':
|
| 677 |
bnpcap_beverbose = 1;
|
| 678 |
break;
|
| 679 |
case '?':
|
| 680 |
printf("unrecognized option '%c'\n",optopt);
|
| 681 |
break;
|
| 682 |
default:
|
| 683 |
printf("getopt returned \'%c\'\n",c);
|
| 684 |
}
|
| 685 |
}
|
| 686 |
|
| 687 |
if (optind < argc) {
|
| 688 |
filename = argv[optind];
|
| 689 |
} else {
|
| 690 |
bnpcap_usage();
|
| 691 |
return 1;
|
| 692 |
}
|
| 693 |
|
| 694 |
pc = pcap_open_offline(filename,ebuf);
|
| 695 |
if (!pc) {
|
| 696 |
fprintf(stderr,"pcap_open_offline: %s\n",ebuf);
|
| 697 |
return -1;
|
| 698 |
}
|
| 699 |
|
| 700 |
eventlog_set(stderr);
|
| 701 |
eventlog_clear_level();
|
| 702 |
if (bnpcap_dodebug)
|
| 703 |
eventlog_add_level("debug");
|
| 704 |
if (bnpcap_beverbose||bnpcap_dodebug)
|
| 705 |
eventlog_add_level("info");
|
| 706 |
eventlog_add_level("warn");
|
| 707 |
eventlog_add_level("error");
|
| 708 |
eventlog_add_level("fatal");
|
| 709 |
|
| 710 |
conns = list_create();
|
| 711 |
udppackets = list_create();
|
| 712 |
pcap_dispatch(pc,0,bnpcap_process_packet,NULL);
|
| 713 |
|
| 714 |
printf("### This packet dump was created by bnpcap.\n");
|
| 715 |
LIST_TRAVERSE(conns,currconn) {
|
| 716 |
t_bnpcap_conn *c;
|
| 717 |
char cstr[64];
|
| 718 |
char sstr[64];
|
| 719 |
t_elem * currpacket;
|
| 720 |
|
| 721 |
c = elem_get_data(currconn);
|
| 722 |
snprintf(cstr,64,"%u.%u.%u.%u:%u",((c->client.ip & 0xFF000000)>>24),((c->client.ip & 0x00FF0000)>>16),((c->client.ip & 0x0000FF00)>>8),((c->client.ip & 0x000000FF)),c->client.port);
|
| 723 |
snprintf(sstr,64,"%u.%u.%u.%u:%u",((c->server.ip & 0xFF000000)>>24),((c->server.ip & 0x00FF0000)>>16),((c->server.ip & 0x0000FF00)>>8),((c->server.ip & 0x000000FF)),c->server.port);
|
| 724 |
printf("## %s connection: client=%s server=%s\n",(c->incomplete?"incomplete":"complete"),cstr,sstr);
|
| 725 |
LIST_TRAVERSE(c->packets,currpacket) {
|
| 726 |
t_bnpcap_packet * bp;
|
| 727 |
|
| 728 |
bp = elem_get_data(currpacket);
|
| 729 |
printf("# %u packet from %s: type=0x%04x(%s) length=%d class=%s\n",bp->id/*bp->tv.tv_sec*/,(bp->dir==packet_dir_from_client?"client":"server"),packet_get_type(bp->p),packet_get_type_str(bp->p,bp->dir),packet_get_size(bp->p),packet_get_class_str(bp->p));
|
| 730 |
hexdump(stdout,packet_get_raw_data(bp->p,0),packet_get_size(bp->p));
|
| 731 |
printf("\n");
|
| 732 |
}
|
| 733 |
}
|
| 734 |
printf("## udp packets\n");
|
| 735 |
LIST_TRAVERSE(udppackets,currudp) {
|
| 736 |
t_bnpcap_packet *bp;
|
| 737 |
|
| 738 |
bp = elem_get_data(currudp);
|
| 739 |
printf("# %u packet from %s: type=0x%04x(%s) length=%d class=%s\n",bp->id/*bp->tv.tv_sec*/,(bp->dir==packet_dir_from_client?"client":"server"),packet_get_type(bp->p),packet_get_type_str(bp->p,bp->dir),packet_get_size(bp->p),packet_get_class_str(bp->p));
|
| 740 |
hexdump(stdout,packet_get_raw_data(bp->p,0),packet_get_size(bp->p));
|
| 741 |
printf("\n");
|
| 742 |
}
|
| 743 |
pcap_close(pc);
|
| 744 |
return 0;
|
| 745 |
}
|
| 746 |
|