1
0
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:
fyodor
2006-08-29 03:26:00 +00:00
parent 426a6d36db
commit 9cbae88f44
13 changed files with 769 additions and 244 deletions

355
utils.cc
View File

@@ -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