in erts/emulator/beam/packet_parser.c [255:507]
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 */
char delimiter, /* Line delimiting character */
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 ... Delimiter] */
const char* ptr2;
if ((ptr2 = memchr(ptr, delimiter, 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;
}