/* * Determines from the first five bytes of a potential SLP header * if the following message is really an SLP message. Returns 1 if * it is a real SLP message, 0 if not. */ int valid_slp(unsigned char *slphdr, int len) { struct slpv1_hdr slp1; struct slpv2_hdr slp2; len -= (8 /* udp */ + 20 /* IP */ + 14 /* ether */); /* a valid version will be 1 or 2 */ switch (*slphdr) { case 1: memcpy(&slp1, slphdr, 5); /* valid function? */ if (slp1.function > V1_MAX_FUNCTION) { return (0); } /* valid length heuristic */ if (slp1.length > len) { return (0); } return (1); case 2: memcpy(&slp2, slphdr, 5); /* valid function? */ if (slp2.function > V2_MAX_FUNCTION) { return (0); } /* valid length heuristic */ get_int24(&(slp2.l1)); if (netval > len) { return (0); } return (1); default: return (0); } }
/* Return > 0 Total packet length.in bytes * = 0 Length unknown, need more data. * < 0 Error, invalid format. */ int packet_get_length(enum PacketParseType htype, const char* ptr, unsigned n, /* Bytes read so far */ unsigned max_plen, /* Max packet length, 0=no limit */ unsigned trunc_len, /* Truncate (lines) if longer, 0=no limit */ int* statep) /* Protocol specific state */ { unsigned hlen, plen; switch (htype) { case TCP_PB_RAW: if (n == 0) goto more; else { DEBUGF((" => nothing remain packet=%d\r\n", n)); return n; } case TCP_PB_1: /* TCP_PB_1: [L0 | Data] */ hlen = 1; if (n < hlen) goto more; plen = get_int8(ptr); goto remain; case TCP_PB_2: /* TCP_PB_2: [L1,L0 | Data] */ hlen = 2; if (n < hlen) goto more; plen = get_int16(ptr); goto remain; case TCP_PB_4: /* TCP_PB_4: [L3,L2,L1,L0 | Data] */ hlen = 4; if (n < hlen) goto more; plen = get_int32(ptr); goto remain; case TCP_PB_RM: /* TCP_PB_RM: [L3,L2,L1,L0 | Data] ** where MSB (bit) is used to signal end of record */ hlen = 4; if (n < hlen) goto more; plen = get_int32(ptr) & 0x7fffffff; goto remain; case TCP_PB_LINE_LF: { /* TCP_PB_LINE_LF: [Data ... \n] */ const char* ptr2; if ((ptr2 = memchr(ptr, '\n', n)) == NULL) { if (n > max_plen && max_plen != 0) { /* packet full */ DEBUGF((" => packet full (no NL)=%d\r\n", n)); goto error; } else if (n >= trunc_len && trunc_len!=0) { /* buffer full */ DEBUGF((" => line buffer full (no NL)=%d\r\n", n)); return trunc_len; } goto more; } else { int len = (ptr2 - ptr) + 1; /* including newline */ if (len > max_plen && max_plen!=0) { DEBUGF((" => packet_size %d exceeded\r\n", max_plen)); goto error; } if (len > trunc_len && trunc_len!=0) { DEBUGF((" => truncated line=%d\r\n", trunc_len)); return trunc_len; } DEBUGF((" => nothing remain packet=%d\r\n", len)); return len; } } case TCP_PB_ASN1: { /* TCP_PB_ASN1: handles long (4 bytes) or short length format */ const char* tptr = ptr; int length; int nn = n; if (n < 2) goto more; nn--; if ((*tptr++ & 0x1f) == 0x1f) { /* Long tag format */ while (nn && ((*tptr & 0x80) == 0x80)) { tptr++; nn--; } if (nn < 2) goto more; tptr++; nn--; } /* tptr now point to length field and nn characters remain */ length = *tptr & 0x7f; if ((*tptr & 0x80) == 0x80) { /* Long length format */ tptr++; nn--; if (nn < length) goto more; switch (length) { case 0: plen = 0; break; case 1: plen = get_int8(tptr); tptr += 1; break; case 2: plen = get_int16(tptr); tptr += 2; break; case 3: plen = get_int24(tptr); tptr += 3; break; case 4: plen = get_int32(tptr); tptr += 4; break; default: goto error; /* error */ } } else { tptr++; plen = length; } hlen = (tptr-ptr); goto remain; } case TCP_PB_CDR: { const struct cdr_head* hp; hlen = sizeof(struct cdr_head); if (n < hlen) goto more; hp = (struct cdr_head*) ptr; if (sys_memcmp(hp->magic, CDR_MAGIC, 4) != 0) goto error; if (hp->flags & 0x01) /* Byte ordering flag */ plen = get_little_int32(hp->message_size); else plen = get_int32(hp->message_size); goto remain; } case TCP_PB_FCGI: { const struct fcgi_head* hp; hlen = sizeof(struct fcgi_head); if (n < hlen) goto more; hp = (struct fcgi_head*) ptr; if (hp->version != FCGI_VERSION_1) goto error; plen = ((hp->contentLengthB1 << 8) | hp->contentLengthB0) + hp->paddingLength; goto remain; } case TCP_PB_HTTPH: case TCP_PB_HTTPH_BIN: *statep = !0; case TCP_PB_HTTP: case TCP_PB_HTTP_BIN: /* TCP_PB_HTTP: data \r\n(SP data\r\n)* */ plen = n; if (((plen == 1) && NL(ptr)) || ((plen == 2) && CRNL(ptr))) goto done; else { const char* ptr1 = ptr; int len = plen; if (!max_plen) { /* This is for backward compatibility with old user of decode_packet * that might use option 'line_length' to limit accepted length of * http lines. */ max_plen = trunc_len; } while (1) { const char* ptr2 = memchr(ptr1, '\n', len); if (ptr2 == NULL) { if (max_plen != 0) { if (n >= max_plen) /* packet full */ goto error; } goto more; } else { plen = (ptr2 - ptr) + 1; if (*statep == 0) { if (max_plen != 0 && plen > max_plen) goto error; goto done; } if (plen < n) { if (SP(ptr2+1) && plen>2) { /* header field value continue on next line */ ptr1 = ptr2+1; len = n - plen; } else { if (max_plen != 0 && plen > max_plen) goto error; goto done; } } else { if (max_plen != 0 && plen > max_plen) goto error; goto more; } } } } case TCP_PB_TPKT: { const struct tpkt_head* hp; hlen = sizeof(struct tpkt_head); if (n < hlen) goto more; hp = (struct tpkt_head*) ptr; if (hp->vrsn == TPKT_VRSN) { plen = get_int16(hp->packet_length) - hlen; } else { goto error; } goto remain; } case TCP_PB_SSL_TLS: hlen = 5; if (n < hlen) goto more; if ((ptr[0] & 0x80) && ptr[2] == 1) { /* Ssl-v2 Client hello <<1:1, Len:15, 1:8, Version:16>> */ plen = (get_int16(&ptr[0]) & 0x7fff) - 3; } else { /* <<ContentType:8, Version:16, Length:16>> */ plen = get_int16(&ptr[3]); } goto remain; default: DEBUGF((" => case error\r\n")); return -1; } more: return 0; remain: { int tlen = hlen + plen; if ((max_plen != 0 && plen > max_plen) || tlen < (int)hlen) { /* wrap-around protection */ return -1; } return tlen; } done: return plen; error: return -1; }
static int v2_finish(struct slpv2_hdr *slp, int flags) { unsigned int firstop; if (!(flags & F_DTAIL)) return (1); /* check for options */ get_int24(&(slp->o1)); firstop = netval; if (firstop) { unsigned short op_id; unsigned short nextop; char *op_class; for (;;) { unsigned short real_oplen; if (msglength < 4) { sprintf(get_line(0, 0), "Option expected but not present"); return (0); } nbtohs(); op_id = netval; p += sizeof (unsigned short); msglength -= sizeof (unsigned short); nbtohs(); nextop = netval; p += sizeof (unsigned short); msglength -= sizeof (unsigned short); real_oplen = nextop ? nextop : msglength; /* known options */ switch (op_id) { case 1: sprintf(get_line(0, 0), "Option: Required Attribute Missing"); DOFIELD("Template IDVer", FIELD_DEFAULT); DOFIELD("Required Attrs", FIELD_DEFAULT); break; default: sprintf(get_line(0, 0), "Option: Unknown"); p += (real_oplen - 4); msglength -= (real_oplen - 4); break; } if (op_id < 0x3fff) op_class = "Standardized, optional"; else if (op_id < 0x7fff) op_class = "Standardized, mandatory"; else if (op_id < 0x8fff) op_class = "Not standardized, private"; else if (op_id < 0xffff) op_class = "Reserved"; sprintf(get_line(0, 0), "Option ID = 0x%04x, %s", op_id, op_class); if (nextop && ((nextop - 4) > msglength) && !tcp_continuation) { sprintf(get_line(0, 0), "[Framing error: remaining pkt length = %u]", msglength); return (0); } sprintf(get_line(0, 0), "Option Length = %u", real_oplen); if (!nextop) break; } } return (1); }
static int v2_header(int flags, struct slpv2_hdr *slp, int *totallen, int fraglen) { extern int curr_proto, dst_port; char *prototag = (curr_proto == IPPROTO_TCP ? "/tcp" : ""); if ((slp->flags & V2_OVERFLOW) == V2_OVERFLOW) overflow = B_TRUE; /* summary mode header parsing */ if (flags & F_SUM) { /* make sure we have at least a header */ if (msglength < sizeof (*slp)) { sprintf(get_sum_line(), "SLP V2 [Incomplete Header]"); return (0); } sprintf(msgbuf, "SLP V2 %s [%d%s] ", slpv2_func(slp->function, B_TRUE), ntohs(slp->xid), prototag); /* skip to end of header */ msgend = msgbuf + strlen(msgbuf); msglength -= sizeof (*slp); p += sizeof (*slp); /* skip language tag */ SKIPFIELD(FIELD_DEFAULT); } else if (flags & F_DTAIL) { char *lang; int len; /* detailed mode header parsing */ show_header("SLP: ", "Service Location Protocol (v2)", fraglen); show_space(); if (msglength < sizeof (*slp)) { sprintf(get_line(0, 0), "==> Incomplete SLP header"); return (0); } sprintf(get_line(0, 0), "Version = %d", slp->vers); sprintf(get_line(0, 0), "Function = %d, %s", slp->function, slpv2_func(slp->function, B_FALSE)); get_int24(&(slp->l1)); *totallen = netval; sprintf(get_line(0, 0), "Message length = %u", *totallen); /* check for TCP continuation */ if (curr_proto == IPPROTO_TCP && *totallen > msglength && !tcp_continuation) { tcp_continuation = B_TRUE; reg_tcp_cont((char *)slp, *totallen, msglength, dst_port); } if (!tcp_continuation && *totallen != msglength) { sprintf(get_line(0, 0), " (Stated and on-the-wire lengths differ)"); } /* flags */ sprintf(get_line(0, 0), "Flags = 0x%02x", slp->flags); sprintf(get_line(0, 0), " %s", getflag(slp->flags, V2_OVERFLOW, "overflow", "no overflow")); sprintf(get_line(0, 0), " %s", getflag(slp->flags, V2_FRESH, "fresh registration", "no fresh registration")); sprintf(get_line(0, 0), " %s", getflag(slp->flags, V2_MCAST, "request multicast / broadcast", "unicast")); /* check reserved flags that must be zero */ if ((slp->flags & 7) != 0) { sprintf(get_line(0, 0), " .... .xxx = %d (reserved flags nonzero)", slp->flags & 7); } /* end of flags */ /* language tag */ p = (char *)slp + sizeof (*slp); msglength -= sizeof (*slp); GETSHORT(len); if (len > msglength) { sprintf(get_line(0, 0), "Language Tag Length = %u [CORRUPT MESSAGE]", len); return (0); } lang = get_line(0, 0); strcpy(lang, "Language Tag = "); strncat(lang, p, len); sprintf(get_line(0, 0), "XID = %u", ntohs(slp->xid)); /* set msglength to remaining length of SLP message */ p += len; msglength -= len; } return (1); }