#include <libpcapng/posa.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <ctype.h>
#ifdef _WIN32
# include <libpcapng/win_compat.h>
#else
# include <dirent.h>
#endif
#define MAX_PROTOS 512
static pcapng_posa_proto_t *g_protos[MAX_PROTOS];
static char *g_src[MAX_PROTOS];
static int g_nprotos;
static void src_set(int idx, const char *text)
{
if (idx < 0 || idx >= MAX_PROTOS) return;
free(g_src[idx]);
g_src[idx] = NULL;
if (text) {
size_t n = strlen(text) + 1;
g_src[idx] = malloc(n);
if (g_src[idx]) memcpy(g_src[idx], text, n);
}
}
const char *pcapng_posa_source(const char *name)
{
int i;
if (!name) return NULL;
for (i = 0; i < g_nprotos; i++)
if (g_protos[i] && !strcmp(g_protos[i]->name, name)) return g_src[i];
return NULL;
}
#define MAX_BINDS 256
typedef struct { int ipproto; uint16_t port; char proto[PCAPNG_POSA_NAME_MAX]; int used; } bind_t;
static bind_t g_binds[MAX_BINDS];
static int g_nbinds;
#define MAX_L3_BINDS 64
typedef struct { int key; char proto[PCAPNG_POSA_NAME_MAX]; int used; } l3bind_t;
static l3bind_t g_ipbinds[MAX_L3_BINDS]; static int g_nipbinds;
static l3bind_t g_ethbinds[MAX_L3_BINDS]; static int g_nethbinds;
#define MAX_CONTENT_BINDS 64
#define CONTENT_SIG_MAX 24
typedef struct {
uint8_t sig[CONTENT_SIG_MAX]; int siglen;
int offset;
int ipproto;
char proto[PCAPNG_POSA_NAME_MAX]; int used;
int weak;
} content_bind_t;
static content_bind_t g_cbinds[MAX_CONTENT_BINDS]; static int g_ncbinds;
#define MAX_IP4_CIDR_BINDS 64
typedef struct {
uint32_t addr;
uint32_t mask;
int match_src, match_dst;
char proto[PCAPNG_POSA_NAME_MAX]; int used;
} ip4cidr_bind_t;
static ip4cidr_bind_t g_ip4cidrs[MAX_IP4_CIDR_BINDS]; static int g_nip4cidrs;
#define MAX_ALIASES 64
#define ALIAS_MAX_TGTS 4
typedef struct {
char from[PCAPNG_POSA_NAME_MAX];
char to[ALIAS_MAX_TGTS][PCAPNG_POSA_NAME_MAX];
int nto;
char expr[192];
int is_macro, used;
} alias_t;
static alias_t g_aliases[MAX_ALIASES]; static int g_naliases;
static pcapng_posa_lookup_t *g_lookups[PCAPNG_POSA_MAX_LOOKUPS];
static int g_nlookups;
int pcapng_posa_lookup_count(void) { return g_nlookups; }
const pcapng_posa_lookup_t *pcapng_posa_find_lookup(const char *name)
{
int i;
if (!name || !*name) return NULL;
for (i = 0; i < g_nlookups; i++)
if (g_lookups[i] && !strcmp(g_lookups[i]->name, name)) return g_lookups[i];
return NULL;
}
static void parse_delim(const char *tok, char *out, int *nout);
#define MAX_COLORS 128
typedef struct {
char expr[PCAPNG_POSA_COLOR_EXPR_MAX];
char fg[PCAPNG_POSA_COLOR_NAME_MAX];
char bg[PCAPNG_POSA_COLOR_NAME_MAX];
} posa_color_t;
static posa_color_t g_colors[MAX_COLORS];
static int g_ncolors;
int pcapng_posa_count(void) { return g_nprotos; }
const pcapng_posa_proto_t *pcapng_posa_at(int i)
{ return (i >= 0 && i < g_nprotos) ? g_protos[i] : NULL; }
const pcapng_posa_proto_t *pcapng_posa_find(const char *name)
{
int i;
if (!name) return NULL;
for (i = 0; i < g_nprotos; i++)
if (g_protos[i] && !strcmp(g_protos[i]->name, name)) return g_protos[i];
return NULL;
}
void pcapng_posa_clear(void)
{
int i;
for (i = 0; i < g_nprotos; i++) {
free(g_src[i]); g_src[i] = NULL;
free(g_protos[i]); g_protos[i] = NULL;
}
g_nprotos = 0; g_nbinds = 0; g_ncolors = 0; g_nipbinds = 0; g_nethbinds = 0; g_ncbinds = 0;
g_naliases = 0; g_nip4cidrs = 0;
{ int i; for (i = 0; i < g_nlookups; i++) { free(g_lookups[i]); g_lookups[i] = NULL; } }
g_nlookups = 0;
}
int pcapng_posa_color_count(void) { return g_ncolors; }
int pcapng_posa_color_get(int i, const char **expr, const char **fg, const char **bg)
{
if (i < 0 || i >= g_ncolors) return -1;
if (expr) *expr = g_colors[i].expr;
if (fg) *fg = g_colors[i].fg;
if (bg) *bg = g_colors[i].bg;
return 0;
}
static void parse_color(const char *rest)
{
const char *arrow = NULL, *p;
char fg[PCAPNG_POSA_COLOR_NAME_MAX] = "", bg[PCAPNG_POSA_COLOR_NAME_MAX] = "";
size_t elen;
for (p = rest; (p = strstr(p, "=>")) != NULL; p += 2) arrow = p;
if (!arrow || g_ncolors >= MAX_COLORS) return;
if (sscanf(arrow + 2, " %23s %23s", fg, bg) != 2) return;
elen = (size_t)(arrow - rest);
while (elen > 0 && (rest[elen - 1] == ' ' || rest[elen - 1] == '\t')) elen--;
if (elen == 0 || elen >= PCAPNG_POSA_COLOR_EXPR_MAX) return;
memcpy(g_colors[g_ncolors].expr, rest, elen);
g_colors[g_ncolors].expr[elen] = '\0';
snprintf(g_colors[g_ncolors].fg, sizeof g_colors[g_ncolors].fg, "%s", fg);
snprintf(g_colors[g_ncolors].bg, sizeof g_colors[g_ncolors].bg, "%s", bg);
g_ncolors++;
}
static const char *l3_lookup(const l3bind_t *t, int n, int key)
{
int i;
for (i = 0; i < n; i++) if (t[i].used && t[i].key == key) return t[i].proto;
return NULL;
}
static void l3_add(l3bind_t *t, int *n, int key, const char *proto)
{
int i;
for (i = 0; i < *n; i++)
if (t[i].used && t[i].key == key) { snprintf(t[i].proto, sizeof t[i].proto, "%s", proto); return; }
if (*n < MAX_L3_BINDS) {
t[*n].used = 1; t[*n].key = key;
snprintf(t[*n].proto, sizeof t[*n].proto, "%s", proto);
(*n)++;
}
}
const char *pcapng_posa_bound_ipproto(int num)
{ return l3_lookup(g_ipbinds, g_nipbinds, num); }
const char *pcapng_posa_bound_ethertype(uint16_t type)
{ return l3_lookup(g_ethbinds, g_nethbinds, (int)type); }
const char *pcapng_posa_bound_port(int ipproto, uint16_t port)
{
int i;
for (i = 0; i < g_nbinds; i++)
if (g_binds[i].used && g_binds[i].ipproto == ipproto && g_binds[i].port == port)
return g_binds[i].proto;
return NULL;
}
static void bind_add(int ipproto, uint16_t port, const char *proto)
{
int i;
for (i = 0; i < g_nbinds; i++)
if (g_binds[i].used && g_binds[i].ipproto == ipproto && g_binds[i].port == port) {
snprintf(g_binds[i].proto, sizeof g_binds[i].proto, "%s", proto); return; }
if (g_nbinds < MAX_BINDS) {
g_binds[g_nbinds].used = 1; g_binds[g_nbinds].ipproto = ipproto; g_binds[g_nbinds].port = port;
snprintf(g_binds[g_nbinds].proto, sizeof g_binds[g_nbinds].proto, "%s", proto);
g_nbinds++;
}
}
static void content_add(int ipproto, int offset, const uint8_t *sig, int n, const char *proto, int weak)
{
if (n <= 0 || n > CONTENT_SIG_MAX || offset < 0 || g_ncbinds >= MAX_CONTENT_BINDS) return;
g_cbinds[g_ncbinds].used = 1; g_cbinds[g_ncbinds].ipproto = ipproto;
g_cbinds[g_ncbinds].offset = offset;
g_cbinds[g_ncbinds].siglen = n; memcpy(g_cbinds[g_ncbinds].sig, sig, (size_t)n);
g_cbinds[g_ncbinds].weak = weak;
snprintf(g_cbinds[g_ncbinds].proto, sizeof g_cbinds[g_ncbinds].proto, "%s", proto);
g_ncbinds++;
}
static int g_weak_enabled = 1;
void pcapng_posa_weak_rules_enable(int on) { g_weak_enabled = on ? 1 : 0; }
int pcapng_posa_weak_rules_enabled(void) { return g_weak_enabled; }
static const char *content_match(int ipproto, const uint8_t *data, int len, int want_weak)
{
int i;
if (!data || len <= 0) return NULL;
if (want_weak && !g_weak_enabled) return NULL;
for (i = 0; i < g_ncbinds; i++) {
const content_bind_t *b = &g_cbinds[i];
if (!b->used || (b->ipproto && b->ipproto != ipproto)) continue;
if (!b->weak != !want_weak) continue;
if (b->offset + b->siglen <= len &&
memcmp(data + b->offset, b->sig, (size_t)b->siglen) == 0) return b->proto;
}
return NULL;
}
const char *pcapng_posa_bound_content(int ipproto, const uint8_t *data, int len)
{ return content_match(ipproto, data, len, 0); }
const char *pcapng_posa_bound_content_weak(int ipproto, const uint8_t *data, int len)
{ return content_match(ipproto, data, len, 1); }
static void ip4cidr_add(uint32_t addr, uint32_t mask, int msrc, int mdst, const char *proto)
{
if (g_nip4cidrs >= MAX_IP4_CIDR_BINDS) return;
g_ip4cidrs[g_nip4cidrs].addr = addr;
g_ip4cidrs[g_nip4cidrs].mask = mask;
g_ip4cidrs[g_nip4cidrs].match_src = msrc;
g_ip4cidrs[g_nip4cidrs].match_dst = mdst;
snprintf(g_ip4cidrs[g_nip4cidrs].proto, sizeof g_ip4cidrs[g_nip4cidrs].proto, "%s", proto);
g_ip4cidrs[g_nip4cidrs].used = 1;
g_nip4cidrs++;
}
static int parse_cidr4(const char *s, uint32_t *addr_out, uint32_t *mask_out)
{
unsigned a, b, c, d, pfx;
if (sscanf(s, "%u.%u.%u.%u/%u", &a, &b, &c, &d, &pfx) != 5) return 0;
if (a > 255 || b > 255 || c > 255 || d > 255 || pfx > 32) return 0;
*addr_out = (a << 24) | (b << 16) | (c << 8) | d;
*mask_out = pfx ? (~0u << (32 - pfx)) : 0u;
return 1;
}
const char *pcapng_posa_bound_ip4cidr(const uint8_t *src, const uint8_t *dst)
{
int i;
uint32_t s4 = 0, d4 = 0;
if (src) s4 = ((uint32_t)src[0]<<24)|((uint32_t)src[1]<<16)|((uint32_t)src[2]<<8)|src[3];
if (dst) d4 = ((uint32_t)dst[0]<<24)|((uint32_t)dst[1]<<16)|((uint32_t)dst[2]<<8)|dst[3];
for (i = 0; i < g_nip4cidrs; i++) {
const ip4cidr_bind_t *b = &g_ip4cidrs[i];
if (!b->used) continue;
if (b->match_src && src && (s4 & b->mask) == (b->addr & b->mask)) return b->proto;
if (b->match_dst && dst && (d4 & b->mask) == (b->addr & b->mask)) return b->proto;
}
return NULL;
}
static pcapng_field_t *pf_add(pcapng_field_t *parent, const char *abbrev, pcapng_ftype_t vt)
{
pcapng_field_t *f = calloc(1, sizeof *f);
if (!f) return NULL;
snprintf(f->abbrev, sizeof f->abbrev, "%s", abbrev ? abbrev : "");
f->vtype = vt; f->parent = parent;
if (parent) {
if (parent->last_child) parent->last_child->next = f; else parent->children = f;
parent->last_child = f;
}
return f;
}
static void pf_remove_child(pcapng_field_t *parent, pcapng_field_t *child)
{
pcapng_field_t *c, *prev = NULL;
if (!parent || !child) return;
for (c = parent->children; c; prev = c, c = c->next) {
if (c != child) continue;
if (prev) prev->next = c->next; else parent->children = c->next;
if (parent->last_child == c) parent->last_child = prev;
c->next = NULL;
pcapng_field_free(c);
return;
}
}
static void pf_label(pcapng_field_t *f, const char *fmt, ...)
{ va_list ap; if (!f) return; va_start(ap, fmt); vsnprintf(f->label, sizeof f->label, fmt, ap); va_end(ap); }
static void pf_uint(pcapng_field_t *f, uint64_t v) { if (f) { f->vtype = PCAPNG_FT_UINT; f->u = v; } }
static void pf_str(pcapng_field_t *f, const char *s)
{ if (f) { f->vtype = PCAPNG_FT_STR; snprintf(f->str, sizeof f->str, "%s", s ? s : ""); } }
static void pf_ipv4(pcapng_field_t *f, const uint8_t ip[4])
{ if (!f) return; f->vtype = PCAPNG_FT_IPV4; memcpy(f->bytes, ip, 4); f->blen = 4;
snprintf(f->str, sizeof f->str, "%u.%u.%u.%u", ip[0], ip[1], ip[2], ip[3]); }
static void pf_ipv6(pcapng_field_t *f, const uint8_t ip[16])
{
static const char hx[] = "0123456789abcdef";
char *p; int i;
if (!f) return;
f->vtype = PCAPNG_FT_IPV6; memcpy(f->bytes, ip, 16); f->blen = 16;
for (p = f->str, i = 0; i < 16; i += 2) {
if (i) *p++ = ':';
*p++ = hx[(ip[i] >> 4) & 0xf]; *p++ = hx[ip[i] & 0xf];
*p++ = hx[(ip[i+1] >> 4) & 0xf]; *p++ = hx[ip[i+1] & 0xf];
}
*p = '\0';
}
static void pf_mac(pcapng_field_t *f, const uint8_t m[6])
{ if (!f) return; f->vtype = PCAPNG_FT_MAC; memcpy(f->bytes, m, 6); f->blen = 6;
snprintf(f->str, sizeof f->str, "%02x:%02x:%02x:%02x:%02x:%02x", m[0],m[1],m[2],m[3],m[4],m[5]); }
static void pf_bytes(pcapng_field_t *f, const uint8_t *b, int n)
{
int k;
if (!f) return;
f->vtype = PCAPNG_FT_BYTES;
k = n < PCAPNG_FIELD_BYTES_MAX ? n : PCAPNG_FIELD_BYTES_MAX;
if (k > 0) memcpy(f->bytes, b, (size_t)k);
f->blen = k;
}
static void pf_range(pcapng_field_t *f, int off, int len) { if (f) { f->off = off; f->len = len; } }
static uint64_t parse_num(const char *s)
{
if (!s) return 0;
while (*s == ' ' || *s == '\t') s++;
if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) return (uint64_t)strtoull(s, NULL, 16);
return (uint64_t)strtoull(s, NULL, 10);
}
static int all_digits(const char *s, int len)
{
int i = 0, n = 0;
while (i < len && (s[i] == ' ' || s[i] == '\t')) i++;
while (len > i && (s[len - 1] == ' ' || s[len - 1] == '\t')) len--;
if (i >= len) return 0;
if (len - i > 2 && s[i] == '0' && (s[i + 1] == 'x' || s[i + 1] == 'X')) {
for (i += 2; i < len; i++, n++) if (!isxdigit((unsigned char)s[i])) return 0;
return n > 0;
}
for (; i < len; i++, n++) if (!isdigit((unsigned char)s[i])) return 0;
return n > 0;
}
static void rest_after_kw(const char *raw, const char *kw, char *out, size_t osz)
{
const char *p = strstr(raw, kw);
size_t n;
out[0] = '\0';
if (!p) return;
p += strlen(kw);
while (*p == ' ' || *p == '\t') p++;
n = strlen(p);
while (n > 0 && (p[n-1] == ' ' || p[n-1] == '\t' || p[n-1] == '\r')) n--;
if (n >= osz) n = osz - 1;
memcpy(out, p, n); out[n] = '\0';
}
static int parse_type(const char *tok, pcapng_posa_fld_t *f)
{
if (!strcmp(tok, "uint8")) f->type = PCAPNG_POSA_U8;
else if (!strcmp(tok, "uint16")) f->type = PCAPNG_POSA_U16;
else if (!strcmp(tok, "uint24")) f->type = PCAPNG_POSA_U24;
else if (!strcmp(tok, "uint32")) f->type = PCAPNG_POSA_U32;
else if (!strcmp(tok, "uint64")) f->type = PCAPNG_POSA_U64;
else if (!strcmp(tok, "le_uint16")) f->type = PCAPNG_POSA_LE16;
else if (!strcmp(tok, "le_uint32")) f->type = PCAPNG_POSA_LE32;
else if (!strcmp(tok, "le_uint64")) f->type = PCAPNG_POSA_LE64;
else if (!strcmp(tok, "mac")) f->type = PCAPNG_POSA_MAC;
else if (!strcmp(tok, "ip4")) f->type = PCAPNG_POSA_IP4;
else if (!strcmp(tok, "ip6")) f->type = PCAPNG_POSA_IP6;
else if (!strcmp(tok, "cstring")) f->type = PCAPNG_POSA_CSTRING;
else if (!strcmp(tok, "string")) f->type = PCAPNG_POSA_CSTRING;
else if (!strcmp(tok, "payload")) f->type = PCAPNG_POSA_PAYLOAD;
else if (!strcmp(tok, "dnsname")) f->type = PCAPNG_POSA_DNSNAME;
else if (!strcmp(tok, "uuid")) f->type = PCAPNG_POSA_UUID;
else if (!strcmp(tok, "guid")) f->type = PCAPNG_POSA_UUID;
else if (!strcmp(tok, "quic_varint")) f->type = PCAPNG_POSA_QUIC_VARINT;
else if (!strcmp(tok, "leb128")) f->type = PCAPNG_POSA_LEB128;
else if (!strncmp(tok, "bytes<", 6)) { f->type = PCAPNG_POSA_BYTES_FIXED; f->nbytes = (size_t)parse_num(tok + 6); }
else if (!strncmp(tok, "str<", 4)) { f->type = PCAPNG_POSA_STR_FIXED; f->nbytes = (size_t)parse_num(tok + 4); }
else if (!strncmp(tok, "bytes[", 6)) {
const char *e = strchr(tok + 6, ']');
f->type = PCAPNG_POSA_BYTES_REF;
snprintf(f->lenfield, sizeof f->lenfield, "%.*s", e ? (int)(e - (tok + 6)) : 0, tok + 6);
} else if (!strncmp(tok, "str[", 4)) {
const char *e = strchr(tok + 4, ']');
f->type = PCAPNG_POSA_STR_REF;
snprintf(f->lenfield, sizeof f->lenfield, "%.*s", e ? (int)(e - (tok + 4)) : 0, tok + 4);
} else if (!strncmp(tok, "utf16[", 6)) {
const char *e = strchr(tok + 6, ']');
f->type = PCAPNG_POSA_UTF16;
snprintf(f->lenfield, sizeof f->lenfield, "%.*s", e ? (int)(e - (tok + 6)) : 0, tok + 6);
} else return -1;
return 0;
}
static int is_type_tok(const char *t) { pcapng_posa_fld_t tmp; return parse_type(t, &tmp) == 0; }
static int is_kw(const char *t)
{
static const char *kw[] = { "required","optional","when","scope","repeat","label","bits",
"layer","include","seek","info","col","abbrev","rule","color",
"protocol","Object","end","kvblock","Lookup","lookup","let","bind","recall", NULL };
int i;
if (!strncmp(t, "Object<", 7)) return 1;
for (i = 0; kw[i]; i++) if (!strcmp(t, kw[i])) return 1;
return is_type_tok(t);
}
static int tokenize(char *line, char *toks[], int max)
{
int n = 0; char *p = line;
while (*p && n < max) {
while (*p == ' ' || *p == '\t') p++;
if (!*p) break;
toks[n++] = p;
while (*p && *p != ' ' && *p != '\t') p++;
if (*p) *p++ = '\0';
}
return n;
}
static void parse_rule(const char *rest, int weak)
{
char t[16] = "", field[24] = "", num[24] = "";
const char *arrow;
char proto[PCAPNG_POSA_NAME_MAX] = "";
while (*rest == ' ' || *rest == '\t') rest++;
if (!strncmp(rest, "weak", 4) && (rest[4] == ' ' || rest[4] == '\t')) {
weak = 1; rest += 4;
while (*rest == ' ' || *rest == '\t') rest++;
}
{ const char *cw = strstr(rest, "content");
if (cw) {
const char *q = strchr(cw, '"');
int ipproto = 0, offset = 0;
if (!strncmp(rest, "tcp", 3)) ipproto = 6;
else if (!strncmp(rest, "udp", 3)) ipproto = 17;
if (cw[7] == '@') offset = (int)parse_num(cw + 8);
arrow = strstr(rest, "=>");
if (q && arrow && sscanf(arrow + 2, " %63s", proto) == 1) {
char sig[PCAPNG_POSA_DELIM_MAX]; int n = 0;
parse_delim(q, sig, &n);
content_add(ipproto, offset, (const uint8_t *)sig, n, proto, weak);
}
return;
}
}
{ char tobj[16] = "", tfield[16] = "", cidr[32] = "";
if (sscanf(rest, "%15[a-z0-9].%15[a-z] in %31s", tobj, tfield, cidr) == 3 &&
!strcmp(tobj, "ip4")) {
uint32_t addr = 0, mask = 0;
arrow = strstr(rest, "=>");
if (parse_cidr4(cidr, &addr, &mask) && arrow &&
sscanf(arrow + 2, " %63s", proto) == 1) {
int msrc = (!strcmp(tfield, "src") || !strcmp(tfield, "addr")) ? 1 : 0;
int mdst = (!strcmp(tfield, "dst") || !strcmp(tfield, "addr")) ? 1 : 0;
ip4cidr_add(addr, mask, msrc, mdst, proto);
}
return;
}
}
if (sscanf(rest, "%15[a-z].%23[a-z] == %23s", t, field, num) != 3) return;
arrow = strstr(rest, "=>");
if (!arrow || sscanf(arrow + 2, " %63s", proto) != 1) return;
{ unsigned long v = (unsigned long)parse_num(num);
if (!strcmp(t, "tcp")) bind_add(6, (uint16_t)v, proto);
else if (!strcmp(t, "udp")) bind_add(17, (uint16_t)v, proto);
else if (!strcmp(t, "ip") && !strcmp(field, "proto")) l3_add(g_ipbinds, &g_nipbinds, (int)v, proto);
else if (!strcmp(t, "eth") && !strcmp(field, "type")) l3_add(g_ethbinds, &g_nethbinds, (int)v, proto);
}
}
static int alias_plain_field(const char *t)
{
static const char *const kw[] = { "and","or","not","in","eq","ne","gt","lt",
"ge","le","contains","matches",NULL };
int i;
if (!*t) return 0;
for (i = 0; t[i]; i++) {
char c = t[i];
if (!(isalnum((unsigned char)c) || c=='.' || c=='_' || c==':' || c=='-' || c=='/'))
return 0;
}
for (i = 0; kw[i]; i++) if (!strcmp(t, kw[i])) return 0;
return 1;
}
static void parse_alias(const char *rest)
{
const char *arrow = strstr(rest, "=>"), *p;
char from[PCAPNG_POSA_NAME_MAX] = "";
alias_t *a;
int macro = 0, ntok = 0;
char tgts[ALIAS_MAX_TGTS][PCAPNG_POSA_NAME_MAX];
if (!arrow || g_naliases >= MAX_ALIASES) return;
if (sscanf(rest, " %63s", from) != 1 || !from[0] || !strcmp(from, "=>")) return;
for (p = arrow + 2; ; ) {
char tok[PCAPNG_POSA_NAME_MAX] = "";
while (*p == ' ' || *p == '\t' || *p == ',') p++;
if (!*p) break;
if (sscanf(p, "%63[^ \t,]", tok) != 1 || !tok[0]) break;
p += strlen(tok);
if (!alias_plain_field(tok) || ntok >= ALIAS_MAX_TGTS) { macro = 1; break; }
snprintf(tgts[ntok++], PCAPNG_POSA_NAME_MAX, "%s", tok);
}
if (ntok == 0) macro = 1;
a = &g_aliases[g_naliases];
memset(a, 0, sizeof *a);
snprintf(a->from, sizeof a->from, "%s", from);
if (macro) {
const char *e = arrow + 2;
while (*e == ' ' || *e == '\t') e++;
if (!*e) return;
snprintf(a->expr, sizeof a->expr, "%s", e);
a->is_macro = 1;
} else {
int j;
for (j = 0; j < ntok; j++) snprintf(a->to[j], PCAPNG_POSA_NAME_MAX, "%s", tgts[j]);
a->nto = ntok;
}
a->used = 1;
g_naliases++;
}
int pcapng_posa_alias_expand(const char *field, const char **out, int max)
{
int i, j, n;
if (!field) return 0;
for (i = 0; i < g_naliases; i++) {
if (!g_aliases[i].used || g_aliases[i].is_macro ||
strcmp(g_aliases[i].from, field) != 0) continue;
n = g_aliases[i].nto < max ? g_aliases[i].nto : max;
for (j = 0; j < n; j++) out[j] = g_aliases[i].to[j];
return n;
}
return 0;
}
const char *pcapng_posa_alias_macro(const char *name)
{
int i;
if (!name) return NULL;
for (i = 0; i < g_naliases; i++)
if (g_aliases[i].used && g_aliases[i].is_macro && !strcmp(g_aliases[i].from, name))
return g_aliases[i].expr;
return NULL;
}
static pcapng_posa_fld_t *add_fld(pcapng_posa_proto_t *cur, pcapng_posa_ftype_t t)
{
pcapng_posa_fld_t *f;
if (cur->nflds >= PCAPNG_POSA_MAX_FLDS) return NULL;
f = &cur->flds[cur->nflds++];
memset(f, 0, sizeof *f);
f->type = t; f->scope_len_field = -1;
return f;
}
static int hexdig(int ch)
{
if (ch >= '0' && ch <= '9') return ch - '0';
if (ch >= 'a' && ch <= 'f') return ch - 'a' + 10;
if (ch >= 'A' && ch <= 'F') return ch - 'A' + 10;
return -1;
}
static void parse_delim(const char *tok, char *out, int *nout)
{
int n = 0; const char *p = tok;
if (*p == '"') p++;
while (*p && *p != '"' && n < PCAPNG_POSA_DELIM_MAX) {
if (*p == '\\' && p[1]) {
p++;
if (*p == 'x' && hexdig((unsigned char)p[1]) >= 0 && hexdig((unsigned char)p[2]) >= 0) {
out[n++] = (char)((hexdig((unsigned char)p[1]) << 4) | hexdig((unsigned char)p[2]));
p += 2;
} else switch (*p) {
case 'r': out[n++]='\r'; break; case 'n': out[n++]='\n'; break;
case 't': out[n++]='\t'; break; case '0': out[n++]='\0'; break;
default: out[n++]=*p;
}
p++;
} else out[n++] = *p++;
}
*nout = n;
}
static int quoted(const char *s, char *out, size_t sz)
{
const char *q1 = strchr(s, '"'), *q2 = q1 ? strchr(q1 + 1, '"') : NULL;
if (!q1 || !q2) return 0;
snprintf(out, sz, "%.*s", (int)(q2 - q1 - 1), q1 + 1);
return 1;
}
static int args_after_quote(const char *s, char args[][PCAPNG_POSA_NAME_MAX], int maxa)
{
const char *q1 = strchr(s, '"'), *q2 = q1 ? strchr(q1 + 1, '"') : NULL;
const char *a; int na = 0;
if (!q2) return 0;
for (a = q2 + 1; *a && na < maxa; ) {
const char *comma; int L;
while (*a == ' ' || *a == '\t' || *a == ',') a++;
if (!*a) break;
comma = strchr(a, ',');
L = comma ? (int)(comma - a) : (int)strlen(a);
while (L > 0 && (a[L - 1] == ' ' || a[L - 1] == '\t')) L--;
snprintf(args[na++], PCAPNG_POSA_NAME_MAX, "%.*s", L, a);
if (!comma) break;
a = comma + 1;
}
return na;
}
static pcapng_posa_cmp_t parse_op(const char *op)
{
if (!strcmp(op, "==")) return PCAPNG_POSA_CMP_EQ;
else if (!strcmp(op, "!=")) return PCAPNG_POSA_CMP_NE;
else if (!strcmp(op, "<")) return PCAPNG_POSA_CMP_LT;
else if (!strcmp(op, ">")) return PCAPNG_POSA_CMP_GT;
else if (!strcmp(op, ">=")) return PCAPNG_POSA_CMP_GE;
else if (!strcmp(op, "<=")) return PCAPNG_POSA_CMP_LE;
return PCAPNG_POSA_CMP_NONE;
}
static void guard_from_toks(char **toks, int start, int nt, pcapng_posa_guard_t *g)
{
int i = start;
memset(g, 0, sizeof *g);
if (i >= nt) return;
snprintf(g->lhs, sizeof g->lhs, "%s", toks[i]); i++;
if (i < nt && !strcmp(toks[i], "&")) { i++; if (i < nt) { g->mask = parse_num(toks[i]); i++; } }
if (i < nt) {
pcapng_posa_cmp_t op = parse_op(toks[i]);
if (op == PCAPNG_POSA_CMP_NONE) { g->op = PCAPNG_POSA_CMP_NE; g->rhs = 0; return; }
g->op = op; i++;
if (i < nt) { g->rhs = parse_num(toks[i]); i++; }
} else { g->op = PCAPNG_POSA_CMP_NE; g->rhs = 0; return; }
if (i < nt && (!strcmp(toks[i], "and") || !strcmp(toks[i], "or"))) {
g->logic2 = !strcmp(toks[i], "or") ? 1 : 0;
i++;
if (i < nt) { snprintf(g->lhs2, sizeof g->lhs2, "%s", toks[i]); i++; }
if (i < nt && !strcmp(toks[i], "&")) { i++; if (i < nt) { g->mask2 = parse_num(toks[i]); i++; } }
if (i < nt) {
pcapng_posa_cmp_t op2 = parse_op(toks[i]);
if (op2 != PCAPNG_POSA_CMP_NONE) {
g->op2 = op2; i++;
if (i < nt) g->rhs2 = parse_num(toks[i]);
} else if (g->lhs2[0]) {
g->op2 = PCAPNG_POSA_CMP_NE; g->rhs2 = 0;
}
}
}
}
static int parse_src(const char *src, char *errbuf, size_t errlen)
{
const char *line = src;
pcapng_posa_proto_t *cur = NULL;
pcapng_posa_lookup_t *cur_lookup = NULL;
pcapng_posa_fld_t *lastfld = NULL;
int added = 0, lineno = 0;
int blk_indent[32], nblk = 0;
while (*line) {
char buf[1024], raw[1024], *toks[32], *hash, *tl;
const char *eol = strchr(line, '\n');
size_t llen = eol ? (size_t)(eol - line) : strlen(line);
int nt, indent, ti, structural;
lineno++;
if (llen >= sizeof buf) llen = sizeof buf - 1;
memcpy(buf, line, llen); buf[llen] = '\0';
line = eol ? eol + 1 : line + strlen(line);
hash = strchr(buf, '#'); if (hash) *hash = '\0';
indent = 0; while (buf[indent] == ' ' || buf[indent] == '\t') indent++;
tl = buf + indent;
if (!*tl) continue;
snprintf(raw, sizeof raw, "%s", tl);
{ const char *rl = tl; int wk = 0;
if (!strncmp(rl, "weak", 4) && (rl[4] == ' ' || rl[4] == '\t')) {
const char *r2 = rl + 4; while (*r2 == ' ' || *r2 == '\t') r2++;
if (!strncmp(r2, "rule", 4) && (r2[4] == ' ' || r2[4] == '\t')) { wk = 1; rl = r2; }
}
if (!strncmp(rl, "rule", 4) && (rl[4] == ' ' || rl[4] == '\t')) {
const char *r = rl + 4; while (*r == ' ' || *r == '\t') r++;
parse_rule(r, wk);
continue;
} }
if (!strncmp(tl, "color", 5) && (tl[5] == ' ' || tl[5] == '\t')) {
char *r = tl + 5; while (*r == ' ' || *r == '\t') r++;
parse_color(r);
continue;
}
if (!strncmp(tl, "alias", 5) && (tl[5] == ' ' || tl[5] == '\t')) {
char *r = tl + 5; while (*r == ' ' || *r == '\t') r++;
parse_alias(r);
continue;
}
if (cur && !strncmp(tl, "col", 3) && (tl[3] == ' ' || tl[3] == '"' || tl[3] == '\t')) {
char *q1 = strchr(tl, '"'), *q2 = q1 ? strchr(q1 + 1, '"') : NULL;
if (q1 && q2) snprintf(cur->display, sizeof cur->display, "%.*s", (int)(q2 - q1 - 1), q1 + 1);
continue;
}
if (cur && !strncmp(tl, "abbrev", 6) && (tl[6] == ' ' || tl[6] == '"' || tl[6] == '\t')) {
char *q1 = strchr(tl, '"'), *q2 = q1 ? strchr(q1 + 1, '"') : NULL;
if (q1 && q2) snprintf(cur->abbrev, sizeof cur->abbrev, "%.*s", (int)(q2 - q1 - 1), q1 + 1);
continue;
}
if (cur && !strncmp(tl, "starts", 6) && (tl[6] == ' ' || tl[6] == '"' || tl[6] == '\t')) {
const char *p = tl + 6;
while (cur->nprefix < 8) {
int n; char raw_delim[PCAPNG_POSA_DELIM_MAX];
p = strchr(p, '"');
if (!p) break;
parse_delim(p, raw_delim, &n);
if (n > 0 && n < (int)sizeof cur->prefixes[0]) {
memcpy(cur->prefixes[cur->nprefix], raw_delim, n);
cur->prefix_len[cur->nprefix] = n;
cur->nprefix++;
}
p++;
p = strchr(p, '"');
if (!p) break;
p++;
}
continue;
}
if (cur && !strncmp(tl, "info", 4) && (tl[4] == ' ' || tl[4] == '"' || tl[4] == '\t')) {
if (quoted(tl, cur->info_fmt, sizeof cur->info_fmt))
cur->info_nargs = args_after_quote(tl, cur->info_args, 8);
continue;
}
nt = tokenize(buf, toks, 32);
if (nt == 0) continue;
if (!strcmp(toks[0], "Lookup") && nt >= 2) {
pcapng_posa_lookup_t *lk = NULL;
int k;
cur = NULL; cur_lookup = NULL; lastfld = NULL;
for (k = 0; k < g_nlookups; k++)
if (g_lookups[k] && !strcmp(g_lookups[k]->name, toks[1])) { lk = g_lookups[k]; break; }
if (!lk) {
if (g_nlookups >= PCAPNG_POSA_MAX_LOOKUPS) {
if (errbuf) snprintf(errbuf, errlen, "line %d: too many Lookup tables", lineno);
return -1;
}
lk = calloc(1, sizeof *lk);
if (!lk) { if (errbuf) snprintf(errbuf, errlen, "out of memory"); return -1; }
snprintf(lk->name, sizeof lk->name, "%s", toks[1]);
g_lookups[g_nlookups++] = lk;
}
cur_lookup = lk;
continue;
}
if (!strncmp(toks[0], "Object<", 7) || !strcmp(toks[0], "protocol") || !strcmp(toks[0], "Object")) {
char parent[PCAPNG_POSA_NAME_MAX] = "main";
const char *name = NULL;
while (cur && nblk > 0) { add_fld(cur, PCAPNG_POSA_END); nblk--; }
nblk = 0; lastfld = NULL; cur_lookup = NULL;
if (!strcmp(toks[0], "protocol")) { name = nt > 1 ? toks[1] : NULL; parent[0] = '\0'; }
else {
char *gt = strchr(toks[0], '>'); const char *pp = toks[0] + 7;
if (gt) snprintf(parent, sizeof parent, "%.*s", (int)(gt - pp), pp);
if (!strcmp(parent, "main")) parent[0] = '\0';
name = nt > 1 ? toks[1] : NULL;
}
if (!name) { if (errbuf) snprintf(errbuf, errlen, "line %d: missing protocol name", lineno); return -1; }
{ int idx = -1, k;
for (k = 0; k < g_nprotos; k++)
if (g_protos[k] && !strcmp(g_protos[k]->name, name)) { idx = k; break; }
if (idx < 0) {
if (g_nprotos >= MAX_PROTOS) { if (errbuf) snprintf(errbuf, errlen, "too many protocols"); return -1; }
idx = g_nprotos++;
g_protos[idx] = calloc(1, sizeof *g_protos[idx]);
if (!g_protos[idx]) { g_nprotos--; if (errbuf) snprintf(errbuf, errlen, "out of memory"); return -1; }
}
cur = g_protos[idx];
memset(cur, 0, sizeof *cur);
snprintf(cur->name, sizeof cur->name, "%s", name);
snprintf(cur->parent, sizeof cur->parent, "%s", parent);
{ int k; for (k = 2; k < nt; k++) if (!strcmp(toks[k], "default")) cur->is_default = 1; }
src_set(idx, src); }
added++;
continue;
}
if (!strcmp(toks[0], "end")) { cur = NULL; cur_lookup = NULL; nblk = 0; lastfld = NULL; continue; }
if (!cur && !cur_lookup) continue;
if ((lastfld || cur_lookup) && !is_kw(toks[0]) && strchr(raw, '=')) {
const char *eq = strchr(raw, '=');
int L = (int)(eq - raw);
while (L > 0 && (raw[L - 1] == ' ' || raw[L - 1] == '\t')) L--;
if (L > 0) {
pcapng_posa_enum_t *e = NULL;
if (lastfld && lastfld->nenums < PCAPNG_POSA_MAX_ENUMS)
e = &lastfld->enums[lastfld->nenums++];
else if (!lastfld && cur_lookup && cur_lookup->nenums < PCAPNG_POSA_LOOKUP_MAX_ENUMS)
e = &cur_lookup->enums[cur_lookup->nenums++];
if (e) {
if (raw[0] == '"') {
const char *q1 = raw + 1, *q2 = strchr(q1, '"');
int klen = q2 ? (int)(q2 - q1) : L - 1;
snprintf(e->key, sizeof e->key, "%.*s", klen, q1);
const char *rhs = eq + 1;
while (*rhs == ' ' || *rhs == '\t') rhs++;
if (*rhs == '"') {
const char *v1 = rhs + 1, *v2 = strchr(v1, '"');
int vlen = v2 ? (int)(v2 - v1) : (int)strlen(v1);
snprintf(e->name, sizeof e->name, "%.*s", vlen, v1);
} else {
snprintf(e->name, sizeof e->name, "%.*s", L - (int)(q1 - raw), q1);
}
e->val = 0;
} else {
const char *rhs = eq + 1;
while (*rhs == ' ' || *rhs == '\t') rhs++;
if (all_digits(raw, L) && !all_digits(rhs, (int)strlen(rhs))) {
int vlen;
const char *v1;
e->val = parse_num(raw);
if (*rhs == '"') {
const char *v2;
v1 = rhs + 1; v2 = strchr(v1, '"');
vlen = v2 ? (int)(v2 - v1) : (int)strlen(v1);
} else {
v1 = rhs; vlen = (int)strlen(rhs);
while (vlen > 0 && (v1[vlen - 1] == ' ' || v1[vlen - 1] == '\t')) vlen--;
}
snprintf(e->name, sizeof e->name, "%.*s", vlen, v1);
} else {
snprintf(e->name, sizeof e->name, "%.*s", L, raw);
e->val = parse_num(rhs);
}
}
}
}
continue;
}
if (!cur) continue;
structural = 1;
if (structural) while (nblk > 0 && blk_indent[nblk - 1] >= indent) { add_fld(cur, PCAPNG_POSA_END); nblk--; }
if (!strcmp(toks[0], "let") && nt >= 2) {
pcapng_posa_fld_t *f = add_fld(cur, PCAPNG_POSA_LET);
if (f) {
const char *eq = strchr(raw, '=');
snprintf(f->name, sizeof f->name, "%s", toks[1]);
if (eq) {
const char *e = eq + 1, *q;
size_t n;
while (*e == ' ' || *e == '\t') e++;
q = strchr(e, '"');
n = q ? (size_t)(q - e) : strlen(e);
while (n > 0 && (e[n-1] == ' ' || e[n-1] == '\t' || e[n-1] == '\r')) n--;
if (n >= sizeof f->expr) n = sizeof f->expr - 1;
memcpy(f->expr, e, n); f->expr[n] = '\0';
}
quoted(raw, f->disp, sizeof f->disp);
}
lastfld = f;
continue;
}
if ((!strcmp(toks[0], "bind") || !strcmp(toks[0], "recall")) && nt >= 2) {
int is_bind = !strcmp(toks[0], "bind");
pcapng_posa_fld_t *f = add_fld(cur, is_bind ? PCAPNG_POSA_BIND : PCAPNG_POSA_RECALL);
if (f) {
const char *lb = strchr(raw, '[');
const char *rb = lb ? strchr(lb, ']') : NULL;
const char *tb = strstr(raw, toks[0]) + strlen(toks[0]);
while (*tb == ' ' || *tb == '\t') tb++;
{ int k = 0;
while (tb < (lb ? lb : tb) && *tb && *tb != '[' && k < (int)sizeof f->sub - 1)
f->sub[k++] = *tb++;
f->sub[k] = '\0'; }
if (lb && rb && rb > lb + 1) {
size_t n = (size_t)(rb - lb - 1);
if (n >= sizeof f->lenfield) n = sizeof f->lenfield - 1;
memcpy(f->lenfield, lb + 1, n); f->lenfield[n] = '\0';
}
if (is_bind) {
const char *eq = strchr(raw, '=');
if (eq) {
int k = 0;
eq++;
while (*eq == ' ' || *eq == '\t') eq++;
while (*eq && *eq != ' ' && *eq != '\t' && k < (int)sizeof f->src - 1)
f->src[k++] = *eq++;
f->src[k] = '\0';
}
} else {
int k;
for (k = 1; k + 1 < nt; k++)
if (!strcmp(toks[k], "as")) {
snprintf(f->name, sizeof f->name, "%s", toks[k + 1]); break; }
if (!f->name[0]) snprintf(f->name, sizeof f->name, "%s", f->sub);
for (k = 1; k + 1 < nt; k++)
if (!strcmp(toks[k], "lookup")) {
snprintf(f->lookup_name, sizeof f->lookup_name, "%s", toks[k + 1]); break; }
quoted(raw, f->disp, sizeof f->disp);
}
}
lastfld = NULL;
continue;
}
if (!strcmp(toks[0], "scope") && nt >= 2) {
pcapng_posa_fld_t *f = add_fld(cur, PCAPNG_POSA_SCOPE);
if (f) rest_after_kw(raw, "scope", f->lenfield, sizeof f->lenfield);
if (nblk < 32) blk_indent[nblk++] = indent;
lastfld = NULL;
continue;
}
if (!strcmp(toks[0], "when") && nt >= 2) {
pcapng_posa_fld_t *f = add_fld(cur, PCAPNG_POSA_WHEN);
char *last = toks[nt - 1]; int L = (int)strlen(last);
if (L > 0 && last[L - 1] == ':') last[L - 1] = '\0';
if (f) guard_from_toks(toks, 1, nt, &f->guard);
if (nblk < 32) blk_indent[nblk++] = indent;
lastfld = NULL;
continue;
}
if (!strcmp(toks[0], "layer") && nt >= 3) {
pcapng_posa_fld_t *f = add_fld(cur, PCAPNG_POSA_LAYER);
if (f) { snprintf(f->name, sizeof f->name, "%s", toks[1]);
snprintf(f->sub, sizeof f->sub, "%s", toks[2]); }
lastfld = NULL;
continue;
}
if (!strcmp(toks[0], "include") && nt >= 2) {
const pcapng_posa_proto_t *tpl = pcapng_posa_find(toks[1]);
if (!tpl) {
if (errbuf) snprintf(errbuf, errlen, "line %d: include: no such object '%s'", lineno, toks[1]);
return -1;
}
{ int k;
for (k = 0; k < tpl->nflds && cur->nflds < PCAPNG_POSA_MAX_FLDS; k++)
cur->flds[cur->nflds++] = tpl->flds[k]; }
lastfld = NULL;
continue;
}
if (!strcmp(toks[0], "else") || !strcmp(toks[0], "else:")) {
add_fld(cur, PCAPNG_POSA_ELSE);
if (nblk < 32) blk_indent[nblk++] = indent;
lastfld = NULL;
continue;
}
if (!strcmp(toks[0], "repeat") && nt >= 2) {
pcapng_posa_fld_t *f = add_fld(cur, PCAPNG_POSA_REPEAT);
if (f) {
int ai;
if (!strcmp(toks[1], "until")) {
f->until_end = 1;
if (nt > 2 && toks[2][0] == '"') { parse_delim(toks[2], f->delim, &f->ndelim); ai = 3; }
else { ai = (nt > 2 && !strcmp(toks[2], "end")) ? 3 : 2; }
} else {
char cnt[PCAPNG_POSA_NAME_MAX]; char *sign;
snprintf(cnt, sizeof cnt, "%s", toks[1]);
sign = strpbrk(cnt + 1, "+-");
if (sign) {
f->count_bias = atoi(sign);
*sign = '\0';
}
snprintf(f->lenfield, sizeof f->lenfield, "%s", cnt);
ai = 2;
}
if (ai + 1 < nt && !strcmp(toks[ai], "as"))
snprintf(f->name, sizeof f->name, "%s", toks[ai + 1]);
if (!f->name[0]) snprintf(f->name, sizeof f->name, "item");
{ const char *q = raw;
if (f->ndelim > 0) {
const char *q1 = strchr(raw, '"');
const char *q2 = q1 ? strchr(q1 + 1, '"') : NULL;
q = q2 ? q2 + 1 : raw + strlen(raw);
}
quoted(q, f->disp, sizeof f->disp); }
}
if (nblk < 32) blk_indent[nblk++] = indent;
lastfld = NULL;
continue;
}
if (!strcmp(toks[0], "seek") && nt >= 2) {
pcapng_posa_fld_t *f = add_fld(cur, PCAPNG_POSA_SEEK);
if (f) {
char arg[PCAPNG_POSA_NAME_MAX];
rest_after_kw(raw, "seek", arg, sizeof arg);
if (nt == 2 && arg[0] >= '0' && arg[0] <= '9') {
f->defnum = parse_num(arg); f->until_end = 1;
} else snprintf(f->lenfield, sizeof f->lenfield, "%s", arg);
}
lastfld = NULL;
continue;
}
if (!strcmp(toks[0], "lookup") && nt >= 2) {
if (lastfld) snprintf(lastfld->lookup_name, sizeof lastfld->lookup_name, "%s", toks[1]);
continue;
}
if (!strcmp(toks[0], "bits") && nt >= 5) {
pcapng_posa_fld_t *f = add_fld(cur, PCAPNG_POSA_BITS);
if (f) {
int k;
snprintf(f->src, sizeof f->src, "%s", toks[1]);
snprintf(f->name, sizeof f->name, "%s", toks[2]);
f->shift = (int)parse_num(toks[3]);
f->width = (int)parse_num(toks[4]);
if (f->width <= 0 || f->width > 64) f->width = 1;
for (k = 5; k < nt; k++)
if (!strcmp(toks[k], "lookup") && k + 1 < nt) {
snprintf(f->lookup_name, sizeof f->lookup_name, "%s", toks[k + 1]); break; }
quoted(raw, f->disp, sizeof f->disp);
}
lastfld = f;
continue;
}
if (!strcmp(toks[0], "label") && strchr(raw, '"')) {
pcapng_posa_fld_t *f = add_fld(cur, PCAPNG_POSA_LABEL);
if (f) {
quoted(raw, f->disp, sizeof f->disp);
f->nlargs = args_after_quote(raw, f->largs, PCAPNG_POSA_MAX_LARGS);
}
lastfld = NULL;
continue;
}
ti = 0;
if (!strcmp(toks[0], "required") || !strcmp(toks[0], "optional")) ti = 1;
if (ti < nt && !strcmp(toks[ti], "kvblock") && ti + 1 < nt) {
pcapng_posa_fld_t *f = add_fld(cur, PCAPNG_POSA_KVBLOCK);
if (f) {
snprintf(f->name, sizeof f->name, "%s", toks[ti + 1]);
memcpy(f->delim, "\r\n\r\n", 4); f->ndelim = 4;
memcpy(f->sub, ": ", 3);
{ const char *p = raw;
while ((p = strstr(p, "sep")) != NULL) {
if ((p == raw || p[-1] == ' ' || p[-1] == '\t') &&
(p[3] == ' ' || p[3] == '\t' || p[3] == '"')) {
const char *q = strchr(p + 3, '"');
if (q) {
char tmp[PCAPNG_POSA_DELIM_MAX]; int n = 0;
parse_delim(q, tmp, &n);
if (n > 0 && n < (int)sizeof f->sub) { memcpy(f->sub, tmp, n); f->sub[n] = '\0'; }
}
break;
}
p += 3;
}
}
{ const char *p = raw, *lq1 = NULL, *lq2 = NULL;
while (*p) {
const char *q1 = strchr(p, '"'), *q2 = q1 ? strchr(q1 + 1, '"') : NULL;
if (!q1 || !q2) break;
lq1 = q1; lq2 = q2; p = q2 + 1;
}
if (lq1 && lq2)
snprintf(f->disp, sizeof f->disp, "%.*s", (int)(lq2 - lq1 - 1), lq1 + 1);
}
}
lastfld = NULL;
continue;
}
if (ti < nt && (is_type_tok(toks[ti]) || !strcmp(toks[ti], "string"))) {
pcapng_posa_fld_t *f = add_fld(cur, PCAPNG_POSA_U8);
if (!f) continue;
if (strstr(raw, "eval(")) {
if (errbuf) snprintf(errbuf, errlen,
"line %d: eval(...) is not a field type — write `let %s = <expr>`",
lineno, (ti + 1 < nt) ? toks[ti + 1] : "name");
return -1;
}
parse_type(toks[ti], f);
if (f->type == PCAPNG_POSA_BYTES_REF || f->type == PCAPNG_POSA_STR_REF ||
f->type == PCAPNG_POSA_UTF16) {
const char *lb = strchr(raw, '[');
const char *rb = lb ? strchr(lb, ']') : NULL;
if (lb && rb && rb > lb + 1) {
size_t n = (size_t)(rb - lb - 1);
if (n >= sizeof f->lenfield) n = sizeof f->lenfield - 1;
memcpy(f->lenfield, lb + 1, n); f->lenfield[n] = '\0';
{ const char *nm = rb + 1; int k = 0;
while (*nm == ' ' || *nm == '\t') nm++;
while (*nm && *nm != ' ' && *nm != '\t' && k < (int)sizeof f->name - 1)
f->name[k++] = *nm++;
f->name[k] = '\0'; }
}
} else if (ti + 1 < nt) snprintf(f->name, sizeof f->name, "%s", toks[ti + 1]);
if (!strcmp(toks[ti], "string") && ti + 3 < nt && !strcmp(toks[ti + 2], "until")) {
const char *u = strstr(raw, "until");
const char *q = u ? strchr(u, '"') : NULL;
f->type = PCAPNG_POSA_STR_DELIM;
if (q) parse_delim(q, f->delim, &f->ndelim);
}
{ int k; for (k = ti + 2; k < nt; k++) if (!strcmp(toks[k], "=") && k + 1 < nt) {
f->defnum = parse_num(toks[k + 1]); break; } }
{ int k; for (k = ti + 2; k < nt; k++) if (!strcmp(toks[k], "mask") && k + 1 < nt) {
f->mask = parse_num(toks[k + 1]); break; } }
{ int k; for (k = ti + 2; k < nt; k++) if (!strcmp(toks[k], "matches") && k + 1 < nt) {
f->has_match = 1; f->match_val = parse_num(toks[k + 1]); break; } }
{ int k; for (k = ti + 1; k < nt; k++) if (!strcmp(toks[k], "hex")) { f->hex = 1; break; } }
{ int k; for (k = ti + 2; k < nt; k++) if (!strcmp(toks[k], "lookup") && k + 1 < nt) {
snprintf(f->lookup_name, sizeof f->lookup_name, "%s", toks[k + 1]); break; } }
{ const char *q = raw;
if (f->type == PCAPNG_POSA_STR_DELIM) {
const char *u = strstr(raw, "until");
const char *q1 = u ? strchr(u, '"') : NULL;
const char *q2 = q1 ? strchr(q1 + 1, '"') : NULL;
q = q2 ? q2 + 1 : raw + strlen(raw);
}
quoted(q, f->disp, sizeof f->disp); }
lastfld = f;
continue;
}
}
while (cur && nblk > 0) { add_fld(cur, PCAPNG_POSA_END); nblk--; }
return added;
}
int pcapng_posa_load_file(const char *path, char *errbuf, size_t errlen)
{
FILE *fp = fopen(path, "rb");
long sz; char *src; int rc;
if (!fp) { if (errbuf) snprintf(errbuf, errlen, "cannot open %s", path); return -1; }
fseek(fp, 0, SEEK_END); sz = ftell(fp); fseek(fp, 0, SEEK_SET);
if (sz < 0) { fclose(fp); return -1; }
src = malloc((size_t)sz + 1);
if (!src) { fclose(fp); return -1; }
if (fread(src, 1, (size_t)sz, fp) != (size_t)sz) { free(src); fclose(fp); return -1; }
src[sz] = '\0'; fclose(fp);
rc = parse_src(src, errbuf, errlen);
free(src);
return rc;
}
int pcapng_posa_load_text(const char *src, char *errbuf, size_t errlen)
{ return src ? parse_src(src, errbuf, errlen) : -1; }
int pcapng_posa_load_dir(const char *dir)
{
DIR *dp = opendir(dir); struct dirent *de; int total = 0;
if (!dp) return -1;
while ((de = readdir(dp))) {
size_t n = strlen(de->d_name); char path[1200]; int rc;
if (n < 6 || strcmp(de->d_name + n - 5, ".posa") != 0) continue;
snprintf(path, sizeof path, "%s/%s", dir, de->d_name);
rc = pcapng_posa_load_file(path, NULL, 0);
if (rc > 0) total += rc;
}
closedir(dp);
return total;
}
static const char *enum_name(const pcapng_posa_fld_t *f, uint64_t v)
{
int i;
for (i = 0; i < f->nenums; i++)
if (!f->enums[i].key[0] && f->enums[i].val == v) return f->enums[i].name;
if (f->lookup_name[0]) {
const pcapng_posa_lookup_t *lk = pcapng_posa_find_lookup(f->lookup_name);
if (lk)
for (i = 0; i < lk->nenums; i++)
if (!lk->enums[i].key[0] && lk->enums[i].val == v) return lk->enums[i].name;
}
return NULL;
}
static const char *enum_name_str(const pcapng_posa_fld_t *f, const char *text)
{
int i;
for (i = 0; i < f->nenums; i++)
if (f->enums[i].key[0] && strcmp(f->enums[i].key, text) == 0) return f->enums[i].name;
if (f->lookup_name[0]) {
const pcapng_posa_lookup_t *lk = pcapng_posa_find_lookup(f->lookup_name);
if (lk)
for (i = 0; i < lk->nenums; i++)
if (lk->enums[i].key[0] && strcmp(lk->enums[i].key, text) == 0) return lk->enums[i].name;
}
return NULL;
}
#define PCAPNG_POSA_ABBREV_JOIN (PCAPNG_POSA_NAME_MAX * 2 + 2)
static int fld_fixed_size(const pcapng_posa_fld_t *f)
{
switch (f->type) {
case PCAPNG_POSA_U8: return 1;
case PCAPNG_POSA_U16: case PCAPNG_POSA_LE16: return 2;
case PCAPNG_POSA_U24: return 3;
case PCAPNG_POSA_U32: case PCAPNG_POSA_LE32: return 4;
case PCAPNG_POSA_U64: case PCAPNG_POSA_LE64: return 8;
case PCAPNG_POSA_MAC: return 6;
case PCAPNG_POSA_IP4: return 4;
case PCAPNG_POSA_IP6: return 16;
case PCAPNG_POSA_UUID: return 16;
case PCAPNG_POSA_BYTES_FIXED: return (int)f->nbytes;
case PCAPNG_POSA_STR_FIXED: return (int)f->nbytes;
default: return -1;
}
}
static uint64_t rd_be(const uint8_t *d, int n) { uint64_t v = 0; int i; for (i = 0; i < n; i++) v = (v << 8) | d[i]; return v; }
static uint64_t rd_le(const uint8_t *d, int n) { uint64_t v = 0; int i; for (i = n - 1; i >= 0; i--) v = (v << 8) | d[i]; return v; }
typedef struct {
char name[PCAPNG_POSA_NAME_MAX];
uint64_t val, raw;
int start_off, end_off;
char disp[96];
} seen_t;
#define POSA_MAX_SEEN 128
static void seen_add(seen_t *seen, int *nseen, const char *name, uint64_t val, uint64_t raw,
int start_off, int end_off, const char *disp)
{
seen_t *s;
if (*nseen >= POSA_MAX_SEEN) return;
s = &seen[(*nseen)++];
snprintf(s->name, sizeof s->name, "%s", name);
s->val = val; s->raw = raw; s->start_off = start_off; s->end_off = end_off;
snprintf(s->disp, sizeof s->disp, "%s", disp ? disp : "");
}
static const seen_t *seen_get(const seen_t *seen, int nseen, const char *name)
{ int i; for (i = nseen - 1; i >= 0; i--) if (!strcmp(seen[i].name, name)) return &seen[i]; return NULL; }
typedef struct {
const char *p;
const seen_t *seen;
int nseen;
int offset, remaining;
int bad;
} evalctx_t;
static uint64_t ev_or(evalctx_t *e);
static void ev_ws(evalctx_t *e) { while (*e->p == ' ' || *e->p == '\t') e->p++; }
static uint64_t ev_primary(evalctx_t *e)
{
uint64_t v = 0;
ev_ws(e);
if (*e->p == '(') {
e->p++; v = ev_or(e); ev_ws(e);
if (*e->p == ')') e->p++; else e->bad = 1;
return v;
}
if (*e->p == '~') { e->p++; return ~ev_primary(e); }
if (*e->p == '-') { e->p++; return (uint64_t)(-(int64_t)ev_primary(e)); }
if (isdigit((unsigned char)*e->p)) {
char *end = NULL;
int base = (e->p[0] == '0' && (e->p[1] == 'x' || e->p[1] == 'X')) ? 16 : 10;
v = (uint64_t)strtoull(e->p, &end, base);
e->p = end ? end : e->p;
return v;
}
if (isalpha((unsigned char)*e->p) || *e->p == '_') {
char nm[PCAPNG_POSA_NAME_MAX]; int n = 0;
while ((isalnum((unsigned char)*e->p) || *e->p == '_') && n < (int)sizeof nm - 1)
nm[n++] = *e->p++;
nm[n] = '\0';
if (!strcmp(nm, "offset")) return (uint64_t)e->offset;
if (!strcmp(nm, "remaining")) return (uint64_t)e->remaining;
{ const seen_t *s = seen_get(e->seen, e->nseen, nm);
if (!s) { e->bad = 1; return 0; }
return s->val; }
}
e->bad = 1;
return 0;
}
static uint64_t ev_mul(evalctx_t *e)
{
uint64_t v = ev_primary(e);
for (;;) {
ev_ws(e);
if (*e->p == '*') { e->p++; v = v * ev_primary(e); }
else if (*e->p == '/') { uint64_t d; e->p++; d = ev_primary(e); v = d ? v / d : 0; }
else if (*e->p == '%') { uint64_t d; e->p++; d = ev_primary(e); v = d ? v % d : 0; }
else return v;
}
}
static uint64_t ev_add(evalctx_t *e)
{
uint64_t v = ev_mul(e);
for (;;) {
ev_ws(e);
if (*e->p == '+') { e->p++; v = v + ev_mul(e); }
else if (*e->p == '-' ) { e->p++; v = v - ev_mul(e); }
else return v;
}
}
static uint64_t ev_shift(evalctx_t *e)
{
uint64_t v = ev_add(e);
for (;;) {
ev_ws(e);
if (e->p[0] == '<' && e->p[1] == '<') { uint64_t s; e->p += 2; s = ev_add(e); v = (s < 64) ? v << s : 0; }
else if (e->p[0] == '>' && e->p[1] == '>') { uint64_t s; e->p += 2; s = ev_add(e); v = (s < 64) ? v >> s : 0; }
else return v;
}
}
static uint64_t ev_and(evalctx_t *e)
{
uint64_t v = ev_shift(e);
for (;;) { ev_ws(e);
if (*e->p == '&' && e->p[1] != '&') { e->p++; v = v & ev_shift(e); } else return v; }
}
static uint64_t ev_xor(evalctx_t *e)
{
uint64_t v = ev_and(e);
for (;;) { ev_ws(e); if (*e->p == '^') { e->p++; v = v ^ ev_and(e); } else return v; }
}
static uint64_t ev_or(evalctx_t *e)
{
uint64_t v = ev_xor(e);
for (;;) { ev_ws(e);
if (*e->p == '|' && e->p[1] != '|') { e->p++; v = v | ev_xor(e); } else return v; }
}
static int posa_eval(const char *expr, const seen_t *seen, int nseen,
int offset, int remaining, uint64_t *out)
{
evalctx_t e;
uint64_t v;
e.p = expr; e.seen = seen; e.nseen = nseen;
e.offset = offset; e.remaining = remaining; e.bad = 0;
v = ev_or(&e);
ev_ws(&e);
if (e.bad || *e.p) return 0;
*out = v;
return 1;
}
static int resolve_len(const char *text, const seen_t *seen, int nseen,
int offset, int remaining, uint64_t *out)
{
const seen_t *s;
if (!text || !*text) return 0;
if (!strpbrk(text, "+-*/%&|^<>~()")) {
s = seen_get(seen, nseen, text);
if (!s) return 0;
*out = s->val;
return 1;
}
return posa_eval(text, seen, nseen, offset, remaining, out);
}
#define POSA_BIND_SLOTS 4096
typedef struct {
int used;
char conv[40];
char table[PCAPNG_POSA_NAME_MAX];
uint64_t key;
uint64_t valnum;
char val[96];
} posa_bind_t;
static posa_bind_t g_convmem[POSA_BIND_SLOTS];
static int g_nconvmem;
static char g_conv[40];
#define POSA_WARN_MAX 16
static char g_warn[POSA_WARN_MAX][160];
static int g_nwarn;
void pcapng_posa_set_conversation(const char *community_id)
{ snprintf(g_conv, sizeof g_conv, "%s", community_id ? community_id : ""); }
void pcapng_posa_binds_clear(void)
{ memset(g_convmem, 0, sizeof g_convmem); g_nconvmem = 0; }
int pcapng_posa_bind_count(void) { return g_nconvmem; }
int pcapng_posa_warning_count(void) { return g_nwarn; }
const char *pcapng_posa_warning_at(int i)
{ return (i >= 0 && i < g_nwarn) ? g_warn[i] : NULL; }
static void posa_warn(const char *fmt, const char *a, uint64_t b)
{
if (g_nwarn >= POSA_WARN_MAX) return;
snprintf(g_warn[g_nwarn], sizeof g_warn[0], fmt, a, (unsigned long long)b);
g_nwarn++;
}
static unsigned posa_bind_hash(const char *conv, const char *table, uint64_t key)
{
unsigned h = 2166136261u;
const char *p;
int i;
for (p = conv; *p; p++) { h ^= (unsigned char)*p; h *= 16777619u; }
for (p = table; *p; p++) { h ^= (unsigned char)*p; h *= 16777619u; }
for (i = 0; i < 8; i++) { h ^= (unsigned)((key >> (i * 8)) & 0xff); h *= 16777619u; }
return h & (POSA_BIND_SLOTS - 1);
}
static void posa_bind_put(const char *table, uint64_t key,
uint64_t valnum, const char *val)
{
unsigned i = posa_bind_hash(g_conv, table, key);
int probe;
if (!g_conv[0]) return;
for (probe = 0; probe < 64; probe++) {
posa_bind_t *b = &g_convmem[(i + (unsigned)probe) & (POSA_BIND_SLOTS - 1)];
if (b->used && (b->key != key || strcmp(b->conv, g_conv) || strcmp(b->table, table)))
continue;
if (!b->used) g_nconvmem++;
b->used = 1;
snprintf(b->conv, sizeof b->conv, "%s", g_conv);
snprintf(b->table, sizeof b->table, "%s", table);
b->key = key; b->valnum = valnum;
snprintf(b->val, sizeof b->val, "%s", val ? val : "");
return;
}
{ posa_bind_t *b = &g_convmem[i];
snprintf(b->conv, sizeof b->conv, "%s", g_conv);
snprintf(b->table, sizeof b->table, "%s", table);
b->key = key; b->valnum = valnum;
snprintf(b->val, sizeof b->val, "%s", val ? val : ""); }
}
static const posa_bind_t *posa_bind_get(const char *table, uint64_t key)
{
unsigned i = posa_bind_hash(g_conv, table, key);
int probe;
if (!g_conv[0]) return NULL;
for (probe = 0; probe < 64; probe++) {
const posa_bind_t *b = &g_convmem[(i + (unsigned)probe) & (POSA_BIND_SLOTS - 1)];
if (!b->used) return NULL;
if (b->key == key && !strcmp(b->conv, g_conv) && !strcmp(b->table, table)) return b;
}
return NULL;
}
static int guard_eval1(pcapng_posa_cmp_t op, uint64_t lv, uint64_t mask, uint64_t rhs)
{
if (mask) lv &= mask;
switch (op) {
case PCAPNG_POSA_CMP_EQ: return lv == rhs;
case PCAPNG_POSA_CMP_NE: return lv != rhs;
case PCAPNG_POSA_CMP_LT: return lv < rhs;
case PCAPNG_POSA_CMP_GT: return lv > rhs;
case PCAPNG_POSA_CMP_GE: return lv >= rhs;
case PCAPNG_POSA_CMP_LE: return lv <= rhs;
default: return 1;
}
}
static uint64_t guard_lv(const char *name, const seen_t *seen, int nseen, int off, int lim)
{
if (!strcmp(name, "remaining")) return (uint64_t)(lim - off);
{ const seen_t *s = seen_get(seen, nseen, name); return s ? s->val : 0; }
}
static int guard_ok(const pcapng_posa_guard_t *g, const seen_t *seen, int nseen, int off, int lim)
{
int r1, r2;
if (g->op == PCAPNG_POSA_CMP_NONE) return 1;
r1 = guard_eval1(g->op, guard_lv(g->lhs, seen, nseen, off, lim), g->mask, g->rhs);
if (g->op2 == PCAPNG_POSA_CMP_NONE) return r1;
r2 = guard_eval1(g->op2, guard_lv(g->lhs2, seen, nseen, off, lim), g->mask2, g->rhs2);
return g->logic2 ? (r1 || r2) : (r1 && r2);
}
static void fmt_expand(const char *fmt, const char args[][PCAPNG_POSA_NAME_MAX], int nargs,
const seen_t *pref, int npref, const seen_t *seen, int nseen,
char *out, size_t outlen)
{
const char *f = fmt; size_t o = 0; int ai = 0;
if (!out || !outlen) return;
while (*f && o < outlen - 1) {
if (*f == '%' && f[1]) {
char c = f[1];
const seen_t *s = NULL;
if (ai < nargs) {
s = seen_get(pref, npref, args[ai]);
if (!s) s = seen_get(seen, nseen, args[ai]);
}
ai++; f += 2;
if (c == 's') o += (size_t)snprintf(out + o, outlen - o, "%s", s ? s->disp : "");
else if (c=='u'||c=='d') o += (size_t)snprintf(out + o, outlen - o, "%llu", s ? (unsigned long long)s->val : 0ULL);
else if (c == 'x') o += (size_t)snprintf(out + o, outlen - o, "%llx", s ? (unsigned long long)s->val : 0ULL);
else if (c == '%') out[o++] = '%';
} else out[o++] = *f++;
}
out[o] = '\0';
{ size_t r = 0, w = 0;
while (out[r]) {
if (out[r] == ' ' && (w == 0 || out[w - 1] == ' ')) { r++; continue; }
out[w++] = out[r++];
}
while (w > 0 && out[w - 1] == ' ') w--;
out[w] = '\0'; }
}
static int dns_name_at(const uint8_t *d, int len, int off, char *out, int outsz)
{
int op = 0, end = -1, hops = 0;
out[0] = '\0';
while (off >= 0 && off < len) {
int lab = d[off];
if ((lab & 0xc0) == 0xc0) {
if (off + 1 >= len || ++hops > 16) break;
if (end < 0) end = off + 2;
off = ((lab & 0x3f) << 8) | d[off + 1];
continue;
}
if (lab & 0xc0) break;
off++;
if (lab == 0) { if (end < 0) end = off; break; }
if (off + lab > len) break;
if (op && op < outsz - 1) out[op++] = '.';
{ int k; for (k = 0; k < lab; k++) if (op < outsz - 1) out[op++] = (char)d[off + k]; }
off += lab;
}
out[op] = '\0';
return end < 0 ? off : end;
}
static int find_delim(const uint8_t *d, int off, int lim, const char *delim, int ndelim)
{
int i;
if (ndelim <= 0) return -1;
for (i = off; i + ndelim <= lim; i++) if (!memcmp(d + i, delim, (size_t)ndelim)) return i;
return -1;
}
typedef struct {
int type;
int prev_lim;
int fld_index;
int count, iter, until_end;
int item_start, section_start, seen_base;
pcapng_field_t *prev_node;
pcapng_field_t *section;
pcapng_field_t *item;
const pcapng_posa_fld_t *lbl;
char item_ab[PCAPNG_POSA_ABBREV_JOIN];
} blk_t;
#define POSA_MAX_ITER 4096
static const char *fld_disp(const pcapng_posa_fld_t *f)
{ return f->disp[0] ? f->disp : f->name; }
static int at_delim(const pcapng_posa_fld_t *f, const uint8_t *d, int off, int lim)
{
if (f->ndelim <= 0) return 0;
if (off + f->ndelim > lim) return 0;
return memcmp(d + off, f->delim, (size_t)f->ndelim) == 0;
}
static int dissect_one(const pcapng_posa_proto_t *p, const uint8_t *data, int len,
pcapng_field_t *node, int abs_off, char *info, size_t infolen)
{
int off = 0, i, nseen = 0, nfirst = 0, lim = len, skip = 0, rejected = 0;
seen_t seen[POSA_MAX_SEEN], first[POSA_MAX_SEEN];
char ab[PCAPNG_POSA_ABBREV_JOIN], child_info[192] = "";
const char *prefix = p->abbrev[0] ? p->abbrev : p->name;
pcapng_field_t *cur = node;
blk_t bstack[32]; int nb = 0;
int taken[34] = {0};
for (i = 0; i < p->nflds; i++) {
const pcapng_posa_fld_t *f = &p->flds[i];
int sz;
pcapng_field_t *cf = NULL;
if (skip) {
if (f->type == PCAPNG_POSA_SCOPE || f->type == PCAPNG_POSA_WHEN ||
f->type == PCAPNG_POSA_ELSE || f->type == PCAPNG_POSA_REPEAT) skip++;
else if (f->type == PCAPNG_POSA_END) skip--;
continue;
}
if (f->type == PCAPNG_POSA_ELSE) {
if (taken[nb] || nb >= 32) {
skip = 1;
} else {
taken[nb] = 1;
bstack[nb].type = PCAPNG_POSA_WHEN; bstack[nb].prev_lim = lim; nb++;
taken[nb] = 0;
}
continue;
}
if (f->type == PCAPNG_POSA_SCOPE) {
uint64_t sv = 0;
const seen_t *s = seen_get(seen, nseen, f->lenfield);
int have = resolve_len(f->lenfield, seen, nseen, off, lim - off, &sv);
int nl = !have ? lim : (s ? s->end_off + (int)sv : off + (int)sv);
if (nl > lim) nl = lim;
if (nl < off) nl = off;
if (nb < 32) { bstack[nb].type = PCAPNG_POSA_SCOPE; bstack[nb].prev_lim = lim; nb++;
taken[nb] = 0; }
lim = nl;
continue;
}
if (f->type == PCAPNG_POSA_WHEN) {
int ok = guard_ok(&f->guard, seen, nseen, off, lim);
if (ok) taken[nb] = 1;
if (ok) {
if (nb < 32) { bstack[nb].type = PCAPNG_POSA_WHEN; bstack[nb].prev_lim = lim; nb++;
taken[nb] = 0; }
} else skip = 1;
continue;
}
if (f->type == PCAPNG_POSA_REPEAT) {
uint64_t rv = 0;
int have = f->until_end ? 0 : resolve_len(f->lenfield, seen, nseen, off, lim - off, &rv);
int cnt = f->until_end ? -1 : (have ? (int)rv + f->count_bias : 0);
blk_t *b;
if (nb >= 32 || off >= lim || (!f->until_end && cnt <= 0) ||
at_delim(f, data, off, lim)) { skip = 1; continue; }
b = &bstack[nb++];
b->type = PCAPNG_POSA_REPEAT; b->prev_lim = lim;
b->fld_index = i; b->count = cnt; b->iter = 0; b->until_end = f->until_end;
b->prev_node = cur; b->section = cur; b->lbl = NULL;
b->section_start = off;
snprintf(b->item_ab, sizeof b->item_ab, "%s.%s", prefix, f->name);
if (f->disp[0]) {
b->section = pf_add(cur, NULL, PCAPNG_FT_NONE);
pf_label(b->section, "%s", f->disp);
}
b->item = pf_add(b->section, b->item_ab, PCAPNG_FT_NONE);
pf_label(b->item, "%s", f->name);
b->item_start = off; b->seen_base = nseen;
taken[nb] = 0;
cur = b->item;
continue;
}
if (f->type == PCAPNG_POSA_LABEL) {
if (nb > 0 && bstack[nb - 1].type == PCAPNG_POSA_REPEAT) bstack[nb - 1].lbl = f;
continue;
}
if (f->type == PCAPNG_POSA_END) {
blk_t *b;
if (nb == 0) continue;
b = &bstack[nb - 1];
if (b->type != PCAPNG_POSA_REPEAT) {
nb--;
if (b->type == PCAPNG_POSA_SCOPE) off = lim;
lim = b->prev_lim;
continue;
}
pf_range(b->item, abs_off + b->item_start, off - b->item_start);
if (b->lbl) {
char t[192];
fmt_expand(b->lbl->disp, b->lbl->largs, b->lbl->nlargs, NULL, 0, seen, nseen,
t, sizeof t);
pf_label(b->item, "%s", t);
}
{ int k;
for (k = b->seen_base; k < nseen; k++)
if (!seen_get(first, nfirst, seen[k].name) && nfirst < POSA_MAX_SEEN)
first[nfirst++] = seen[k]; }
b->iter++;
if (off > b->item_start && off < lim && b->iter < POSA_MAX_ITER &&
!at_delim(&p->flds[b->fld_index], data, off, lim) &&
(b->until_end || b->iter < b->count)) {
nseen = b->seen_base;
b->item = pf_add(b->section, b->item_ab, PCAPNG_FT_NONE);
pf_label(b->item, "%s", p->flds[b->fld_index].name);
b->item_start = off;
taken[nb] = 0;
cur = b->item;
i = b->fld_index;
continue;
}
if (b->section != b->prev_node)
pf_range(b->section, abs_off + b->section_start, off - b->section_start);
cur = b->prev_node; lim = b->prev_lim; nb--;
continue;
}
if (f->type == PCAPNG_POSA_LAYER) {
int sublen = lim - off; char sinfo[192] = "";
if (sublen > 0) {
int used = pcapng_posa_dissect(f->sub, data + off, sublen, cur, abs_off + off, sinfo, sizeof sinfo);
if (used > 0) off += used;
if (sinfo[0]) snprintf(child_info, sizeof child_info, "%s", sinfo);
}
continue;
}
if (f->type == PCAPNG_POSA_BIND) {
const seen_t *k = seen_get(seen, nseen, f->lenfield);
const seen_t *v = seen_get(seen, nseen, f->src);
if (k && v) posa_bind_put(f->sub, k->val, v->val, v->disp);
continue;
}
if (f->type == PCAPNG_POSA_RECALL) {
const seen_t *k = seen_get(seen, nseen, f->lenfield);
const posa_bind_t *b = k ? posa_bind_get(f->sub, k->val) : NULL;
char rab[PCAPNG_POSA_ABBREV_JOIN];
pcapng_field_t *rf;
snprintf(rab, sizeof rab, "%s.%s", prefix, f->name);
if (b) {
const char *en = enum_name_str(f, b->val);
rf = pf_add(cur, rab, PCAPNG_FT_STR); pf_str(rf, b->val);
if (en) pf_label(rf, "%s: %s (%s)", fld_disp(f), en, b->val);
else pf_label(rf, "%s: %s", fld_disp(f), b->val);
pf_range(rf, abs_off + off, 0);
seen_add(seen, &nseen, f->name, b->valnum, b->valnum, off, off, b->val);
} else if (k) {
rf = pf_add(cur, rab, PCAPNG_FT_STR);
pf_str(rf, "<not bound in this conversation>");
pf_label(rf, "%s: <not bound in this conversation> (%s=%llu)",
fld_disp(f), f->lenfield, (unsigned long long)k->val);
pf_range(rf, abs_off + off, 0);
posa_warn("recall %s[%llu]: nothing bound in this conversation",
f->sub, k->val);
}
continue;
}
if (f->type == PCAPNG_POSA_SEEK) {
uint64_t sv = 0;
int have = f->until_end ? 0 : resolve_len(f->lenfield, seen, nseen, off, lim - off, &sv);
int to = f->until_end ? (int)f->defnum : (have ? (int)sv : -1);
if (to >= 0 && to <= lim) off = to;
continue;
}
if (f->type == PCAPNG_POSA_BITS) {
const seen_t *s = seen_get(seen, nseen, f->src);
uint64_t m = (f->width >= 64) ? ~0ULL : ((1ULL << f->width) - 1);
uint64_t v = s ? (s->raw >> f->shift) & m : 0;
const char *en = enum_name(f, v);
char disp[96];
snprintf(ab, sizeof ab, "%s.%s", prefix, f->name);
cf = pf_add(cur, ab, PCAPNG_FT_UINT); pf_uint(cf, v);
if (en) snprintf(disp, sizeof disp, "%s", en);
else snprintf(disp, sizeof disp, "%llu", (unsigned long long)v);
pf_label(cf, "%s: %s", fld_disp(f), disp);
if (s) pf_range(cf, abs_off + s->start_off, s->end_off - s->start_off);
seen_add(seen, &nseen, f->name, v, v, s ? s->start_off : off, s ? s->end_off : off, disp);
continue;
}
snprintf(ab, sizeof ab, "%s.%s", prefix, f->name);
sz = fld_fixed_size(f);
if (sz >= 0) {
if (off + sz > lim) break;
switch (f->type) {
case PCAPNG_POSA_U8: case PCAPNG_POSA_U16: case PCAPNG_POSA_U24:
case PCAPNG_POSA_U32: case PCAPNG_POSA_U64:
case PCAPNG_POSA_LE16: case PCAPNG_POSA_LE32: case PCAPNG_POSA_LE64: {
int le = (f->type == PCAPNG_POSA_LE16 || f->type == PCAPNG_POSA_LE32 || f->type == PCAPNG_POSA_LE64);
uint64_t raw = le ? rd_le(data + off, sz) : rd_be(data + off, sz);
uint64_t v = f->mask ? (raw & f->mask) : raw;
const char *en;
char disp[96];
if (f->has_match && v != f->match_val) { rejected = 1; break; }
en = enum_name(f, v);
cf = pf_add(cur, ab, PCAPNG_FT_UINT); pf_uint(cf, v);
if (en) { pf_label(cf, "%s: %s (%llu)", fld_disp(f), en, (unsigned long long)v);
snprintf(disp, sizeof disp, "%s", en); }
else if (f->hex) { pf_label(cf, "%s: 0x%0*llx", fld_disp(f), sz * 2, (unsigned long long)v);
snprintf(disp, sizeof disp, "0x%0*llx", sz * 2, (unsigned long long)v); }
else { pf_label(cf, "%s: %llu", fld_disp(f), (unsigned long long)v);
snprintf(disp, sizeof disp, "%llu", (unsigned long long)v); }
seen_add(seen, &nseen, f->name, v, raw, off, off + sz, disp);
break; }
case PCAPNG_POSA_MAC:
cf = pf_add(cur, ab, PCAPNG_FT_MAC); pf_mac(cf, data + off);
pf_label(cf, "%s: %s", fld_disp(f), cf->str);
seen_add(seen, &nseen, f->name, 0, 0, off, off + sz, cf->str); break;
case PCAPNG_POSA_IP4:
cf = pf_add(cur, ab, PCAPNG_FT_IPV4); pf_ipv4(cf, data + off);
pf_label(cf, "%s: %s", fld_disp(f), cf->str);
seen_add(seen, &nseen, f->name, 0, 0, off, off + sz, cf->str); break;
case PCAPNG_POSA_IP6:
cf = pf_add(cur, ab, PCAPNG_FT_IPV6); pf_ipv6(cf, data + off);
pf_label(cf, "%s: %s", fld_disp(f), cf->str);
seen_add(seen, &nseen, f->name, 0, 0, off, off + sz, cf->str); break;
case PCAPNG_POSA_UUID: {
char u[40];
const uint8_t *b = data + off;
snprintf(u, sizeof u,
"%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
b[3], b[2], b[1], b[0], b[5], b[4], b[7], b[6],
b[8], b[9], b[10], b[11], b[12], b[13], b[14], b[15]);
cf = pf_add(cur, ab, PCAPNG_FT_STR); pf_str(cf, u);
{ const char *en = enum_name_str(f, u);
if (en) pf_label(cf, "%s: %s (%s)", fld_disp(f), en, u);
else pf_label(cf, "%s: %s", fld_disp(f), u); }
seen_add(seen, &nseen, f->name, 0, 0, off, off + sz, u); break; }
case PCAPNG_POSA_BYTES_FIXED:
cf = pf_add(cur, ab, PCAPNG_FT_BYTES); pf_bytes(cf, data + off, sz);
pf_label(cf, "%s: %d bytes", fld_disp(f), sz);
seen_add(seen, &nseen, f->name, 0, 0, off, off + sz, ""); break;
case PCAPNG_POSA_STR_FIXED: {
char tmp[128]; int k, n = sz < (int)sizeof tmp - 1 ? sz : (int)sizeof tmp - 1;
uint64_t num = 0;
for (k = 0; k < n; k++) {
unsigned char ch = data[off + k];
tmp[k] = (ch >= 32 && ch < 127) ? (char)ch : '.';
}
tmp[n] = '\0';
for (k = 0; k < sz && k < 8; k++) num = (num << 8) | data[off + k];
cf = pf_add(cur, ab, PCAPNG_FT_STR); pf_str(cf, tmp);
{ const char *en = enum_name_str(f, tmp);
if (en) pf_label(cf, "%s: %s (%s)", fld_disp(f), en, tmp);
else pf_label(cf, "%s: %s", fld_disp(f), tmp); }
seen_add(seen, &nseen, f->name, num, num, off, off + sz, tmp); break; }
default: break;
}
if (rejected) break;
if (cf) pf_range(cf, abs_off + off, sz);
off += sz;
} else if (f->type == PCAPNG_POSA_LET) {
uint64_t v = 0;
snprintf(ab, sizeof ab, "%s.%s", prefix, f->name);
if (posa_eval(f->expr, seen, nseen, off, lim - off, &v)) {
const char *en = enum_name(f, v);
char disp[96];
cf = pf_add(cur, ab, PCAPNG_FT_UINT); pf_uint(cf, v);
if (en) { pf_label(cf, "%s: %s (%llu)", fld_disp(f), en, (unsigned long long)v);
snprintf(disp, sizeof disp, "%s", en); }
else if (f->hex) { pf_label(cf, "%s: 0x%llx", fld_disp(f), (unsigned long long)v);
snprintf(disp, sizeof disp, "0x%llx", (unsigned long long)v); }
else { pf_label(cf, "%s: %llu", fld_disp(f), (unsigned long long)v);
snprintf(disp, sizeof disp, "%llu", (unsigned long long)v); }
pf_range(cf, abs_off + off, 0);
seen_add(seen, &nseen, f->name, v, v, off, off, disp);
}
} else if (f->type == PCAPNG_POSA_QUIC_VARINT || f->type == PCAPNG_POSA_LEB128) {
int start = off, k, n;
uint64_t raw = 0, v;
if (off >= lim) break;
if (f->type == PCAPNG_POSA_QUIC_VARINT) {
n = 1 << (data[off] >> 6);
if (off + n > lim) break;
raw = (uint64_t)(data[off] & 0x3f);
for (k = 1; k < n; k++) raw = (raw << 8) | data[off + k];
off += n;
} else {
for (k = 0; off < lim && k < 10; k++) {
uint8_t b = data[off++];
raw |= (uint64_t)(b & 0x7f) << (7 * k);
if (!(b & 0x80)) break;
}
}
v = f->mask ? (raw & f->mask) : raw;
{ const char *en = enum_name(f, v);
char disp[96];
cf = pf_add(cur, ab, PCAPNG_FT_UINT); pf_uint(cf, v);
if (en) { pf_label(cf, "%s: %s (%llu)", fld_disp(f), en, (unsigned long long)v);
snprintf(disp, sizeof disp, "%s", en); }
else if (f->hex) { pf_label(cf, "%s: 0x%llx", fld_disp(f), (unsigned long long)v);
snprintf(disp, sizeof disp, "0x%llx", (unsigned long long)v); }
else { pf_label(cf, "%s: %llu", fld_disp(f), (unsigned long long)v);
snprintf(disp, sizeof disp, "%llu", (unsigned long long)v); }
pf_range(cf, abs_off + start, off - start);
seen_add(seen, &nseen, f->name, v, raw, start, off, disp); }
} else if (f->type == PCAPNG_POSA_DNSNAME) {
int start = off; char nm[256];
int end = dns_name_at(data, len, off, nm, sizeof nm);
if (end <= start) end = start < lim ? start + 1 : lim;
off = end > lim ? lim : end;
cf = pf_add(cur, ab, PCAPNG_FT_STR); pf_str(cf, nm);
pf_label(cf, "%s: %s", fld_disp(f), nm[0] ? nm : "<Root>");
pf_range(cf, abs_off + start, off - start);
seen_add(seen, &nseen, f->name, 0, 0, start, off, nm[0] ? nm : "<Root>");
} else if (f->type == PCAPNG_POSA_CSTRING) {
int start = off, n = 0; char tmp[256];
while (off < lim && data[off] != '\0') {
if (n < (int)sizeof tmp - 1) tmp[n++] = (char)data[off];
off++;
}
tmp[n] = '\0';
if (off < lim && data[off] == '\0') off++;
cf = pf_add(cur, ab, PCAPNG_FT_STR); pf_str(cf, tmp);
{ const char *en = enum_name_str(f, tmp);
if (en) pf_label(cf, "%s: %s (%s)", fld_disp(f), en, tmp);
else pf_label(cf, "%s: %s", fld_disp(f), tmp); }
pf_range(cf, abs_off + start, off - start);
{ uint64_t len = (uint64_t)(off - start > 0 ? off - start - 1 : 0);
seen_add(seen, &nseen, f->name, len, len, start, off, tmp); }
} else if (f->type == PCAPNG_POSA_STR_DELIM) {
int start = off, d = find_delim(data, off, lim, f->delim, f->ndelim), n; char tmp[256];
n = (d >= 0) ? d - start : 0;
if (n > (int)sizeof tmp - 1) n = (int)sizeof tmp - 1;
memcpy(tmp, data + start, (size_t)n); tmp[n] = '\0';
if (d >= 0) off = d + f->ndelim;
cf = pf_add(cur, ab, PCAPNG_FT_STR); pf_str(cf, tmp);
{ const char *en = enum_name_str(f, tmp);
if (en) pf_label(cf, "%s: %s (%s)", fld_disp(f), en, tmp);
else pf_label(cf, "%s: %s", fld_disp(f), tmp); }
pf_range(cf, abs_off + start, off - start);
seen_add(seen, &nseen, f->name, (uint64_t)n, (uint64_t)n, start, off, tmp);
} else if (f->type == PCAPNG_POSA_UTF16) {
uint64_t lv = 0;
int start = off, n = resolve_len(f->lenfield, seen, nseen, off, lim - off, &lv) ? (int)lv : 0, k, o2 = 0;
char tmp[256];
if (n < 0) n = 0;
if (off + n > lim) n = lim - off;
if (n < 0) n = 0;
for (k = 0; k + 1 < n && o2 < (int)sizeof tmp - 1; k += 2) {
uint16_t wc = (uint16_t)(data[off + k] | (data[off + k + 1] << 8));
tmp[o2++] = (wc >= 32 && wc < 127) ? (char)wc : (wc ? '.' : ' ');
}
tmp[o2] = '\0';
while (o2 > 0 && tmp[o2 - 1] == ' ') tmp[--o2] = '\0';
cf = pf_add(cur, ab, PCAPNG_FT_STR); pf_str(cf, tmp);
pf_label(cf, "%s: %s", fld_disp(f), tmp);
pf_range(cf, abs_off + start, n);
seen_add(seen, &nseen, f->name, (uint64_t)n, (uint64_t)n, start, off + n, tmp);
off += n;
} else if (f->type == PCAPNG_POSA_BYTES_REF || f->type == PCAPNG_POSA_STR_REF) {
uint64_t lv = 0;
int start = off, n = resolve_len(f->lenfield, seen, nseen, off, lim - off, &lv) ? (int)lv : 0;
if (n < 0) n = 0;
if (off + n > lim) n = lim - off;
if (n < 0) n = 0;
if (f->type == PCAPNG_POSA_STR_REF) {
char tmp[256]; int k, o2 = 0;
for (k = 0; k < n && o2 < (int)sizeof tmp - 1; k++) {
uint8_t ch = data[off + k];
tmp[o2++] = (ch >= 32 && ch < 127) ? (char)ch : '.';
}
tmp[o2] = '\0';
cf = pf_add(cur, ab, PCAPNG_FT_STR); pf_str(cf, tmp);
pf_label(cf, "%s: %s", fld_disp(f), tmp);
seen_add(seen, &nseen, f->name, (uint64_t)n, (uint64_t)n, start, off + n, tmp);
} else {
cf = pf_add(cur, ab, PCAPNG_FT_BYTES); pf_bytes(cf, data + off, n);
pf_label(cf, "%s: %d bytes", fld_disp(f), n);
seen_add(seen, &nseen, f->name, (uint64_t)n, (uint64_t)n, start, off + n, "");
}
pf_range(cf, abs_off + off, n);
off += n;
} else if (f->type == PCAPNG_POSA_KVBLOCK) {
const char *sep = f->sub[0] ? f->sub : ": ";
int seplen = (int)strlen(sep);
int end_pos = (f->ndelim > 0) ? find_delim(data, off, lim, f->delim, f->ndelim) : -1;
int block_end = (end_pos >= 0) ? end_pos + f->ndelim : lim;
int pos = off;
cf = pf_add(cur, ab, PCAPNG_FT_NONE);
pf_label(cf, "%s", fld_disp(f));
pf_range(cf, abs_off + off, block_end - off);
while (pos < block_end) {
int line_end = -1, lsz = 2, k;
for (k = pos; k < block_end - 1; k++)
if (data[k] == '\r' && data[k+1] == '\n') { line_end = k; lsz = 2; break; }
if (line_end < 0) {
for (k = pos; k < block_end; k++)
if (data[k] == '\n') { line_end = k; lsz = 1; break; }
}
if (line_end < 0) break;
if (line_end == pos) { pos += lsz; break; }
int sep_pos = -1;
for (k = pos; k <= line_end - seplen; k++)
if (memcmp(data + k, sep, (size_t)seplen) == 0) { sep_pos = k; break; }
if (sep_pos < 0) { pos = line_end + lsz; continue; }
int key_len = sep_pos - pos;
int val_len = line_end - (sep_pos + seplen);
if (key_len <= 0 || key_len >= PCAPNG_POSA_NAME_MAX) { pos = line_end + lsz; continue; }
char key_norm[PCAPNG_POSA_NAME_MAX], key_orig[PCAPNG_POSA_NAME_MAX], val_buf[256];
int kn = key_len < PCAPNG_POSA_NAME_MAX - 1 ? key_len : PCAPNG_POSA_NAME_MAX - 2;
memcpy(key_orig, data + pos, kn); key_orig[kn] = '\0';
for (k = 0; k < kn; k++) {
unsigned char c = (unsigned char)data[pos + k];
key_norm[k] = (c == '-' || c == ' ') ? '_' : (char)tolower(c);
}
key_norm[kn] = '\0';
if (val_len < 0) val_len = 0;
int vl = val_len < (int)sizeof val_buf - 1 ? val_len : (int)sizeof val_buf - 2;
memcpy(val_buf, data + sep_pos + seplen, (size_t)vl); val_buf[vl] = '\0';
char child_ab[PCAPNG_POSA_ABBREV_JOIN + PCAPNG_POSA_NAME_MAX + 2];
snprintf(child_ab, sizeof child_ab, "%s.%s", ab, key_norm);
{ pcapng_field_t *cfc = pf_add(cf, child_ab, PCAPNG_FT_STR);
pf_str(cfc, val_buf);
pf_label(cfc, "%s: %s", key_orig, val_buf);
pf_range(cfc, abs_off + pos, line_end + lsz - pos); }
seen_add(seen, &nseen, key_norm, 0, 0, pos, line_end + lsz, val_buf);
pos = line_end + lsz;
}
off = block_end;
} else if (f->type == PCAPNG_POSA_PAYLOAD) {
int n = lim - off; if (n < 0) n = 0;
cf = pf_add(cur, ab, PCAPNG_FT_BYTES); pf_bytes(cf, data + off, n);
pf_label(cf, "%s: %d bytes", fld_disp(f), n);
pf_range(cf, abs_off + off, n); off = lim;
}
}
if (info && infolen) {
if (child_info[0]) snprintf(info, infolen, "%s", child_info);
else if (p->info_fmt[0])
fmt_expand(p->info_fmt, p->info_args, p->info_nargs, first, nfirst, seen, nseen,
info, infolen);
}
if (rejected) return -1;
return off;
}
static const pcapng_posa_proto_t *resolve_group(const char *name, const uint8_t *data, int len)
{
const pcapng_posa_proto_t *fallback = NULL;
int i;
for (i = 0; i < g_nprotos; i++) {
const pcapng_posa_proto_t *p = g_protos[i]; int k;
if (!p || p->nprefix == 0 || strcmp(p->parent, name) != 0) continue;
for (k = 0; k < p->nprefix; k++)
if (p->prefix_len[k] <= len && memcmp(data, p->prefixes[k], p->prefix_len[k]) == 0)
return p;
}
for (i = 0; i < g_nprotos; i++) {
const pcapng_posa_proto_t *p = g_protos[i]; int sz;
if (!p) continue;
if (strcmp(p->parent, name) != 0 || p->nflds == 0) continue;
sz = fld_fixed_size(&p->flds[0]);
if (sz <= 0 || sz > len) continue;
{ uint64_t v = (p->flds[0].type == PCAPNG_POSA_LE16 || p->flds[0].type == PCAPNG_POSA_LE32 ||
p->flds[0].type == PCAPNG_POSA_LE64) ? rd_le(data, sz) : rd_be(data, sz);
if (v == p->flds[0].defnum) return p; }
}
for (i = 0; i < g_nprotos; i++) {
const pcapng_posa_proto_t *p = g_protos[i];
if (p && p->is_default && !strcmp(p->parent, name)) { fallback = p; break; }
}
return (pcapng_posa_proto_t *)fallback;
}
const pcapng_posa_proto_t *pcapng_posa_resolve(const char *name, const uint8_t *data, int len)
{
const pcapng_posa_proto_t *p;
if (!name) return NULL;
p = pcapng_posa_find(name);
if (p) return p;
return resolve_group(name, data, len);
}
static char g_last_col[32];
const char *pcapng_posa_last_col(void) { return g_last_col[0] ? g_last_col : NULL; }
void pcapng_posa_reset_col(void) { g_last_col[0] = '\0'; }
int pcapng_posa_dissect(const char *proto_name, const uint8_t *data, int len,
pcapng_field_t *parent, int abs_off, char *info, size_t infolen)
{
const pcapng_posa_proto_t *p = pcapng_posa_find(proto_name);
pcapng_field_t *node; int used;
g_nwarn = 0;
if (!proto_name || !data || len <= 0) return 0;
if (!p) { p = resolve_group(proto_name, data, len); if (!p) return 0; }
node = pf_add(parent, p->abbrev[0] ? p->abbrev : p->name, PCAPNG_FT_NONE);
pf_label(node, "%s", p->name);
if (p->display[0]) snprintf(g_last_col, sizeof g_last_col, "%s", p->display);
used = dissect_one(p, data, len, node, abs_off, info, infolen);
if (used < 0) {
pf_remove_child(parent, node);
return 0;
}
pf_range(node, abs_off, used > 0 ? used : len);
return used > 0 ? used : len;
}
int pcapng_posa_to_text(const pcapng_posa_proto_t *p, char *out, size_t sz)
{
static const char *TN[] = { "uint8","uint16","uint32","uint64","le_uint16","le_uint32",
"le_uint64","mac","ip4","cstring","payload" };
size_t o = 0; int i, j;
if (!p || !out || sz == 0) return 0;
o += (size_t)snprintf(out + o, sz - o, "# %s decoder (.posa) — regenerated by libpcapng\n", p->name);
o += (size_t)snprintf(out + o, sz - o, "Object<%s> %s\n", p->parent[0] ? p->parent : "main", p->name);
for (i = 0; i < p->nflds && o < sz; i++) {
const pcapng_posa_fld_t *f = &p->flds[i]; char type[PCAPNG_POSA_NAME_MAX + 16];
if (f->type == PCAPNG_POSA_BYTES_FIXED) snprintf(type, sizeof type, "bytes<%zu>", f->nbytes);
else if (f->type == PCAPNG_POSA_STR_FIXED) snprintf(type, sizeof type, "str<%zu>", f->nbytes);
else if (f->type == PCAPNG_POSA_BYTES_REF) snprintf(type, sizeof type, "bytes[%s]", f->lenfield);
else if (f->type == PCAPNG_POSA_QUIC_VARINT) snprintf(type, sizeof type, "quic_varint");
else if (f->type == PCAPNG_POSA_LEB128) snprintf(type, sizeof type, "leb128");
else if (f->type == PCAPNG_POSA_UUID) snprintf(type, sizeof type, "uuid");
else if (f->type == PCAPNG_POSA_LET) snprintf(type, sizeof type, "uint64");
else snprintf(type, sizeof type, "%s", (f->type >= 0 && f->type <= PCAPNG_POSA_PAYLOAD) ? TN[f->type] : "uint8");
o += (size_t)snprintf(out + o, sz - o, " required %s %s", type, f->name);
if (f->nenums > 0 || f->type <= PCAPNG_POSA_U64)
o += (size_t)snprintf(out + o, sz - o, " = %llu", (unsigned long long)f->defnum);
o += (size_t)snprintf(out + o, sz - o, "\n");
for (j = 0; j < f->nenums && o < sz; j++)
o += (size_t)snprintf(out + o, sz - o, " %s = %llu\n", f->enums[j].name, (unsigned long long)f->enums[j].val);
}
return (int)o;
}