nfqws: QUIC protocol recognition

This commit is contained in:
bol-van
2022-03-20 20:46:39 +03:00
parent 38dca7da70
commit 0a5ffc1a54
10 changed files with 140 additions and 62 deletions

View File

@@ -22,22 +22,22 @@ bool IsHttp(const uint8_t *data, size_t len)
}
bool HttpExtractHost(const uint8_t *data, size_t len, char *host, size_t len_host)
{
const uint8_t *p, *s, *e=data+len;
const uint8_t *p, *s, *e = data + len;
p = (uint8_t*)strncasestr((char*)data, "\nHost:", len);
if (!p) return false;
p+=6;
while(p<e && (*p==' ' || *p=='\t')) p++;
s=p;
while(s<e && (*s!='\r' && *s!='\n' && *s!=' ' && *s!='\t')) s++;
if (s>p)
p += 6;
while (p < e && (*p == ' ' || *p == '\t')) p++;
s = p;
while (s < e && (*s != '\r' && *s != '\n' && *s != ' ' && *s != '\t')) s++;
if (s > p)
{
size_t slen = s-p;
size_t slen = s - p;
if (host && len_host)
{
if (slen>=len_host) slen=len_host-1;
for(size_t i=0;i<slen;i++) host[i]=tolower(p[i]);
host[slen]=0;
if (slen >= len_host) slen = len_host - 1;
for (size_t i = 0; i < slen; i++) host[i] = tolower(p[i]);
host[slen] = 0;
}
return true;
}
@@ -45,7 +45,7 @@ bool HttpExtractHost(const uint8_t *data, size_t len, char *host, size_t len_hos
}
bool IsTLSClientHello(const uint8_t *data, size_t len)
{
return len>=6 && data[0]==0x16 && data[1]==0x03 && data[2]>=0x01 && data[2]<=0x03 && data[5]==0x01 && (ntohs(*(uint16_t*)(data+3))+5)<=len;
return len >= 6 && data[0] == 0x16 && data[1] == 0x03 && data[2] >= 0x01 && data[2] <= 0x03 && data[5] == 0x01 && (ntohs(*(uint16_t*)(data + 3)) + 5) <= len;
}
bool TLSFindExt(const uint8_t *data, size_t len, uint16_t type, const uint8_t **ext, size_t *len_ext)
{
@@ -66,36 +66,36 @@ bool TLSFindExt(const uint8_t *data, size_t len, uint16_t type, const uint8_t **
// <CompressionMethods>
// u16 ExtensionsLength
size_t l,ll;
size_t l, ll;
l = 1+2+2+1+3+2+32;
l = 1 + 2 + 2 + 1 + 3 + 2 + 32;
// SessionIDLength
if (len<(l+1)) return false;
ll = data[6]<<16 | data[7]<<8 | data[8]; // HandshakeProtocol length
if (len<(ll+9)) return false;
l += data[l]+1;
if (len < (l + 1)) return false;
ll = data[6] << 16 | data[7] << 8 | data[8]; // HandshakeProtocol length
if (len < (ll + 9)) return false;
l += data[l] + 1;
// CipherSuitesLength
if (len<(l+2)) return false;
l += ntohs(*(uint16_t*)(data+l))+2;
if (len < (l + 2)) return false;
l += ntohs(*(uint16_t*)(data + l)) + 2;
// CompressionMethodsLength
if (len<(l+1)) return false;
l += data[l]+1;
if (len < (l + 1)) return false;
l += data[l] + 1;
// ExtensionsLength
if (len<(l+2)) return false;
if (len < (l + 2)) return false;
data+=l; len-=l;
l=ntohs(*(uint16_t*)data);
data+=2; len-=2;
if (l<len) return false;
data += l; len -= l;
l = ntohs(*(uint16_t*)data);
data += 2; len -= 2;
if (l < len) return false;
uint16_t ntype=htons(type);
while(l>=4)
uint16_t ntype = htons(type);
while (l >= 4)
{
uint16_t etype=*(uint16_t*)data;
size_t elen=ntohs(*(uint16_t*)(data+2));
data+=4; l-=4;
if (l<elen) break;
if (etype==ntype)
uint16_t etype = *(uint16_t*)data;
size_t elen = ntohs(*(uint16_t*)(data + 2));
data += 4; l -= 4;
if (l < elen) break;
if (etype == ntype)
{
if (ext && len_ext)
{
@@ -104,7 +104,7 @@ bool TLSFindExt(const uint8_t *data, size_t len, uint16_t type, const uint8_t **
}
return true;
}
data+=elen; l-=elen;
data += elen; l -= elen;
}
return false;
@@ -114,19 +114,76 @@ bool TLSHelloExtractHost(const uint8_t *data, size_t len, char *host, size_t len
const uint8_t *ext;
size_t elen;
if (!TLSFindExt(data,len,0,&ext,&elen)) return false;
if (!TLSFindExt(data, len, 0, &ext, &elen)) return false;
// u16 data+0 - name list length
// u8 data+2 - server name type. 0=host_name
// u16 data+3 - server name length
if (elen<5 || ext[2]!=0) return false;
size_t slen = ntohs(*(uint16_t*)(ext+3));
ext+=5; elen-=5;
if (slen<elen) return false;
if (elen < 5 || ext[2] != 0) return false;
size_t slen = ntohs(*(uint16_t*)(ext + 3));
ext += 5; elen -= 5;
if (slen < elen) return false;
if (ext && len_host)
{
if (slen>=len_host) slen=len_host-1;
for(size_t i=0;i<slen;i++) host[i]=tolower(ext[i]);
host[slen]=0;
if (slen >= len_host) slen = len_host - 1;
for (size_t i = 0; i < slen; i++) host[i] = tolower(ext[i]);
host[slen] = 0;
}
return true;
}
#define QUIC_MAX_CID_LENGTH 20
/* Returns the QUIC draft version or 0 if not applicable. */
static inline uint8_t quic_draft_version(uint32_t version) {
/* IETF Draft versions */
if ((version >> 8) == 0xff0000) {
return (uint8_t)version;
}
/* Facebook mvfst, based on draft -22. */
if (version == 0xfaceb001) {
return 22;
}
/* Facebook mvfst, based on draft -27. */
if (version == 0xfaceb002 || version == 0xfaceb00e) {
return 27;
}
/* GQUIC Q050, T050 and T051: they are not really based on any drafts,
* but we must return a sensible value */
if (version == 0x51303530 ||
version == 0x54303530 ||
version == 0x54303531) {
return 27;
}
/* https://tools.ietf.org/html/draft-ietf-quic-transport-32#section-15
"Versions that follow the pattern 0x?a?a?a?a are reserved for use in
forcing version negotiation to be exercised"
It is tricky to return a correct draft version: such number is primarily
used to select a proper salt (which depends on the version itself), but
we don't have a real version here! Let's hope that we need to handle
only latest drafts... */
if ((version & 0x0F0F0F0F) == 0x0a0a0a0a) {
return 29;
}
/* QUIC (final?) constants for v1 are defined in draft-33, but draft-34 is the
final draft version */
if (version == 0x00000001) {
return 34;
}
/* QUIC Version 2 */
/* TODO: for the time being use 100 as a number for V2 and let see how v2 drafts evolve */
if (version == 0x709A50C4) {
return 100;
}
return 0;
}
bool IsQUICInitial(uint8_t *data, size_t len)
{
// long header, fixed bit, type=initial
if (len < 512 || (data[0] & 0xF0) != 0xC0) return false;
uint8_t *p = data + 1;
uint32_t ver = ntohl(*(uint32_t*)p);
if (quic_draft_version(ver) < 11) return false;
p += 4;
if (!*p || *p > QUIC_MAX_CID_LENGTH) return false;
return true;
}