pktdump.c 10 KB
Newer Older
1 2
/* pktdump.c */

3
/* $Id: pktdump.c,v 1.17 2020/04/28 05:41:30 rswindell Exp $ */
4 5

#include "fidodefs.h"
deuce's avatar
deuce committed
6
#include "xpendian.h"	/* swap */
7 8
#include "dirwrap.h"	/* _PATH_DEVNULL */
#include <stdio.h>
9
#include <stdbool.h>
10
#include <string.h>
11 12 13

FILE* nulfp;
FILE* bodyfp;
14
FILE* ctrlfp;
15 16 17 18

/****************************************************************************/
/* Returns an ASCII string for FidoNet address 'addr'                       */
/****************************************************************************/
19
char *faddrtoa(struct fidoaddr* addr, char* outstr)
20 21 22 23 24 25
{
	static char str[64];

	if(addr==NULL)
		return("0:0/0");
	sprintf(str,"%hu:%hu/%hu",addr->zone,addr->net,addr->node);
26 27 28 29
	if(addr->point)
		sprintf(str + strlen(str), ".%hu", addr->point);
	if(addr->domain[0])
		sprintf(str + strlen(str), "@%s", addr->domain);
30 31 32 33 34 35
	if(outstr==NULL)
		return(str);
	strcpy(outstr,str);
	return(outstr);
}

36
bool freadstr(FILE* fp, char* str, size_t maxlen)
37 38 39 40
{
	int		ch;
	size_t	len=0;

41
	memset(str, 0, maxlen);
42 43 44 45 46 47
	while((ch=fgetc(fp))!=EOF && len<maxlen) {
		str[len++]=ch;
		if(ch==0)
			break;
	}

48
	return str[maxlen-1] == 0;
49 50
}

51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73
const char* fmsgattr_str(uint16_t attr)
{
	static char str[64] = "";

#define FIDO_ATTR_CHECK(a, f) if(a&FIDO_##f)	sprintf(str + strlen(str), "%s%s", str[0] == 0 ? "" : ", ", #f);
	FIDO_ATTR_CHECK(attr, PRIVATE);
	FIDO_ATTR_CHECK(attr, CRASH);
	FIDO_ATTR_CHECK(attr, RECV);
	FIDO_ATTR_CHECK(attr, SENT);
	FIDO_ATTR_CHECK(attr, FILE);
	FIDO_ATTR_CHECK(attr, INTRANS);
	FIDO_ATTR_CHECK(attr, ORPHAN);
	FIDO_ATTR_CHECK(attr, KILLSENT);
	FIDO_ATTR_CHECK(attr, LOCAL);
	FIDO_ATTR_CHECK(attr, HOLD);
	FIDO_ATTR_CHECK(attr, FREQ);
	FIDO_ATTR_CHECK(attr, RRREQ);
	FIDO_ATTR_CHECK(attr, RR);
	FIDO_ATTR_CHECK(attr, AUDIT);
	FIDO_ATTR_CHECK(attr, FUPREQ);
	return str;
}

74
int pktdump(FILE* fp, const char* fname, FILE* good, FILE* bad)
75
{
76
	int			ch,lastch=0;
77 78 79 80 81
	char		buf[128];
	char		to[FIDO_NAME_LEN];
	char		from[FIDO_NAME_LEN];
	char		subj[FIDO_SUBJ_LEN];
	long		offset;
82 83
	struct fidoaddr	orig = {0};
	struct fidoaddr	dest = {0};
84 85 86
	fpkthdr_t	pkthdr;
	fpkdmsg_t	pkdmsg;

87
	if(fread(&pkthdr,sizeof(pkthdr),1,fp) != 1) {
88
		fprintf(stderr,"%s !Error reading pkthdr (%" XP_PRIsize_t "u bytes)\n"
89 90 91 92 93 94 95 96 97 98 99 100
			,fname,sizeof(pkthdr));
		return(-1);
	}

	fseek(fp,-2L,SEEK_END);
	fread(buf,sizeof(BYTE),sizeof(buf),fp);
	if(memcmp(buf,"\x00\x00",2)) {
		fprintf(stderr,"%s !Packet missing terminating nulls: %02X %02X\n"
			,fname,buf[0],buf[1]);
//		return(-2);
	}

101 102 103 104 105 106 107 108 109 110
	printf("%s Packet Type ", fname);

	if(pkthdr.type2.pkttype != 2) {
		fprintf(stderr, "%u (unsupported packet type)\n", pkthdr.type2.pkttype);
		return -3;
	}

	orig.zone=pkthdr.type2.origzone;
	orig.net=pkthdr.type2.orignet;
	orig.node=pkthdr.type2.orignode;
111 112
	orig.point=0;

113 114 115
	dest.zone=pkthdr.type2.destzone;
	dest.net=pkthdr.type2.destnet;
	dest.node=pkthdr.type2.destnode;
116 117
	dest.point=0;				/* No point info in the 2.0 hdr! */

118 119
	if(pkthdr.type2plus.cword==BYTE_SWAP_16(pkthdr.type2plus.cwcopy)  /* 2+ Packet Header */
		&& pkthdr.type2plus.cword&1) {
120 121 122
		fprintf(stdout,"2+ (prod: %02X%02X, rev: %u.%u)"
			,pkthdr.type2plus.prodcodeHi	,pkthdr.type2plus.prodcodeLo
			,pkthdr.type2plus.prodrevMajor	,pkthdr.type2plus.prodrevMinor);
123 124 125 126
		dest.point=pkthdr.type2plus.destpoint;
		if(pkthdr.type2plus.origpoint!=0 && orig.net==0xffff) {	/* see FSC-0048 for details */
			orig.net=pkthdr.type2plus.auxnet;
			orig.point=pkthdr.type2plus.origpoint;
127
		}
128
		if(pkthdr.type2plus.origzone != orig.zone)
129
			printf("!Warning: origination zone mismatch in type 2+ packet header (%u != %u)\n"
130 131
				,pkthdr.type2plus.origzone, orig.zone);
		if(pkthdr.type2plus.destzone != dest.zone)
132
			printf("!Warning: destination zone mismatch in type 2+ packet header (%u != %u)\n"
133
				,pkthdr.type2plus.destzone, dest.zone);
134
	} else if(pkthdr.type2_2.subversion==2) {					/* Type 2.2 Packet Header (FSC-45) */
135
		fprintf(stdout,"2.2 (prod: %02X, rev: %u)", pkthdr.type2_2.prodcode, pkthdr.type2_2.prodrev);
136
		dest.point=pkthdr.type2_2.destpoint; 
137 138
		memcpy(orig.domain, pkthdr.type2_2.origdomn, sizeof(pkthdr.type2_2.origdomn));
		memcpy(dest.domain, pkthdr.type2_2.destdomn, sizeof(pkthdr.type2_2.destdomn));
139
	} else
140
		fprintf(stdout,"2.0 (prod: %02X, serial: %u)", pkthdr.type2.prodcode, pkthdr.type2.sernum);
141

142 143
	printf(" from %s", faddrtoa(&orig,NULL));
	printf(" to %s\n", faddrtoa(&dest,NULL));
144

145
	if(pkthdr.type2.password[0])
146
		fprintf(stdout,"Password: '%.*s'\n",(int)sizeof(pkthdr.type2.password),pkthdr.type2.password);
147

148 149 150 151
	if(good != NULL)
		fwrite(&pkthdr, sizeof(pkthdr), 1, good);
	if(bad != NULL)
		fwrite(&pkthdr, sizeof(pkthdr), 1, bad);
152 153 154 155 156
	fseek(fp,sizeof(pkthdr),SEEK_SET);

	/* Read/Display packed messages */
	while(!feof(fp)) {

157
		offset=ftell(fp);
158

159 160 161 162 163 164 165 166 167 168 169 170 171 172
		if(fread(&pkdmsg.type, 1, sizeof(pkdmsg.type), fp) != sizeof(pkdmsg.type))
			break;
		if(pkdmsg.type == FIDO_PACKET_TERMINATOR)
			continue;
		if(pkdmsg.type != 2) {
			printf("%s %06lX Corrupted Message Header (type: %04hX)\n"
				,fname
				,offset
				,pkdmsg.type);
			continue;
		}

		bool corrupted = false;
		fseek(fp, offset, SEEK_SET);
173
		/* Read fixed-length header fields (or final NULL byte) */
174 175
		if(fread(&pkdmsg,sizeof(BYTE),sizeof(pkdmsg),fp)!=sizeof(pkdmsg))
			break;
176 177 178 179 180 181
		if(pkdmsg.time[0] == '\0' || pkdmsg.time[sizeof(pkdmsg.time) - 1] != '\0') {
			printf("%s %06lX Corrupted Message Header (DateTime)\n"
				,fname
				,offset);
			corrupted = true;
		}
182 183 184 185 186

		/* Read variable-length header fields */
		freadstr(fp,to,sizeof(to));
		freadstr(fp,from,sizeof(from));
		freadstr(fp,subj,sizeof(subj));
187
		if(!corrupted && (from[0] == '\0'
188 189 190
			|| to[sizeof(to) - 1] != '\0'
			|| from[sizeof(from) - 1] != '\0'
			|| subj[sizeof(subj) - 1] != '\0'
191
			)) {
192
			printf("%s %06lX Corrupted Message Header (variable-length fields)\n"
193 194
				,fname
				,offset);
195 196 197 198
			corrupted = true;
		}

		if(corrupted) { // Seek to the end of the message body (hopefully)
199 200 201 202 203 204 205 206 207 208 209 210 211 212 213
			if(bad != NULL) {
				fwrite(&pkdmsg, sizeof(pkdmsg), 1, bad);
				for(int i = 0, ch = EOF; i < sizeof(to) && ch != '\0'; i++)
					fputc(ch = to[i], bad);
				for(int i = 0, ch = EOF; i < sizeof(from) && ch != '\0'; i++)
					fputc(ch = from[i], bad);
				for(int i = 0, ch = EOF; i < sizeof(subj) && ch != '\0'; i++)
					fputc(ch = subj[i], bad);
			}
			while((ch = fgetc(fp)) != EOF && ch != 0) {
				if(bad != NULL)
					fputc(ch, bad);
			}
			if(bad != NULL)
				fputc('\0', bad);
214 215 216
			continue;
		}

217 218 219 220 221 222 223
		/* Display fixed-length fields */
		printf("%s %06lX Packed Message Type: %d from %u/%u to %u/%u\n"
			,fname
			,offset
			,pkdmsg.type
			,pkdmsg.orignet, pkdmsg.orignode
			,pkdmsg.destnet, pkdmsg.destnode);
224
		printf("Attribute: 0x%04X (%s)\n",pkdmsg.attr, fmsgattr_str(pkdmsg.attr));
225 226 227 228 229 230 231
		printf("Date/Time: %s\n",pkdmsg.time);
	
		/* Display variable-length fields */
		printf("%-4s : %s\n","To",to);
		printf("%-4s : %s\n","From",from);
		printf("%-4s : %s\n","Subj",subj);

232 233 234 235 236
		if(good != NULL) {
			fwrite(&pkdmsg, sizeof(pkdmsg), 1, good);
			fwrite(to, strlen(to) + 1, 1, good);
			fwrite(from, strlen(from) + 1, 1, good);
			fwrite(subj, strlen(subj) + 1, 1, good);
237 238
		}

239 240
		fprintf(bodyfp,"\n-start of message text-\n");

241
		size_t count = 0;
242
		while((ch=fgetc(fp))!=EOF && ch!=0) {
243 244 245 246 247 248 249 250 251 252
			count++;
			if((count == 1 || lastch == '\r') && ch == 1) {
				fputc('@', ctrlfp);
				while((ch = fgetc(fp)) != EOF && ch != 0 && ch != '\r')
					fputc(ch, ctrlfp);
				fputc('\n', ctrlfp);
				if(ch == 0)
					break;
				continue;
			}
253 254 255
			if(lastch=='\r' && ch!='\n')
				fputc('\n',bodyfp);
			fputc(lastch=ch,bodyfp);
256 257
			if(good != NULL)
				fputc(ch, good);
258
		}
259 260
		if(good != NULL)
			fputc('\0', good);
261 262 263

		fprintf(bodyfp,"\n-end of message text-\n");
	}
264 265 266 267 268 269 270
	if(good != NULL) { // Final terminating NULL bytes
		fputc('\0', good);
		fputc('\0', good);
	}
	if(bad != NULL) { // Final terminating NULL bytes
		fputc('\0', bad);
		fputc('\0', bad);
271
	}
272 273 274 275

	return(0);
}

