mirror of
https://github.com/nmap/nmap.git
synced 2026-06-27 19:53:10 +00:00
Consolidate TCP option parsing
Some checks are pending
nmap multiplatform autobuilds / build (arm64, gcc, ubuntu-latest-gcc-arm64, ubuntu-latest) (push) Waiting to run
nmap multiplatform autobuilds / build (clang, freebsd-15-clang, ubuntu-latest) (push) Waiting to run
nmap multiplatform autobuilds / build (clang, macos-15-clang, macos-15) (push) Waiting to run
nmap multiplatform autobuilds / build (clang, macos-26-clang, macos-26) (push) Waiting to run
nmap multiplatform autobuilds / build (clang, netbsd-10-clang, ubuntu-latest) (push) Waiting to run
nmap multiplatform autobuilds / build (clang, openbsd-7-clang, ubuntu-latest) (push) Waiting to run
nmap multiplatform autobuilds / build (clang, solaris-11-clang, ubuntu-latest) (push) Waiting to run
nmap multiplatform autobuilds / build (clang, ubuntu-latest-clang, ubuntu-latest) (push) Waiting to run
nmap multiplatform autobuilds / build (egcc, openbsd-7-gcc, ubuntu-latest) (push) Waiting to run
nmap multiplatform autobuilds / build (gcc, freebsd-15-gcc, ubuntu-latest) (push) Waiting to run
nmap multiplatform autobuilds / build (gcc, netbsd-10-gcc, ubuntu-latest) (push) Waiting to run
nmap multiplatform autobuilds / build (gcc, solaris-11-gcc, ubuntu-latest) (push) Waiting to run
nmap multiplatform autobuilds / build (gcc, ubuntu-latest-gcc, ubuntu-latest) (push) Waiting to run
nmap multiplatform autobuilds / build (msvc, windows-latest-msvc, windows-latest) (push) Waiting to run
Some checks are pending
nmap multiplatform autobuilds / build (arm64, gcc, ubuntu-latest-gcc-arm64, ubuntu-latest) (push) Waiting to run
nmap multiplatform autobuilds / build (clang, freebsd-15-clang, ubuntu-latest) (push) Waiting to run
nmap multiplatform autobuilds / build (clang, macos-15-clang, macos-15) (push) Waiting to run
nmap multiplatform autobuilds / build (clang, macos-26-clang, macos-26) (push) Waiting to run
nmap multiplatform autobuilds / build (clang, netbsd-10-clang, ubuntu-latest) (push) Waiting to run
nmap multiplatform autobuilds / build (clang, openbsd-7-clang, ubuntu-latest) (push) Waiting to run
nmap multiplatform autobuilds / build (clang, solaris-11-clang, ubuntu-latest) (push) Waiting to run
nmap multiplatform autobuilds / build (clang, ubuntu-latest-clang, ubuntu-latest) (push) Waiting to run
nmap multiplatform autobuilds / build (egcc, openbsd-7-gcc, ubuntu-latest) (push) Waiting to run
nmap multiplatform autobuilds / build (gcc, freebsd-15-gcc, ubuntu-latest) (push) Waiting to run
nmap multiplatform autobuilds / build (gcc, netbsd-10-gcc, ubuntu-latest) (push) Waiting to run
nmap multiplatform autobuilds / build (gcc, solaris-11-gcc, ubuntu-latest) (push) Waiting to run
nmap multiplatform autobuilds / build (gcc, ubuntu-latest-gcc, ubuntu-latest) (push) Waiting to run
nmap multiplatform autobuilds / build (msvc, windows-latest-msvc, windows-latest) (push) Waiting to run
This commit is contained in:
parent
486fa2ebdd
commit
c86a45ef6e
6 changed files with 367 additions and 294 deletions
|
|
@ -813,6 +813,27 @@ const u8 *TCPHeader::getOptions(size_t *optslen) const {
|
|||
return this->h.options;
|
||||
} /* End of getOptions() */
|
||||
|
||||
struct tcpopt_atindex_ctx {
|
||||
unsigned int index;
|
||||
unsigned int found;
|
||||
nping_tcp_opt_t result;
|
||||
tcpopt_atindex_ctx() : index(0), found(0) {
|
||||
memset(&result, 0, sizeof(result));
|
||||
}
|
||||
};
|
||||
|
||||
static bool tcpopt_atindex(u8 op, u8 oplen, const u8 *data, void *ctx)
|
||||
{
|
||||
tcpopt_atindex_ctx *args = static_cast<tcpopt_atindex_ctx *>(ctx);
|
||||
if (args->index == args->found) {
|
||||
args->result.type = op;
|
||||
args->result.len = oplen;
|
||||
args->result.value = data + 2;
|
||||
return false;
|
||||
}
|
||||
args->found += 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Returns the index-th option in the TCP header. On success it returns a
|
||||
* structure filled with option information. If there is no index-th option,
|
||||
|
|
@ -824,61 +845,13 @@ const u8 *TCPHeader::getOptions(size_t *optslen) const {
|
|||
* would be set to zero and the "value" field should NOT be accessed, as it
|
||||
* will not contain reliable information. */
|
||||
nping_tcp_opt_t TCPHeader::getOption(unsigned int index) const {
|
||||
nping_tcp_opt_t *curr_opt=NULL;
|
||||
u8 *curr_pnt=(u8 *)this->h.options;
|
||||
int bytes_left=this->length - TCP_HEADER_LEN;
|
||||
assert((this->length - TCP_HEADER_LEN) == this->tcpoptlen);
|
||||
unsigned int optsfound=0;
|
||||
nping_tcp_opt_t result;
|
||||
memset(&result, 0, sizeof(nping_tcp_opt_t));
|
||||
|
||||
while(bytes_left>0){
|
||||
/* Use the opts structure as a template to access current option. It is
|
||||
* OK to use it because we only access the first two elements. */
|
||||
curr_opt=(nping_tcp_opt_t *)curr_pnt;
|
||||
|
||||
/* If we are right in the option that the caller wants, just return it */
|
||||
if(optsfound==index){
|
||||
result.type=curr_opt->type;
|
||||
if(result.type==TCPOPT_EOL || result.type==TCPOPT_NOOP)
|
||||
result.len=1;
|
||||
else
|
||||
result.len=curr_opt->len;
|
||||
result.value=(u8 *)curr_pnt+2;
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Otherwise, we have to parse it, so we can skip it and access the next
|
||||
* option */
|
||||
switch(curr_opt->type){
|
||||
|
||||
/* EOL or NOOP
|
||||
+-+-+-+-+-+-+-+-+
|
||||
| X |
|
||||
+-+-+-+-+-+-+-+-+ */
|
||||
case TCPOPT_EOL:
|
||||
goto out;
|
||||
|
||||
case TCPOPT_NOOP:
|
||||
curr_pnt++; /* Skip one octet */
|
||||
bytes_left--;
|
||||
break;
|
||||
|
||||
/* TLV encoded option */
|
||||
default:
|
||||
/* If we don't have as many octets as the option advertises, the
|
||||
* option is bogus. Return failure. */
|
||||
if(bytes_left<curr_opt->len)
|
||||
return result;
|
||||
curr_pnt+=curr_opt->len;
|
||||
bytes_left-=curr_opt->len;
|
||||
break;
|
||||
}
|
||||
optsfound++;
|
||||
TCPOptions opts;
|
||||
tcpopt_atindex_ctx ctx;
|
||||
if (opts.fromTCPHeader(*this)) {
|
||||
ctx.index = index;
|
||||
opts.foreachOpt(tcpopt_atindex, &ctx);
|
||||
}
|
||||
|
||||
out:
|
||||
return result;
|
||||
return ctx.result;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -935,3 +908,71 @@ const char *TCPHeader::optcode2str(u8 optcode){
|
|||
} /* End of optcode2str() */
|
||||
|
||||
|
||||
bool TCPOptions::fromTCPPacket(const u8 *tcppkt, int tcplen)
|
||||
{
|
||||
tcpopts = NULL;
|
||||
optslen = 0;
|
||||
if (tcplen < TCP_HEADER_LEN)
|
||||
return false;
|
||||
|
||||
u8 data_offset = tcppkt[12] >> 4;
|
||||
if (data_offset < 5)
|
||||
return false;
|
||||
|
||||
tcpopts = tcppkt + TCP_HEADER_LEN;
|
||||
optslen = MIN(4 * data_offset, tcplen) - TCP_HEADER_LEN;
|
||||
if (optslen == 0)
|
||||
tcpopts = NULL;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TCPOptions::fromBuffer(const u8 *tcpoptions, int optionslen)
|
||||
{
|
||||
if (!tcpoptions || optionslen <= 0)
|
||||
return false;
|
||||
tcpopts = tcpoptions;
|
||||
optslen = optionslen;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TCPOptions::fromTCPHeader(const TCPHeader &T)
|
||||
{
|
||||
size_t len = 0;
|
||||
tcpopts = T.getOptions(&len);
|
||||
if (len > INT_MAX) {
|
||||
tcpopts = NULL;
|
||||
return false;
|
||||
}
|
||||
optslen = len;
|
||||
return tcpopts != NULL;
|
||||
}
|
||||
|
||||
bool TCPOptions::foreachOpt(tcpopt_callback cb, void *ctx) const
|
||||
{
|
||||
const u8 *p = tcpopts;
|
||||
int len = optslen;
|
||||
while (len > 0) {
|
||||
int op = p[0];
|
||||
int oplen = 1;
|
||||
switch (op) {
|
||||
case 0: /* TCPOPT_EOL */
|
||||
case 1: /* TCPOPT_NOP */
|
||||
break;
|
||||
default: /* TLV option */
|
||||
if (len < 2)
|
||||
return false;
|
||||
oplen = p[1];
|
||||
if (oplen < 2)
|
||||
return false; /* No infinite loops, please */
|
||||
if (oplen > len)
|
||||
return false; /* Not enough space */
|
||||
break;
|
||||
}
|
||||
if (!cb(op, oplen, p, ctx))
|
||||
return true;
|
||||
len -= oplen;
|
||||
p += oplen;
|
||||
}
|
||||
return len == 0;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -123,7 +123,7 @@
|
|||
struct nping_tcp_opt {
|
||||
u8 type; /* Option type code. */
|
||||
u8 len; /* Option length. */
|
||||
u8 *value; /* Option value */
|
||||
const u8 *value; /* Option value */
|
||||
}__attribute__((__packed__));
|
||||
typedef struct nping_tcp_opt nping_tcp_opt_t;
|
||||
|
||||
|
|
@ -257,4 +257,26 @@ class TCPHeader : public TransportLayerElement {
|
|||
|
||||
}; /* End of class TCPHeader */
|
||||
|
||||
/* This callback will be called for each option in the buffer.
|
||||
* Return true to continue processing options.
|
||||
* Return false to stop processing options. */
|
||||
typedef bool (*tcpopt_callback)(u8 op, u8 oplen, const u8 *data, void *ctx);
|
||||
|
||||
class TCPOptions {
|
||||
public:
|
||||
/* Note: this class parses in-place and does not make a copy of the data. The
|
||||
* data pointed to by tcppkt must remain allocated as long as the TCPOptions
|
||||
* object is in use. */
|
||||
TCPOptions() : tcpopts(NULL), optslen(0) {}
|
||||
bool fromTCPPacket(const u8 *tcppkt, int tcplen);
|
||||
bool fromBuffer(const u8 *tcpoptions, int optionslen);
|
||||
bool fromTCPHeader(const TCPHeader &T);
|
||||
|
||||
/* Returns true if no errors were encountered, even if the callback returns false. */
|
||||
bool foreachOpt(tcpopt_callback cb, void *ctx) const;
|
||||
private:
|
||||
const u8 *tcpopts;
|
||||
int optslen;
|
||||
};
|
||||
|
||||
#endif /* __TCPHEADER_H__ */
|
||||
|
|
|
|||
|
|
@ -120,6 +120,7 @@ typedef unsigned __int8 u_int8_t;
|
|||
#endif
|
||||
|
||||
#include "netutil.h"
|
||||
#include "TCPHeader.h"
|
||||
|
||||
#if HAVE_NET_IF_H
|
||||
#ifndef NET_IF_H /* This guarding is needed for at least some versions of OpenBSD */
|
||||
|
|
@ -1392,6 +1393,42 @@ const char *proto2ascii_uppercase(u8 proto) {
|
|||
return proto2ascii_case(proto, 1);
|
||||
}
|
||||
|
||||
struct tcpopt_info_ctx {
|
||||
char *p;
|
||||
char *end;
|
||||
bool valid;
|
||||
tcpopt_info_ctx() : p(NULL), end(NULL), valid(true) {}
|
||||
bool check_length(size_t len) const {
|
||||
return (end - p) >= len;
|
||||
}
|
||||
void put_str(const char *str) {
|
||||
if (p >= end)
|
||||
return;
|
||||
int r = Snprintf(p, end - p, "%s", str);
|
||||
if (r < 0)
|
||||
p = end;
|
||||
else {
|
||||
p += r;
|
||||
if (p > end)
|
||||
p = end;
|
||||
}
|
||||
}
|
||||
void fmt_num(const char *fmt, unsigned int n) {
|
||||
if (p >= end)
|
||||
return;
|
||||
int r = Snprintf(p, end - p, fmt, n);
|
||||
if (r < 0)
|
||||
p = end;
|
||||
else {
|
||||
p += r;
|
||||
if (p > end)
|
||||
p = end;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static bool tcpopt_info(u8 opcode, u8 len, const u8 *data, void *ctx);
|
||||
|
||||
/* Get an ASCII information about a tcp option which is pointed by
|
||||
optp, with a length of len. The result is stored in the result
|
||||
buffer. The result may look like "<mss 1452,sackOK,timestamp
|
||||
|
|
@ -1399,130 +1436,93 @@ const char *proto2ascii_uppercase(u8 proto) {
|
|||
void tcppacketoptinfo(u8 *optp, int len, char *result, int bufsize) {
|
||||
assert(optp);
|
||||
assert(result);
|
||||
char *p, ch;
|
||||
u8 *q;
|
||||
int opcode;
|
||||
u16 tmpshort;
|
||||
u32 tmpword1, tmpword2;
|
||||
unsigned int i=0;
|
||||
assert(bufsize > 0);
|
||||
memset(result, 0, bufsize);
|
||||
|
||||
p = result;
|
||||
*p = '\0';
|
||||
q = optp;
|
||||
ch = '<';
|
||||
TCPOptions opts;
|
||||
if (bufsize < 2 || !opts.fromBuffer(optp, len))
|
||||
return;
|
||||
tcpopt_info_ctx ctx;
|
||||
ctx.p = result;
|
||||
ctx.end = result + bufsize - 1;
|
||||
ctx.put_str("<");
|
||||
|
||||
while (len > 0 && bufsize > 2) {
|
||||
Snprintf(p, bufsize, "%c", ch);
|
||||
bufsize--;
|
||||
p++;
|
||||
opcode = *q++;
|
||||
if (!opcode) { /* End of List */
|
||||
|
||||
Snprintf(p, bufsize, "eol");
|
||||
bufsize -= strlen(p);
|
||||
p += strlen(p);
|
||||
|
||||
len--;
|
||||
|
||||
} else if (opcode == 1) { /* No Op */
|
||||
Snprintf(p, bufsize, "nop");
|
||||
bufsize -= strlen(p);
|
||||
p += strlen(p);
|
||||
|
||||
len--;
|
||||
} else if (opcode == 2) { /* MSS */
|
||||
if (len < 4)
|
||||
break; /* MSS has 4 bytes */
|
||||
|
||||
q++;
|
||||
memcpy(&tmpshort, q, 2);
|
||||
|
||||
Snprintf(p, bufsize, "mss %hu", (unsigned short) ntohs(tmpshort));
|
||||
bufsize -= strlen(p);
|
||||
p += strlen(p);
|
||||
|
||||
q += 2;
|
||||
len -= 4;
|
||||
} else if (opcode == 3) { /* Window Scale */
|
||||
if (len < 3)
|
||||
break; /* Window Scale option has 3 bytes */
|
||||
|
||||
q++;
|
||||
|
||||
Snprintf(p, bufsize, "wscale %u", *q);
|
||||
bufsize -= strlen(p);
|
||||
p += strlen(p);
|
||||
|
||||
q++;
|
||||
len -= 3;
|
||||
} else if (opcode == 4) { /* SACK permitted */
|
||||
if (len < 2)
|
||||
break; /* SACK permitted option has 2 bytes */
|
||||
|
||||
Snprintf(p, bufsize, "sackOK");
|
||||
bufsize -= strlen(p);
|
||||
p += strlen(p);
|
||||
|
||||
q++;
|
||||
len -= 2;
|
||||
} else if (opcode == 5) { /* SACK */
|
||||
unsigned sackoptlen = *q;
|
||||
if ((unsigned) len < sackoptlen)
|
||||
break;
|
||||
|
||||
/* This would break parsing, so it's best to just give up */
|
||||
if (sackoptlen < 2)
|
||||
break;
|
||||
|
||||
q++;
|
||||
|
||||
if ((sackoptlen - 2) == 0 || ((sackoptlen - 2) % 8 != 0)) {
|
||||
Snprintf(p, bufsize, "malformed sack");
|
||||
bufsize -= strlen(p);
|
||||
p += strlen(p);
|
||||
} else {
|
||||
Snprintf(p, bufsize, "sack %d ", (sackoptlen - 2) / 8);
|
||||
bufsize -= strlen(p);
|
||||
p += strlen(p);
|
||||
for (i = 0; i < sackoptlen - 2; i += 8) {
|
||||
memcpy(&tmpword1, q + i, 4);
|
||||
memcpy(&tmpword2, q + i + 4, 4);
|
||||
Snprintf(p, bufsize, "{%u:%u}", tmpword1, tmpword2);
|
||||
bufsize -= strlen(p);
|
||||
p += strlen(p);
|
||||
}
|
||||
}
|
||||
|
||||
q += sackoptlen - 2;
|
||||
len -= sackoptlen;
|
||||
} else if (opcode == 8) { /* Timestamp */
|
||||
if (len < 10)
|
||||
break; /* Timestamp option has 10 bytes */
|
||||
|
||||
q++;
|
||||
memcpy(&tmpword1, q, 4);
|
||||
memcpy(&tmpword2, q + 4, 4);
|
||||
|
||||
Snprintf(p, bufsize, "timestamp %lu %lu", (unsigned long) ntohl(tmpword1),
|
||||
(unsigned long) ntohl(tmpword2));
|
||||
bufsize -= strlen(p);
|
||||
p += strlen(p);
|
||||
|
||||
q += 8;
|
||||
len -= 10;
|
||||
}
|
||||
|
||||
ch = ',';
|
||||
}
|
||||
|
||||
if (len > 0) {
|
||||
*result = '\0';
|
||||
if (!opts.foreachOpt(tcpopt_info, &ctx) || !ctx.valid) {
|
||||
Snprintf(result, bufsize, "<Invalid TCP options>");
|
||||
return;
|
||||
}
|
||||
|
||||
Snprintf(p, bufsize, ">");
|
||||
char *q = ctx.p - 1;
|
||||
if (*q != ',')
|
||||
q++;
|
||||
*q++ = '>';
|
||||
*q++ = '\0';
|
||||
return;
|
||||
}
|
||||
|
||||
static bool tcpopt_info(u8 opcode, u8 len, const u8 *data, void *ctx)
|
||||
{
|
||||
tcpopt_info_ctx *args = static_cast<tcpopt_info_ctx *>(ctx);
|
||||
if (!args->check_length(4))
|
||||
return false;
|
||||
const u8 *q = data + 2;
|
||||
|
||||
switch (opcode) {
|
||||
case 0: /* End of List */
|
||||
args->put_str("eol,");
|
||||
break;
|
||||
case 1: /* No Op */
|
||||
args->put_str("nop,");
|
||||
break;
|
||||
case 2: /* MSS */
|
||||
if (len < 4) {
|
||||
args->valid = false;
|
||||
break; /* MSS has 4 bytes */
|
||||
}
|
||||
args->fmt_num("mss %u,", (q[0] << 8) + q[1]);
|
||||
case 3: /* Window Scale */
|
||||
if (len < 3) {
|
||||
args->valid = false;
|
||||
break; /* Window Scale option has 3 bytes */
|
||||
}
|
||||
args->fmt_num("wscale %u,", q[0]);
|
||||
break;
|
||||
case 4: /* SACK permitted */
|
||||
if (len < 2) {
|
||||
args->valid = false;
|
||||
break; /* SACK permitted option has 2 bytes */
|
||||
}
|
||||
args->put_str("sackOK,");
|
||||
break;
|
||||
case 5: /* SACK */
|
||||
if (len < 2) {
|
||||
args->valid = false;
|
||||
break;
|
||||
}
|
||||
if ((len - 2) == 0 || ((len - 2) % 8 != 0)) {
|
||||
args->put_str("malformed sack,");
|
||||
} else {
|
||||
args->fmt_num("sack %u ", (len - 2) / 8);
|
||||
for (int i = 0; i < len - 2; i += 8) {
|
||||
args->fmt_num("{%u:", (q[i] << 24) + (q[i+1] << 16) + (q[i+2] << 8) + q[i+3]);
|
||||
args->fmt_num("%u}", (q[i+4] << 24) + (q[i+5] << 16) + (q[i+6] << 8) + q[i+7]);
|
||||
}
|
||||
args->put_str(",");
|
||||
}
|
||||
break;
|
||||
case 8: /* Timestamp */
|
||||
if (len < 10) {
|
||||
args->valid = false;
|
||||
break; /* Timestamp option has 10 bytes */
|
||||
}
|
||||
args->fmt_num("timestamp %u ", (q[0] << 24) + (q[1] << 16) + (q[2] << 8) + q[3]);
|
||||
args->fmt_num("%u,", (q[4] << 24) + (q[5] << 16) + (q[6] << 8) + q[7]);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return args->valid;
|
||||
}
|
||||
|
||||
|
||||
/* A trivial function used with qsort to sort the routes by netmask and metric */
|
||||
|
|
|
|||
169
osscan2.cc
169
osscan2.cc
|
|
@ -2684,7 +2684,7 @@ bool HostOsScan::processTOpsResp(HostOsScanStats *hss, const u8 *tcp, int tcplen
|
|||
if (hss->FP_TOps || hss->TOps_AVs[replyNo])
|
||||
return false;
|
||||
|
||||
int opsParseResult = get_tcpopt_string(tcp, tcplen, this->tcpMss, ops_buf, sizeof(ops_buf));
|
||||
int opsParseResult = get_tcpopt_string(tcp, tcplen, ops_buf, sizeof(ops_buf));
|
||||
|
||||
if (opsParseResult <= 0) {
|
||||
if (opsParseResult < 0 && o.debugging)
|
||||
|
|
@ -2740,7 +2740,7 @@ bool HostOsScan::processTEcnResp(HostOsScanStats *hss, const struct ip *ip, cons
|
|||
test.setAVal("W", hss->target->FPR->cp_hex(ntohs(tcp->th_win)));
|
||||
|
||||
/* Now for the TCP options ... */
|
||||
int opsParseResult = get_tcpopt_string(tcppkt, tcplen, this->tcpMss, ops_buf, sizeof(ops_buf));
|
||||
int opsParseResult = get_tcpopt_string(tcppkt, tcplen, ops_buf, sizeof(ops_buf));
|
||||
|
||||
if (opsParseResult <= 0) {
|
||||
if (opsParseResult < 0 && o.debugging)
|
||||
|
|
@ -2880,7 +2880,7 @@ bool HostOsScan::processT1_7Resp(HostOsScanStats *hss, const struct ip *ip, cons
|
|||
char ops_buf[256];
|
||||
|
||||
/* Now for the TCP options ... */
|
||||
int opsParseResult = get_tcpopt_string(tcppkt, tcplen, this->tcpMss, ops_buf, sizeof(ops_buf));
|
||||
int opsParseResult = get_tcpopt_string(tcppkt, tcplen, ops_buf, sizeof(ops_buf));
|
||||
if (opsParseResult <= 0) {
|
||||
if (opsParseResult < 0 && o.debugging)
|
||||
error("Option parse error for T%d response from %s.", replyNo, hss->target->targetipstr());
|
||||
|
|
@ -3136,86 +3136,103 @@ bool HostOsScan::processTIcmpResp(HostOsScanStats *hss, const struct ip *ip, con
|
|||
return true;
|
||||
}
|
||||
|
||||
|
||||
int HostOsScan::get_tcpopt_string(const u8 *tcp, int tcplen, int mss, char *result, int maxlen) const {
|
||||
struct tcpopt_string_ctx {
|
||||
char *p;
|
||||
const u8 *q;
|
||||
u16 tmpshort;
|
||||
u32 tmpword;
|
||||
int length;
|
||||
int opcode;
|
||||
char *end;
|
||||
bool valid;
|
||||
tcpopt_string_ctx() : p(NULL), end(NULL), valid(true) {}
|
||||
bool check_length(size_t len) const {
|
||||
return (end - p) >= len;
|
||||
}
|
||||
void put(char c) {
|
||||
assert(end > p);
|
||||
*p++ = c;
|
||||
}
|
||||
void put_hex(unsigned int u) {
|
||||
int w = sprintf(p, "%X", u);
|
||||
p += w;
|
||||
}
|
||||
};
|
||||
|
||||
p = result;
|
||||
struct tcp_hdr hdr;
|
||||
memcpy(&hdr, tcp, sizeof(hdr));
|
||||
length = MIN(hdr.th_off * 4, tcplen) - sizeof(struct tcp_hdr);
|
||||
q = tcp + sizeof(struct tcp_hdr);
|
||||
static bool tcpopt_tostring(u8 op, u8 oplen, const u8 *data, void *ctx)
|
||||
{
|
||||
tcpopt_string_ctx *args = static_cast<tcpopt_string_ctx *>(ctx);
|
||||
|
||||
if (!args->check_length(1))
|
||||
return false;
|
||||
|
||||
const u8 *q = data + 2;
|
||||
|
||||
switch (op) {
|
||||
case 0: /* End of List */
|
||||
args->put('L');
|
||||
break;
|
||||
case 1: /* No Op */
|
||||
args->put('N');
|
||||
break;
|
||||
case 2: /* MSS */
|
||||
if (oplen < 4) {
|
||||
args->valid = false;
|
||||
break; /* MSS has 4 bytes */
|
||||
}
|
||||
args->put('M');
|
||||
if (!args->check_length(4))
|
||||
return false;
|
||||
args->put_hex((q[0] << 8) + q[1]);
|
||||
break;
|
||||
case 3:/* Window Scale */
|
||||
if (oplen < 3) {
|
||||
args->valid = false;
|
||||
break; /* Window Scale option has 3 bytes */
|
||||
}
|
||||
args->put('W');
|
||||
if (!args->check_length(2))
|
||||
return false;
|
||||
args->put_hex(q[0]);
|
||||
break;
|
||||
case 4:/* SACK permitted */
|
||||
if (oplen < 2) {
|
||||
args->valid = false;
|
||||
break; /* SACK permitted option has 2 bytes */
|
||||
}
|
||||
args->put('S');
|
||||
break;
|
||||
case 8: /* Timestamp */
|
||||
if (oplen < 10) {
|
||||
args->valid = false;
|
||||
break; /* Timestamp option has 10 bytes */
|
||||
}
|
||||
args->put('T');
|
||||
if (!args->check_length(2))
|
||||
return false;
|
||||
args->put((q[0] || q[1] || q[2] || q[3]) ? '1' : '0');
|
||||
args->put((q[4] || q[5] || q[6] || q[7]) ? '1' : '0');
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return args->valid;
|
||||
}
|
||||
|
||||
int HostOsScan::get_tcpopt_string(const u8 *tcp, int tcplen, char *result, int maxlen) const {
|
||||
assert(tcp);
|
||||
assert(result);
|
||||
assert(maxlen > 0);
|
||||
memset(result, 0, maxlen);
|
||||
|
||||
/*
|
||||
* Example parsed result: M5B4ST11NW2
|
||||
* MSS, Sack Permitted, Timestamp with both value not zero, Nop, WScale with value 2
|
||||
*/
|
||||
|
||||
/* Be aware of the max increment value for p in parsing,
|
||||
* now is 5 = strlen("Mxxxx") <-> MSS Option
|
||||
*/
|
||||
while (length > 0 && (p - result) < (maxlen - 5)) {
|
||||
opcode = *q++;
|
||||
if (!opcode) { /* End of List */
|
||||
*p++ = 'L';
|
||||
length--;
|
||||
} else if (opcode == 1) { /* No Op */
|
||||
*p++ = 'N';
|
||||
length--;
|
||||
} else if (opcode == 2) { /* MSS */
|
||||
if (length < 4)
|
||||
break; /* MSS has 4 bytes */
|
||||
*p++ = 'M';
|
||||
q++;
|
||||
memcpy(&tmpshort, q, 2);
|
||||
/* if (ntohs(tmpshort) == mss) */
|
||||
/* *p++ = 'E'; */
|
||||
sprintf(p, "%hX", ntohs(tmpshort));
|
||||
p += strlen(p); /* max movement of p is 4 (0xFFFF) */
|
||||
q += 2;
|
||||
length -= 4;
|
||||
} else if (opcode == 3) { /* Window Scale */
|
||||
if (length < 3)
|
||||
break; /* Window Scale option has 3 bytes */
|
||||
*p++ = 'W';
|
||||
q++;
|
||||
snprintf(p, length, "%hhX", *((u8*)q));
|
||||
p += strlen(p); /* max movement of p is 2 (max WScale value is 0xFF) */
|
||||
q++;
|
||||
length -= 3;
|
||||
} else if (opcode == 4) { /* SACK permitted */
|
||||
if (length < 2)
|
||||
break; /* SACK permitted option has 2 bytes */
|
||||
*p++ = 'S';
|
||||
q++;
|
||||
length -= 2;
|
||||
} else if (opcode == 8) { /* Timestamp */
|
||||
if (length < 10)
|
||||
break; /* Timestamp option has 10 bytes */
|
||||
*p++ = 'T';
|
||||
q++;
|
||||
memcpy(&tmpword, q, 4);
|
||||
if (tmpword)
|
||||
*p++ = '1';
|
||||
else
|
||||
*p++ = '0';
|
||||
q += 4;
|
||||
memcpy(&tmpword, q, 4);
|
||||
if (tmpword)
|
||||
*p++ = '1';
|
||||
else
|
||||
*p++ = '0';
|
||||
q += 4;
|
||||
length -= 10;
|
||||
}
|
||||
}
|
||||
TCPOptions opts;
|
||||
if (!opts.fromTCPPacket(tcp, tcplen))
|
||||
return -1;
|
||||
tcpopt_string_ctx ctx;
|
||||
ctx.p = result;
|
||||
ctx.end = result + maxlen - 1;
|
||||
|
||||
if (length > 0) {
|
||||
if (!opts.foreachOpt(tcpopt_tostring, &ctx) || !ctx.valid) {
|
||||
/* We could reach here for one of the two reasons:
|
||||
* 1. At least one option is not correct. (Eg. Should have 4 bytes but only has 3 bytes left).
|
||||
* 2. The option string is too long.
|
||||
|
|
@ -3223,9 +3240,7 @@ int HostOsScan::get_tcpopt_string(const u8 *tcp, int tcplen, int mss, char *resu
|
|||
*result = '\0';
|
||||
return -1;
|
||||
}
|
||||
|
||||
*p = '\0';
|
||||
return p - result;
|
||||
return ctx.p - result;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -429,7 +429,7 @@ private:
|
|||
void makeTOpsFP(HostOsScanStats *hss);
|
||||
void makeTWinFP(HostOsScanStats *hss);
|
||||
|
||||
int get_tcpopt_string(const u8 *tcp, int tcplen, int mss, char *result, int maxlen) const;
|
||||
int get_tcpopt_string(const u8 *tcp, int tcplen, char *result, int maxlen) const;
|
||||
|
||||
int rawsd; /* Raw socket descriptor */
|
||||
netutil_eth_t *ethsd; /* Ethernet handle */
|
||||
|
|
|
|||
83
tcpip.cc
83
tcpip.cc
|
|
@ -73,6 +73,7 @@
|
|||
#include "utils.h"
|
||||
#include "nmap_error.h"
|
||||
#include "libnetutil/netutil.h"
|
||||
#include "libnetutil/TCPHeader.h"
|
||||
|
||||
#include "struct_ip.h"
|
||||
|
||||
|
|
@ -1625,6 +1626,33 @@ int recvtime(int sd, char *buf, int len, int seconds, int *timedout) {
|
|||
return -1;
|
||||
}
|
||||
|
||||
struct getTS_args {
|
||||
u32 *timestamp;
|
||||
u32 *echots;
|
||||
int found;
|
||||
getTS_args() : timestamp(NULL), echots(NULL), found(0) {}
|
||||
};
|
||||
|
||||
static bool tcpopt_ts_cb(u8 op, u8 oplen, const u8 *data, void *ctx)
|
||||
{
|
||||
if (op == 8 /* TCPOPT_TIMESTAMP */ && oplen == 10) {
|
||||
getTS_args *args = static_cast<getTS_args *>(ctx);
|
||||
const u8 *p = data + 2;
|
||||
/* Legitimate ts option */
|
||||
if (args->timestamp) {
|
||||
*args->timestamp = (p[0] << 24) + (p[1] << 16) + (p[2] << 8) + p[3];
|
||||
}
|
||||
if (args->echots) {
|
||||
*args->echots = (p[4] << 24) + (p[5] << 16) + (p[6] << 8) + p[7];
|
||||
}
|
||||
// done!
|
||||
args->found = 1;
|
||||
return false;
|
||||
}
|
||||
// Keep looking
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Examines the given tcp packet and obtains the TCP timestamp option
|
||||
information if available. Note that the CALLER must ensure that
|
||||
"tcp" contains a valid header (in particular the th_off must be the
|
||||
|
|
@ -1636,52 +1664,19 @@ int recvtime(int sd, char *buf, int len, int seconds, int *timedout) {
|
|||
correct way to check for errors is to look at the return value
|
||||
since a zero ts or echots could possibly be valid. */
|
||||
int gettcpopt_ts(const u8 *tcppkt, int tcplen, u32 *timestamp, u32 *echots) {
|
||||
|
||||
const u8 *p;
|
||||
int len = 0;
|
||||
int op;
|
||||
int oplen;
|
||||
struct tcp_hdr tcp;
|
||||
assert(tcplen >= sizeof(tcp));
|
||||
memcpy(&tcp, tcppkt, sizeof(tcp));
|
||||
|
||||
/* first we find where the tcp options start ... */
|
||||
p = tcppkt + 20;
|
||||
len = MIN(4 * tcp.th_off, tcplen) - 20;
|
||||
while (len > 0 && *p != 0 /* TCPOPT_EOL */ ) {
|
||||
op = *p++;
|
||||
if (op == 0 /* TCPOPT_EOL */ )
|
||||
break;
|
||||
if (op == 1 /* TCPOPT_NOP */ ) {
|
||||
len--;
|
||||
continue;
|
||||
}
|
||||
oplen = *p++;
|
||||
if (oplen < 2)
|
||||
break; /* No infinite loops, please */
|
||||
if (oplen > len)
|
||||
break; /* Not enough space */
|
||||
if (op == 8 /* TCPOPT_TIMESTAMP */ && oplen == 10) {
|
||||
/* Legitimate ts option */
|
||||
if (timestamp) {
|
||||
memcpy((char *) timestamp, p, 4);
|
||||
*timestamp = ntohl(*timestamp);
|
||||
}
|
||||
p += 4;
|
||||
if (echots) {
|
||||
memcpy((char *) echots, p, 4);
|
||||
*echots = ntohl(*echots);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
len -= oplen;
|
||||
p += oplen - 2;
|
||||
}
|
||||
|
||||
/* Didn't find anything */
|
||||
if (timestamp)
|
||||
*timestamp = 0;
|
||||
if (echots)
|
||||
*echots = 0;
|
||||
return 0;
|
||||
|
||||
TCPOptions opts;
|
||||
if (!opts.fromTCPPacket(tcppkt, tcplen))
|
||||
return 0;
|
||||
|
||||
getTS_args args;
|
||||
args.timestamp = timestamp;
|
||||
args.echots = echots;
|
||||
|
||||
opts.foreachOpt(tcpopt_ts_cb, &args);
|
||||
return args.found;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue