jcs
/subtext
/amendments
/314
fidopkt: Add FidoNet packet message parser
This only supports type 4d format messages, but that's all I've seen.
jcs made amendment 314 about 1 year ago
--- fidopkt.c Thu Feb 23 09:48:06 2023
+++ fidopkt.c Thu Feb 23 09:48:06 2023
@@ -0,0 +1,341 @@
+/*
+ * FidoNet echomail packet parser
+ * http://wiki.synchro.net/ref:fidonet_packets
+ *
+ * Copyright (c) 2023 joshua stein <jcs@jcs.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include "fidopkt.h"
+#include "logger.h"
+#include "util.h"
+#include "zip.h" /* for GET_U* */
+
+/* #define FIDOPKT_DEBUG */
+
+#define PKTHEADER_ORIGNODE 0
+#define PKTHEADER_DESTNODE 2
+#define PKTHEADER_YEAR 4
+#define PKTHEADER_MONTH 6
+#define PKTHEADER_DAY 8
+#define PKTHEADER_HOUR 10
+#define PKTHEADER_MINUTE 12
+#define PKTHEADER_SECOND 14
+
+#define PKTHEADER_BAUD 16
+#define PKTHEADER_PKTTYPE 18
+
+#define PKTHEADER_ORIGNET 20
+#define PKTHEADER_DESTNET 22
+#define PKTHEADER_PRODCODELOW 24
+#define PKTHEADER_REVMAJOR 25
+#define PKTHEADER_PASSWORD 26
+
+#define PKTHEADER_QORIGZONE 34
+#define PKTHEADER_QDESTZONE 36
+
+#define PKTHEADER_AUXNET 38
+#define PKTHEADER_CWVALIDCOPY 40
+#define PKTHEADER_PRODCODEHIGH 42
+#define PKTHEADER_REVMINOR 43
+#define PKTHEADER_CAPABILWORD 44
+
+#define PKTHEADER_ORIGZONE 46
+#define PKTHEADER_DESTZONE 48
+#define PKTHEADER_ORIGPOINT 50
+#define PKTHEADER_DESTPOINT 52
+#define PKTHEADER_PRODDATA 54
+
+#define SIZE_PKTHEADER 58
+
+/* PktMsgHeader */
+#define PKTMSGHEADER_PKTTYPE 0
+#define PKTMSGHEADER_ORIGNODE 2
+#define PKTMSGHEADER_DESTNODE 4
+#define PKTMSGHEADER_ORIGNET 6
+#define PKTMSGHEADER_DESTNET 8
+#define PKTMSGHEADER_ATTR 10
+#define PKTMSGHEADER_COST 12
+
+#define SIZE_PKTMSGHEADER 14
+
+static const char *months[] = {
+ "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct",
+ "Nov", "Dec",
+};
+
+size_t fidopkt_read_until(char **buf, size_t *buf_len, char delim,
+ size_t max_len, char *ret, bool terminate);
+
+size_t
+fidopkt_read_until(char **buf, size_t *buf_len, char delim,
+ size_t max_len, char *ret, bool terminate)
+{
+ char ch;
+ size_t retpos = 0;
+ bool found = false;
+
+ if (*buf_len < max_len)
+ max_len = *buf_len;
+
+ while (max_len != 0) {
+ ch = *(*buf)++;
+ ret[retpos++] = (ch == delim ? '\0' : ch);
+ max_len--;
+ (*buf_len)--;
+ if (ch == delim) {
+ found = true;
+ break;
+ }
+ }
+ if (terminate || found)
+ ret[retpos - 1] = '\0';
+
+ return retpos;
+}
+
+struct fidopkt *
+fidopkt_parse(char *packet_filename, char *buf, size_t len)
+{
+ static char line[100];
+#ifdef FIDOPKT_DEBUG
+ static char datetime[100], tz[50], ct[50];
+ char *lastbreak, was;
+#endif
+ struct fidopkt *ret;
+ size_t message_len, llen;
+ long tzoff;
+ struct tm tm = { 0 };
+ char dmon[6];
+ short dday, dyear, dhour, dmin, dsec, dutc, n;
+ bool tear;
+
+#ifdef FIDOPKT_DEBUG
+ logger_printf("[fidopkt] Parsing %s ==========", packet_filename);
+#endif
+
+ if (len < SIZE_PKTHEADER) {
+ logger_printf("[fidopkt] packet too small (%ld)", len);
+ return;
+ }
+
+ if (GET_U16(buf + PKTHEADER_PKTTYPE) != 2) {
+ logger_printf("[fidopkt] not a fidonet packet");
+ return;
+ }
+
+ if (GET_U16(buf + PKTHEADER_BAUD) == 2) {
+ logger_printf("[fidopkt] 5d format not supported");
+ return;
+ }
+
+ if (buf[PKTHEADER_CWVALIDCOPY] != buf[PKTHEADER_CAPABILWORD + 1] ||
+ buf[PKTHEADER_CWVALIDCOPY + 1] != buf[PKTHEADER_CAPABILWORD]) {
+ logger_printf("[fidopkt] not in 4d format");
+ return;
+ }
+
+ buf += SIZE_PKTHEADER;
+ len -= SIZE_PKTHEADER;
+
+ if (len < SIZE_PKTMSGHEADER) {
+ logger_printf("[fidopkt] message header too short");
+ return;
+ }
+
+ if (GET_U16(buf + PKTMSGHEADER_PKTTYPE) != 2) {
+ logger_printf("[fidopkt] header packet type != 2");
+ return;
+ }
+
+ buf += SIZE_PKTMSGHEADER;
+ len -= SIZE_PKTMSGHEADER;
+
+ ret = xmalloczero(sizeof(struct fidopkt), "fidopkt");
+
+ fidopkt_read_until(&buf, &len, '\0', sizeof(line), line, true);
+ if (len == 0) {
+ logger_printf("[fidopkt] short read");
+ goto parse_fail;
+ }
+ if (sscanf(line, "%d %5s %d %d:%d:%d", &dday, (char *)&dmon, &dyear,
+ &dhour, &dmin, &dsec) != 6) {
+ logger_printf("[fidopkt] couldn't parse date \"%s\"", line);
+ goto parse_fail;
+ }
+
+ tm.tm_sec = dsec;
+ tm.tm_min = dmin;
+ tm.tm_hour = dhour;
+ tm.tm_mday = dday;
+ tm.tm_year = 100 + dyear;
+ tm.tm_mon = -1;
+ for (n = 0; n < nitems(months); n++) {
+ if (strcmp(months[n], dmon) == 0) {
+ tm.tm_mon = n;
+ break;
+ }
+ }
+ if (tm.tm_mon == -1) {
+ logger_printf("[fidopkt] couldn't parse month \"%s\"", dmon);
+ goto parse_fail;
+ }
+#ifdef FIDOPKT_DEBUG
+ strlcpy(datetime, line, sizeof(datetime));
+#endif
+
+ fidopkt_read_until(&buf, &len, '\0', sizeof(ret->to), ret->to, true);
+ if (len == 0) {
+ logger_printf("[fidopkt] short read to");
+ goto parse_fail;
+ }
+ fidopkt_read_until(&buf, &len, '\0', sizeof(ret->from), ret->from, true);
+ if (len == 0) {
+ logger_printf("[fidopkt] short read from");
+ goto parse_fail;
+ }
+ fidopkt_read_until(&buf, &len, '\0', sizeof(ret->subject), ret->subject,
+ true);
+ if (len == 0) {
+ logger_printf("[fidopkt] short read subject");
+ goto parse_fail;
+ }
+
+ fidopkt_read_until(&buf, &len, '\r', sizeof(line), line, true);
+ if (len == 0) {
+ logger_printf("[fidopkt] short read area");
+ goto parse_fail;
+ }
+ if (strncmp(line, "AREA:", 5) != 0) {
+ logger_printf("[fidopkt] no AREA in first line");
+ goto parse_fail;
+ }
+ strlcpy(ret->area, line + 5, sizeof(ret->area));
+
+ ret->message = xmalloc(len, "fidopkt message");
+ message_len = 0;
+
+ tear = false;
+ while (len) {
+ llen = fidopkt_read_until(&buf, &len, '\r', sizeof(line), line,
+ false);
+ /*
+ * We are not terminating long lines now, so don't use unbounded
+ * string comparisons.
+ */
+ if (memcmp(line, "--- ", 4) == 0 || memcmp(line, "---\0", 4) == 0) {
+ /* tear line */
+ tear = true;
+ } else if (line[0] == 0x01) {
+ /* kludge line */
+ line[llen] = '\0';
+ if (sscanf(line + 1, "TZUTC: %d", &dutc) == 1) {
+ tzoff = ((long)dutc * 60 * 60) / 100;
+#ifdef FIDOPKT_DEBUG
+ strlcpy(tz, line + 1 + 7, sizeof(tz));
+#endif
+ }
+ else if (strncmp(line + 1, "MSGID: ", 7) == 0)
+ strlcpy(ret->msgid, line + 1 + 7, sizeof(ret->msgid));
+ else if (strncmp(line + 1, "REPLY: ", 7) == 0)
+ strlcpy(ret->reply, line + 1 + 7, sizeof(ret->reply));
+ } else if (tear && strncmp(line, " * Origin: ", 11) == 0) {
+ line[llen] = '\0';
+ strlcpy(ret->origin, line + 11, sizeof(ret->origin));
+ } else if (!tear) {
+ if (line[0] == ' ' && line[1] == ' ')
+ continue;
+ if (message_len == 0 &&
+ (line[0] == '\n' || line[0] == '\r' || line[0] == '\0'))
+ continue;
+ if (line[llen - 1] == '\0') {
+ memcpy(ret->message + message_len, line, llen - 1);
+ message_len += llen - 1;
+ ret->message[message_len++] = '\r';
+ //ret->message[message_len++] = '\n';
+ } else {
+ /* long line */
+ memcpy(ret->message + message_len, line, llen);
+ message_len += llen;
+ }
+ }
+ }
+
+ while (message_len > 1) {
+ if (ret->message[message_len - 1] == '\n' &&
+ ret->message[message_len - 2] == '\r')
+ message_len -= 2;
+ else if (ret->message[message_len - 1] == '\r')
+ message_len -= 1;
+ else
+ break;
+ }
+ ret->message[message_len] = '\0';
+ ret->message_len = message_len;
+
+ ret->time = mktime(&tm);
+ if (ret->time == -1) {
+ logger_printf("[fidopkt] parsed date but mktime failed");
+ goto parse_fail;
+ }
+ ret->time -= tzoff;
+
+#ifdef FIDOPKT_DEBUG
+ logger_printf("[fidopkt] ID: %s", ret->msgid);
+ logger_printf("[fidopkt] Origin: %s", ret->origin);
+ logger_printf("[fidopkt] Reply To: %s", ret->reply);
+ logger_printf("[fidopkt] Area: %s", ret->area);
+ logger_printf("[fidopkt] From: %s", ret->from);
+ logger_printf("[fidopkt] To: %s", ret->to);
+ logger_printf("[fidopkt] Subject: %s", ret->subject);
+
+ llen = strlcpy(ct, ctime(&ret->time), sizeof(ct));
+ ct[llen - 1] = '\0';
+ logger_printf("[fidopkt] Date: %s UTC (%s %s%s)", ct, datetime,
+ (tzoff >= 0 ? "+" : ""), tz);
+ logger_printf("[fidopkt] Message:");
+
+ lastbreak = ret->message;
+ for (n = 0; n <= ret->message_len; n++) {
+ if (ret->message[n] == '\r' || n == ret->message_len) {
+ was = ret->message[n];
+ ret->message[n] = '\0';
+ logger_printf("[fidopkt] %s", lastbreak);
+ ret->message[n] = was;
+ lastbreak = ret->message + n + 1;
+ }
+ }
+#endif
+
+ return ret;
+
+parse_fail:
+ fidopkt_free(&ret);
+ return NULL;
+}
+
+void
+fidopkt_free(struct fidopkt **pktp)
+{
+ struct fidopkt *pkt = (struct fidopkt *)*pktp;
+
+ if (pkt->message)
+ xfree(&pkt->message);
+ xfree(pktp);
+}
--- fidopkt.h Wed Feb 22 15:54:38 2023
+++ fidopkt.h Wed Feb 22 15:54:38 2023
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2023 joshua stein <jcs@jcs.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef __FIDOPKT_H__
+#define __FIDOPKT_H__
+
+#include "util.h"
+
+struct fidopkt {
+ time_t time;
+ char to[36];
+ char from[36];
+ char subject[72];
+ char area[16];
+ char origin[72];
+ char msgid[32];
+ char reply[32];
+ char *message;
+ size_t message_len;
+};
+
+struct fidopkt * fidopkt_parse(char *packet_filename, char *buf,
+ size_t len);
+void fidopkt_free(struct fidopkt **pktp);
+
+#endif