276
char* usage = "usage: pktdump [-body | -ctrl] [-recover | -split] <file1.pkt> [file2.pkt] [...]\n";
277

278 279 280
int main(int argc, char** argv)
{
	FILE*	fp;
281
	bool	split = false;
282
	bool	recover = false;
283
	int		i;
284 285
	char	revision[16];

286
	sscanf("$Revision: 1.17 $", "%*s %s", revision);
287 288 289 290 291 292 293 294 295

	fprintf(stderr,"pktdump rev %s - Dump FidoNet Packets\n\n"
		,revision
		);

	if(argc<2) {
		fprintf(stderr,"%s",usage);
		return -1;
	}
296 297 298 299 300 301

	if((nulfp=fopen(_PATH_DEVNULL,"w+"))==NULL) {
		perror(_PATH_DEVNULL);
		return -1;
	}
	bodyfp=nulfp;
302
	ctrlfp=nulfp;
303 304

	if(sizeof(fpkthdr_t)!=FIDO_PACKET_HDR_LEN) {
305
		printf("sizeof(fpkthdr_t)=%" XP_PRIsize_t "u, expected: %d\n",sizeof(fpkthdr_t),FIDO_PACKET_HDR_LEN);
306 307 308
		return(-1);
	}
	if(sizeof(fpkdmsg_t)!=FIDO_PACKED_MSG_HDR_LEN) {
309
		printf("sizeof(fpkdmsg_t)=%" XP_PRIsize_t "u, expected: %d\n",sizeof(fpkdmsg_t),FIDO_PACKED_MSG_HDR_LEN);
310 311 312
		return(-1);
	}
	if(sizeof(fmsghdr_t)!=FIDO_STORED_MSG_HDR_LEN) {
313
		printf("sizeof(fmsghdr_t)=%" XP_PRIsize_t "u, expected: %d\n",sizeof(fmsghdr_t),FIDO_STORED_MSG_HDR_LEN);
314 315 316 317 318 319 320
		return(-1);
	}

	for(i=1;i<argc;i++) {
		if(argv[i][0]=='-') {
			switch(tolower(argv[i][1])) {
				case 'b':
321
					bodyfp=stdout;
322 323 324
					/* fall-through */
				case 'c':
					ctrlfp = stdout;
325 326 327
					break;
				case 'r':
					recover=true;
328
					break;
329 330 331
				case 's':
					split=true;
					break;
332
				default:
333
					printf("%s",usage);
334 335 336 337 338 339 340 341 342
					return(0);
			}
			continue;
		}
		fprintf(stdout,"Opening %s\n",argv[i]);
		if((fp=fopen(argv[i],"rb"))==NULL) {
			perror(argv[i]);
			continue;
		}
343 344 345 346 347 348 349
		FILE* good = NULL;
		FILE* bad = NULL;
		char good_fname[MAX_PATH + 1] = "";
		char bad_fname[MAX_PATH + 1] = "";
		if(recover || split) {
			SAFEPRINTF(good_fname, "%s.good", argv[i]);
			if((good = fopen(good_fname, "wb")) == NULL) {
350 351 352 353
				perror(argv[i]);
				return EXIT_FAILURE;
			}
		}
354 355 356 357 358 359
		if(split) {
			SAFEPRINTF(bad_fname, "%s.bad", argv[i]);
			if((bad = fopen(bad_fname, "wb")) == NULL) {
				perror(argv[i]);
				return EXIT_FAILURE;
			}
360
		}
361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378
		pktdump(fp, argv[i], good, bad);
		if(good != NULL) {
			long length = ftell(good);
			fclose(good);
			good = NULL;
			if(length <= sizeof(fpkthdr_t) + sizeof(uint16_t) // no messages
				|| length == ftell(fp))
				remove(good_fname);
		}
		if(bad != NULL) {
			long length = ftell(bad);
			fclose(bad);
			bad = NULL;
			if(length <= sizeof(fpkthdr_t) + sizeof(uint16_t) // no messages
				|| length == ftell(fp))
				remove(bad_fname);
		}
		fclose(fp);
379 380 381
	}

	return(0);
deuce's avatar
deuce committed
382
}