mirror of
https://github.com/nmap/nmap.git
synced 2025-12-15 04:09:01 +00:00
Add -ip-options support
This commit is contained in:
355
utils.cc
355
utils.cc
@@ -99,6 +99,7 @@
|
||||
|
||||
/* $Id$ */
|
||||
|
||||
#include "nmap.h"
|
||||
#include "utils.h"
|
||||
#include "NmapOps.h"
|
||||
|
||||
@@ -559,6 +560,360 @@ char *cstring_unescape(char *str, unsigned int *newlen) {
|
||||
return str;
|
||||
}
|
||||
|
||||
/* This function converts zero-terminated 'txt' string to binary 'data'.
|
||||
It is used to parse user input for ip options. Some examples of possible input
|
||||
strings and results:
|
||||
'\x01*2\xA2' -> [0x01,0x01,0xA2] // with 'x' number is parsed in hex
|
||||
'\01\01\255' -> [0x01,0x01,0xFF] // without 'x' its in decimal
|
||||
'\x01\x00*2' -> [0x01,0x00,0x00] // '*' is copying char
|
||||
'R' -> Record Route with 9 slots
|
||||
'S 192.168.0.1 172.16.0.1' -> Strict Route with 2 slots
|
||||
'L 192.168.0.1 172.16.0.1' -> Loose Route with 2 slots
|
||||
'T' -> Record Timestamp with 9 slots
|
||||
'U' -> Record Timestamp and Ip Address with 4 slots
|
||||
*/
|
||||
int parse_ip_options(char *txt, u8 *data, int datalen, int* firsthopoff, int* lasthopoff){
|
||||
enum{
|
||||
NONE = 0,
|
||||
SLASH = 1,
|
||||
MUL = 2,
|
||||
RR = 3,
|
||||
TIME = 4,
|
||||
} s = NONE;
|
||||
char *n, lc;
|
||||
char *c = txt;
|
||||
u8 *d = data;
|
||||
int i,j;
|
||||
int base = 10;
|
||||
u8 *dataend = &data[datalen];
|
||||
u8 *len = NULL;
|
||||
char buf[32];
|
||||
memset(data, 0, datalen);
|
||||
bool sourcerouting = false;
|
||||
|
||||
|
||||
for(;*c;c++){
|
||||
switch(s){
|
||||
case SLASH:
|
||||
// parse \x00 string
|
||||
if(*c == 'x'){// just ignore this char
|
||||
base = 16;
|
||||
break;
|
||||
}
|
||||
if(isxdigit(*c)){
|
||||
*d++ = strtol(c, &n, base);
|
||||
c=n-1;
|
||||
}else
|
||||
fatal("not a digit after '\\'");
|
||||
s = NONE;
|
||||
break;
|
||||
case MUL:
|
||||
if(d==data)
|
||||
fatal("nothing before '*' char");
|
||||
i = strtol(c, &n, 10);
|
||||
if(i<2)
|
||||
fatal("bad number after '*'");
|
||||
c = n-1; // move current txt pointer
|
||||
lc = *(d-1); // last char, we'll copy this
|
||||
for(j=1; j<i; j++){
|
||||
*d++ = lc;
|
||||
if(d == dataend) // check for overflow
|
||||
goto after;
|
||||
}
|
||||
s = NONE;
|
||||
break;
|
||||
case RR:
|
||||
if(*c==' ' || *c==',')
|
||||
break;
|
||||
n = buf;
|
||||
while(*c=='.' || (*c>='0' && *c<='9') && n-buf <= ((int)sizeof(buf)-1))
|
||||
*n++ = *c++;
|
||||
*n = '\0'; c--;
|
||||
if(d+4>=dataend)
|
||||
fatal("Buffer too small. Or input data too big :)");
|
||||
i = inet_pton(AF_INET, buf, d);
|
||||
if(i<1)
|
||||
fatal("Not a valid ipv4 address '%s'",buf);
|
||||
// remember offset of first hop
|
||||
if(sourcerouting && !*firsthopoff)
|
||||
*firsthopoff = d - data;
|
||||
d+=4;
|
||||
if(*len<37)
|
||||
*len += 4;
|
||||
break;
|
||||
case TIME:
|
||||
fatal("No more arguments allowed!");
|
||||
default:
|
||||
switch(*c){
|
||||
case '\\':s = SLASH;base=10;break;
|
||||
case '*':s = MUL;break;
|
||||
case 'R':
|
||||
case 'S':
|
||||
case 'L':
|
||||
if(d != data)
|
||||
fatal("This option can't be used in that way");
|
||||
*d++ = '\x01';//NOP
|
||||
switch(*c){
|
||||
case 'R':*d++ = 7;break;
|
||||
case 'S':*d++ = 137; sourcerouting=true; break;
|
||||
case 'L':*d++ = 131; sourcerouting=true; break;
|
||||
}
|
||||
len = d;
|
||||
*d++ = (*c=='R')? 39 : 3; // length: 3+4*9 bytes
|
||||
*d++ = 4; //pointer
|
||||
s = RR;
|
||||
break;
|
||||
case 'T':
|
||||
case 'U':
|
||||
if(d != data)
|
||||
fatal("This option can't be used in that way");
|
||||
*d++ = 68; // option type
|
||||
len = d;
|
||||
*d++ = (*c=='U') ? 36 : 40; // length: 3+4*9 bytes or 4+4*9 bytes
|
||||
*d++ = 5; // pointer
|
||||
*d++ = (*c=='U') ? 1 : 0; // flag: address and Time fields
|
||||
s = TIME;
|
||||
break;
|
||||
default://*d++ = *c;
|
||||
fatal("Bad character in ip option '%c'",*c);
|
||||
}
|
||||
}
|
||||
if(d == dataend)
|
||||
break;
|
||||
assert(d<dataend);
|
||||
}
|
||||
if(sourcerouting){
|
||||
if(*len<37){
|
||||
*len+=4;
|
||||
*lasthopoff = d - data;
|
||||
*d++ = 0;*d++ = 0;*d++ = 0;*d++ = 0;
|
||||
}else
|
||||
fatal("When using source routing you must leave at least one slot for target's ip.");
|
||||
}
|
||||
if(s == RR)
|
||||
return(*len+1); // because we inject NOP before
|
||||
if(s == TIME)
|
||||
return(*len);
|
||||
after:
|
||||
return(d - data);
|
||||
}
|
||||
|
||||
void bintohexstr(char *buf, int buflen, char *src, int srclen){
|
||||
int bp=0;
|
||||
int i;
|
||||
for(i=0; i<srclen; i++){
|
||||
bp += snprintf(buf+bp, buflen-bp, "\\x%02hhx",src[i]);
|
||||
if(bp >= buflen)break;
|
||||
if(i%16==7){
|
||||
bp += snprintf(buf+bp, buflen-bp," ");
|
||||
if(bp >= buflen)break;
|
||||
}
|
||||
if(i%16==15){
|
||||
bp += snprintf(buf+bp, buflen-bp,"\n");
|
||||
if(bp >= buflen)break;
|
||||
}
|
||||
}
|
||||
if(i%16!=0 && bp < buflen)
|
||||
bp += snprintf(buf+bp, buflen-bp,"\n");
|
||||
}
|
||||
|
||||
static inline char* STRAPP(char *fmt, ...) {
|
||||
static char buf[256];
|
||||
static int bp;
|
||||
int left = (int)sizeof(buf)-bp;
|
||||
if(!fmt){
|
||||
bp = 0;
|
||||
return(buf);
|
||||
}
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
bp += vsnprintf (buf+bp, (left>0 ? left : 0), fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
return(buf);
|
||||
}
|
||||
|
||||
#define HEXDUMP -2
|
||||
#define UNKNOWN -1
|
||||
|
||||
#define BREAK() \
|
||||
{option_type = HEXDUMP; break;}
|
||||
#define CHECK(tt) \
|
||||
if(tt >= option_end) \
|
||||
{option_type = HEXDUMP; break;}
|
||||
/* It tries to decode ip options.
|
||||
Returns static buffer. watch out. */
|
||||
char* print_ip_options(u8* ipopt, int ipoptlen) {
|
||||
char ipstring[32];
|
||||
int option_type = UNKNOWN;// option type
|
||||
int option_len = 0; // option length
|
||||
int option_pt = 0; // option pointer
|
||||
int option_fl = 0; // option flag
|
||||
u8 *tptr; // temp pointer
|
||||
u32 *tint; // temp int
|
||||
|
||||
int option_sta = 0; // option start offset
|
||||
int option_end = 0; // option end offset
|
||||
int pt = 0; // current offset
|
||||
|
||||
// clear buffer
|
||||
STRAPP(NULL,NULL);
|
||||
|
||||
if(!ipoptlen)
|
||||
return(NULL);
|
||||
|
||||
while(pt<ipoptlen){ // for every char in ipopt
|
||||
// read ip option header
|
||||
if(option_type == UNKNOWN) {
|
||||
option_sta = pt;
|
||||
option_type = ipopt[pt++];
|
||||
if(option_type != 0 && option_type != 1) { // should we be interested in length field?
|
||||
if(pt >= ipoptlen) // no more chars
|
||||
{option_type = HEXDUMP;pt--; option_end = 255; continue;} // no length field, hex dump to the end
|
||||
option_len = ipopt[pt++];
|
||||
// end must not be greater than length
|
||||
option_end = MIN(option_sta + option_len, ipoptlen);
|
||||
// end must not be smaller than current position
|
||||
option_end = MAX(option_end, option_sta+2);
|
||||
}
|
||||
}
|
||||
switch(option_type) {
|
||||
case 0: // IPOPT_END
|
||||
STRAPP(" EOL", NULL);
|
||||
option_type = UNKNOWN;
|
||||
break;
|
||||
case 1: // IPOPT_NOP
|
||||
STRAPP(" NOP", NULL);
|
||||
option_type = UNKNOWN;
|
||||
break;
|
||||
/* case 130: // IPOPT_SECURITY
|
||||
option_type=-1;
|
||||
break;*/
|
||||
case 131: // IPOPT_LSRR -> Loose Source and Record Route
|
||||
case 137: // IPOPT_SSRR -> Strict Source and Record Route
|
||||
case 7: // IPOPT_RR -> Record Route
|
||||
if(pt - option_sta == 2) {
|
||||
STRAPP(" %s%s{", (option_type==131)?"LS":(option_type==137)?"SS":"", "RR");
|
||||
// option pointer
|
||||
CHECK(pt);
|
||||
option_pt = ipopt[pt++];
|
||||
if(option_pt%4 != 0 || (option_sta + option_pt-1)>option_end || option_pt<4) //bad or too big pointer
|
||||
STRAPP(" [bad ptr=%02i]", option_pt);
|
||||
}
|
||||
if(pt - option_sta > 2) { // ip's
|
||||
int i, s = (option_pt)%4;
|
||||
// if pointer is mangled, fix it. it's max 3 bytes wrong
|
||||
CHECK(pt+3);
|
||||
for(i=0; i<s; i++)
|
||||
STRAPP("\\x%02x", ipopt[pt++]);
|
||||
option_pt -= i;
|
||||
// okay, now we can start printing ip's
|
||||
CHECK(pt+3);
|
||||
tptr = &ipopt[pt]; pt+=4;
|
||||
if(inet_ntop(AF_INET, (char *) tptr, ipstring, sizeof(ipstring)) == NULL)
|
||||
fatal("Failed to convert target address to presentation format!?! Error: %s", strerror(socket_errno()));
|
||||
STRAPP("%c%s",(pt-3-option_sta)==option_pt?'#':' ', ipstring);
|
||||
if(pt == option_end)
|
||||
STRAPP("%s",(pt-option_sta)==(option_pt-1)?"#":""); // pointer in the end?
|
||||
}else BREAK();
|
||||
break;
|
||||
case 68: // IPOPT_TS -> Internet Timestamp
|
||||
if(pt - option_sta == 2){
|
||||
STRAPP(" TM{");
|
||||
// pointer
|
||||
CHECK(pt);
|
||||
option_pt = ipopt[pt++];
|
||||
// bad or too big pointer
|
||||
if(option_pt%4 != 1 || (option_sta + option_pt-1)>option_end || option_pt<5)
|
||||
STRAPP(" [bad ptr=%02i]", option_pt);
|
||||
// flags + overflow
|
||||
CHECK(pt);
|
||||
option_fl = ipopt[pt++];
|
||||
if((option_fl&0x0C) || (option_fl&0x03)==2)
|
||||
STRAPP(" [bad flags=\\x%01hhx]", option_fl&0x0F);
|
||||
STRAPP("[%i hosts not recorded]", option_fl>>4);
|
||||
option_fl &= 0x03;
|
||||
}
|
||||
if(pt - option_sta > 2) {// ip's
|
||||
int i, s = (option_pt+3)%(option_fl==0?4:8);
|
||||
// if pointer is mangled, fix it. it's max 3 bytes wrong
|
||||
CHECK(pt+(option_fl==0?3:7));
|
||||
for(i=0; i<s; i++)
|
||||
STRAPP("\\x%02x", ipopt[pt++]);
|
||||
option_pt-=i;
|
||||
|
||||
// print pt
|
||||
STRAPP("%c",(pt+1-option_sta)==option_pt?'#':' ');
|
||||
// okay, first grab ip.
|
||||
if(option_fl!=0){
|
||||
CHECK(pt+3);
|
||||
tptr = &ipopt[pt]; pt+=4;
|
||||
if(inet_ntop(AF_INET, (char *) tptr, ipstring, sizeof(ipstring)) == NULL)
|
||||
fatal("Failed to convert target address to presentation format!?! Error: %s", strerror(socket_errno()));
|
||||
STRAPP("%s@", ipstring);
|
||||
}
|
||||
CHECK(pt+3);
|
||||
tint = (u32*)&ipopt[pt]; pt+=4;
|
||||
STRAPP("%u", ntohl(*tint));
|
||||
|
||||
if(pt == option_end)
|
||||
STRAPP("%s",(pt-option_sta)==(option_pt-1)?"#":" ");
|
||||
}else BREAK();
|
||||
break;
|
||||
case 136: // IPOPT_SATID -> (SANET) Stream Identifier
|
||||
if(pt - option_sta == 2){
|
||||
u16 *sh;
|
||||
STRAPP(" SI{",NULL);
|
||||
// length
|
||||
if(option_sta+option_len > ipoptlen || option_len!=4)
|
||||
STRAPP("[bad len %02i]", option_len);
|
||||
|
||||
// stream id
|
||||
CHECK(pt+1);
|
||||
sh = (u16*) &ipopt[pt]; pt+=2;
|
||||
option_pt = ntohs(*sh);
|
||||
STRAPP("id=%i", option_pt);
|
||||
if(pt != option_end)
|
||||
BREAK();
|
||||
}else BREAK();
|
||||
break;
|
||||
case UNKNOWN:
|
||||
default:
|
||||
// we read option_type and option_len, print them.
|
||||
STRAPP(" ??{\\x%02hhx\\x%02hhx", option_type, option_len);
|
||||
// check option_end once more:
|
||||
if(option_len < ipoptlen)
|
||||
option_end = MIN(MAX(option_sta+option_len, option_sta+2),ipoptlen);
|
||||
else
|
||||
option_end = 255;
|
||||
option_type = HEXDUMP;
|
||||
break;
|
||||
case HEXDUMP:
|
||||
assert(pt<=option_end);
|
||||
if(pt == option_end){
|
||||
STRAPP("}",NULL);
|
||||
option_type=-1;
|
||||
break;
|
||||
}
|
||||
STRAPP("\\x%02hhx", ipopt[pt++]);
|
||||
break;
|
||||
}
|
||||
if(pt == option_end && option_type != UNKNOWN) {
|
||||
STRAPP("}",NULL);
|
||||
option_type = UNKNOWN;
|
||||
}
|
||||
} // while
|
||||
if(option_type != UNKNOWN)
|
||||
STRAPP("}");
|
||||
|
||||
return(STRAPP("",NULL));
|
||||
}
|
||||
#undef CHECK
|
||||
#undef BREAK
|
||||
#undef UNKNOWN
|
||||
#undef HEXDUMP
|
||||
|
||||
|
||||
/* mmap() an entire file into the address space. Returns a pointer
|
||||
to the beginning of the file. The mmap'ed length is returned
|
||||
inside the length parameter. If there is a problem, NULL is
|
||||
|
||||
Reference in New Issue
Block a user