Merge pull request #56 from wuruilong01/master
[prads.git] / src / dhcp.c
blobde261cec7bb75db7a5238bf08169ec03d4407e49
1 #include "prads.h"
2 #include "config.h"
3 #include "sys_func.h"
4 #include "dhcp.h"
6 extern globalconfig config;
8 static int parse_dhcp_sig_opts(dhcp_fp_entry *sig, char* p);
9 static int parse_dhcp_sig_optreq(dhcp_fp_entry *sig, char* p);
10 static dhcp_fp_entry *alloc_dhcp_sig(dhcp_fp_entry *e);
11 static void free_dhcp_sigs(dhcp_fp_entry *e);
13 static const unsigned char vendcookie[] = { 99, 130, 83, 99 };
14 #define BOOTP_COOKIE_SIZE 4
15 #define PKT_MAXPAY 16
17 void print_dhcp_header(dhcp_header *dhcph)
19 plog("OP:%d\n",dhcph->op);
20 plog("HTYPE:%d\n",dhcph->htype);
21 plog("HLEN:%d\n",dhcph->hlen);
22 plog("HOPS:%d\n",dhcph->hops);
23 plog("XID:%d\n",dhcph->xid);
24 plog("SECS:%d\n",dhcph->secs);
25 plog("FLAGS:%d\n",dhcph->flags);
27 char dest[INET_ADDRSTRLEN];
28 inet_ntop(AF_INET,&(dhcph->ciaddr),dest,INET_ADDRSTRLEN + 1);
29 plog("CIP:%s\n",dest);
30 inet_ntop(AF_INET,&(dhcph->yiaddr),dest,INET_ADDRSTRLEN + 1);
31 plog("YIP:%s\n",dest);
32 inet_ntop(AF_INET,&(dhcph->siaddr),dest,INET_ADDRSTRLEN + 1);
33 plog("SIP:%s\n",dest);
34 inet_ntop(AF_INET,&(dhcph->giaddr),dest,INET_ADDRSTRLEN + 1);
35 plog("GIP:%s\n",dest);
36 plog("CHADDR:");
37 uint8_t i;
38 for(i = 0; i < 6; i++){
39 printf("%02hhx", dhcph->chaddr[i]);
40 if (i != dhcph->hlen-1)
41 printf(":");
43 plog("\n");
44 plog("SNAME:%s\n",dhcph->sname);
45 plog("FILE:%s\n",dhcph->file);
49 dhcp_fp_entry *dhcp_fingerprint(packetinfo *pi)
51 plog("Got DHCP packet:\n");
52 config.pr_s.dhcp_os_assets++;
54 uint8_t dhcp_header_length;
55 uint8_t *dhcp_mc;
56 uint8_t *dhcp_options;
57 uint8_t optlen = 0;
58 uint8_t dhcp_opt_type = 0;
59 uint8_t end_opt_parsing = 0;
62 dhcp_header *dhcph;
63 dhcph = (dhcp_header *) (pi->payload);
64 print_dhcp_header(dhcph);
66 dhcp_header_length = sizeof(dhcp_header);
67 dhcp_mc = (uint8_t *) (pi->payload + dhcp_header_length);
69 /* TODO: check and bail if not there */
70 //plog("Magic Cookie: %d%d%d%d\n", *dhcp_mc, *(dhcp_mc+1), *(dhcp_mc+2), *(dhcp_mc+3)); // 99 130 83 99
72 dhcp_options = (uint8_t *) dhcp_mc + BOOTP_COOKIE_SIZE;
73 uint8_t *optptr = dhcp_options;
74 uint8_t max_len = (pi->plen - dhcp_header_length - BOOTP_COOKIE_SIZE);
76 dhcp_fp_entry dhcpfp = {0}; //guarantee it's empty this sig
77 dhcpfp.ttl = pi->ip4->ip_ttl;
80 uint8_t optcnt = 0;
82 while (optlen < max_len) {
83 uint8_t i;
85 uint8_t opt = *(optptr);
86 uint8_t optsize = *(optptr+1);
87 uint8_t *optdata = optptr+2;
89 dhcpfp.opt[optcnt] = opt;
91 switch(opt) {
92 case DHCP_OPTION_TYPE: /* 53 */
93 if (optsize == 1) {
94 dhcp_opt_type = *optdata;
95 dhcpfp.type = dhcp_opt_type;
97 break;
98 case DHCP_OPTION_OPTIONREQ: /* 55 */
99 if (optsize > 0) {
100 uint8_t optreqcnt = 0;
101 for (i=2; i < optsize+2; i++) {
102 dhcpfp.optreq[optreqcnt] = *(optptr+i);
103 optreqcnt++;
105 dhcpfp.optreqcnt = optreqcnt;
107 break;
108 case DHCP_OPTION_CLASS_IDENTIFIER: /* 60 */
109 if (optsize > 0) {
110 dhcpfp.vc = calloc(1, optsize + 1);
111 strncpy(dhcpfp.vc, (char*) optdata, optsize);
113 break;
114 case DHCP_OPTION_PAD: /* 0 */
115 break;
116 case DHCP_OPTION_END: /* 255 */
117 end_opt_parsing = 1;
118 optcnt++;
119 break;
120 default:
121 break;
124 optptr = optptr + optsize + 2;
126 optlen = optlen + optsize + 2;
128 if (end_opt_parsing == 1) break;
130 /* Just to be sure */
131 if (*(optptr) != DHCP_OPTION_END) {
132 if (optptr + *(optptr+1) + 2 > pi->payload + pi->plen) break;
134 optcnt++;
136 dhcpfp.optcnt = optcnt;
138 print_dhcp_sig(&dhcpfp);
139 plog("\n");
141 dhcp_fp_entry *match = find_dhcp_match(&dhcpfp, pi);
143 #define OS_DHCP = 0x01
144 //update_asset_os(pi, OS_DHCP, NULL, &dhcpfp, tstamp);
145 return match;
148 dhcp_fp_entry *find_dhcp_match(dhcp_fp_entry *dhcpfp, packetinfo *pi)
150 dhcp_fp_entry* p;
151 uint32_t j;
153 //uint32_t hashsize; // = config.sig_hashsize;
155 //if(!hashsize)
156 // hashsize = DHCP_SIG_HASHSIZE;
158 uint32_t index;
160 index = (DHCP_SIGHASH(dhcpfp->type, dhcpfp->optcnt) % 331);
162 p = config.sig_dhcp[index];
163 while (p) {
164 /* Cheap and specific checks first... */
165 if (dhcpfp->type ^ p->type) { p = p->next; continue; }
166 if (dhcpfp->optcnt ^ p->optcnt) { p = p->next; continue; }
167 if (dhcpfp->optreqcnt ^ p->optreqcnt) { p = p->next; continue; }
169 /* Numbers agree. Let's check options 53 first */
170 if (dhcpfp->optreqcnt != 0) {
171 for (j=0;j<dhcpfp->optreqcnt;j++){
172 if (p->optreq[j] ^ dhcpfp->optreq[j]) goto continue_search;
176 /* Let's check options */
177 if (dhcpfp->optcnt != 0) {
178 for (j=0;j<dhcpfp->optcnt;j++){
179 if (p->opt[j] ^ dhcpfp->opt[j]) goto continue_search;
183 /* Numbers agree. Lets match Vendor Code */
184 if (p->vc != NULL && dhcpfp->vc != NULL) {
185 if (strcmp(p->vc, dhcpfp->vc) == 0) {
186 /* Huston - we have a match */
187 plog("[*] We have a match (");
188 print_dhcp_sig(p);
189 plog(")\n");
190 //plog("OS: %s\n",p->os);
191 //plog("DESC: %s\n",p->desc);
192 break;
194 } else {
195 /* Huston - we have a match */
196 plog("[*] We have a match (");
197 print_dhcp_sig(p);
198 plog(")\n");
199 //plog("OS: %s, ",p->os);
200 //plog("DESC: %s)\n",p->desc);
201 break;
204 continue_search:
205 p = p->next;
207 return dhcpfp;
210 void print_data(const uint8_t* data, uint16_t dlen) {
211 uint8_t tbuf[PKT_MAXPAY+2];
212 uint8_t* t = tbuf;
213 uint8_t i;
214 uint8_t max = dlen > PKT_MAXPAY ? PKT_MAXPAY : dlen;
216 if (!dlen) return;
218 for (i=0;i<max;i++) {
219 if (isprint(*data)) *(t++) = *data;
220 else if (!*data) *(t++) = '?';
221 else *(t++) = '.';
222 data++;
225 *t = 0;
227 plog("%s",tbuf);
230 int load_dhcp_sigs(const char *file, dhcp_fp_entry **dhcpsp[], int hashsize)
232 // sigp == dhcpsp
233 dhcp_fp_entry **sig; // output
234 uint32_t ln = 0;
235 FILE *f = fopen(file, "r");
236 char buf[MAXLINE];
237 char *p;
238 if (!f) {
239 perror("failed to open file");
240 return errno;
242 if(!dhcpsp){
243 fclose(f);
244 perror("need a pointer to fill");
245 return -1;
247 if(!hashsize)
248 hashsize = DHCP_SIG_HASHSIZE;
249 if(*dhcpsp == NULL){
250 *dhcpsp = calloc(hashsize, sizeof(dhcp_fp_entry*));
251 sig = *dhcpsp;
254 while ((p = fgets(buf, sizeof(buf), f))) {
255 uint32_t l;
257 char opts[MAXLINE], optreq[MAXLINE], genre[MAXLINE], desc[MAXLINE];
258 char vc[MAXLINE];
259 char *gptr = genre;
260 uint32_t t, type;
261 dhcp_fp_entry asig = {0}; //guarantee it's empty this sig
262 dhcp_fp_entry *e;
264 ln++;
266 /* Remove leading and trailing blanks */
267 while (isspace(*p))
268 p++;
269 l = strlen(p);
270 while (l && isspace(*(p + l - 1)))
271 *(p + (l--) - 1) = 0;
273 /* Skip empty lines and comments */
274 if (!l)
275 continue;
276 if (*p == '#')
277 continue;
279 /* T4:64:1:60:M*,S,T,N,W7:.:Linux:2.6 (newer, 7) */
280 /* 53-OPTION:TTL:ALL-OPTIONS:55-OPTIONS:60-OPTIONS:OS:OS Details */
281 /* 1:128:53,116,61,12,60,55,43:1,15,3,6,44,46,47,31,33,121,249,43:MSFT 5.0:Windows:Windows XP SP3 */
282 if (sscanf
283 (p, "%d:%d:%[^:]:%[^:]:%[^:]:%[^:]:%[^:]",
284 &type, &t, opts, optreq, vc, genre, desc) != 7)
285 fatal("Syntax error in config line %d.\n", ln);
287 gptr = genre;
289 asig.type = type;
290 asig.ttl = t;
291 asig.os = strdup(gptr);
292 asig.desc = strdup(desc);
293 asig.vc = strdup(vc);
294 asig.line = ln;
296 parse_dhcp_sig_opts(&asig, opts);
297 parse_dhcp_sig_optreq(&asig, optreq);
299 uint32_t index = (DHCP_SIGHASH(asig.type, asig.optcnt) % 331);
300 e = sig[index];
302 if (!e) {
303 sig[index] = alloc_dhcp_sig(&asig);
304 } else {
305 int cc = 0;
306 // collision!
307 while (e->next){
308 e = e->next;
309 cc++;
312 fprintf(stderr, "hash collision: %s\n", p);
314 e->next = alloc_dhcp_sig(&asig);
318 fclose(f);
319 #ifdef DUMP_SIG_HASH
321 int i;
322 for (i = 0; i < sigcnt; i++) {
323 print_dhcp_sig(&sig[i]);
326 #endif
327 #ifdef DEBUG_HASH
329 int i;
330 dhcp_fp_entry *p;
331 printf("DHCP hash table layout: ");
332 for (i = 0; i < hashsize; i++) {
333 int z = 0;
334 p = sig[i];
335 while (p) {
336 p = p->next;
337 z++;
339 printf("%d ", z);
341 putchar('\n');
343 #endif /* DEBUG_HASH */
345 // if (check_collide)
346 // debug("[+] DHCP signature collision check successful.\n");
348 return 0;
351 /* run through the hash, free entries, then free hash */
352 void unload_dhcp_sigs(dhcp_fp_entry **sigp, int size)
354 int i = size;
355 dhcp_fp_entry *e;
356 while(i--){
357 e = sigp[i];
358 if (e)
359 free_dhcp_sigs(e);
360 sigp[i] = NULL; // clear
362 free(*sigp);
363 *sigp = NULL;
366 /* alloc_sig return a newly allocated copy of *e */
367 static dhcp_fp_entry *alloc_dhcp_sig(dhcp_fp_entry *e)
369 dhcp_fp_entry *n = calloc(1, sizeof(dhcp_fp_entry));
370 *n = *e; // copy
371 return n;
374 /* recursively free signatures */
375 static void free_dhcp_sigs(dhcp_fp_entry *e){
376 if(e->next)
377 free_dhcp_sigs(e->next);
378 free(e);
381 void dump_dhcp_sigs(dhcp_fp_entry *mysig[], int max)
383 int i;
384 for (i = 0; i < max; i++){
385 if (!mysig[i] || !mysig[i]->os)
386 continue;
387 print_dhcp_sig(mysig[i]);
391 void print_dhcp_sig(dhcp_fp_entry * e)
393 int32_t j;
395 plog("[%d:%d:",e->type,e->ttl);
396 for (j=0;j<e->optcnt;j++){
397 plog("%d",e->opt[j]);
398 if ((j+1) < (e->optcnt)) plog(",");
400 plog(":");
401 for (j=0;j<e->optreqcnt;j++){
402 plog("%d",e->optreq[j]);
403 if ((j+1) < (e->optreqcnt)) plog(",");
405 if (e->optreqcnt==0) plog(".");
406 plog(":");
407 if (e->vc == NULL) {
408 plog(".");
409 } else {
410 plog("%s",e->vc);
412 plog(":");
413 if (e->os == NULL) {
414 plog("unknown");
415 } else {
416 plog("%s",e->os);
418 plog(":");
419 if (e->desc == NULL) {
420 plog("unknown");
421 } else {
422 plog("%s",e->desc);
424 plog("]");
426 void print_dhcp_sigs(dhcp_fp_entry * e)
428 print_dhcp_sig(e);
429 if (e->next)
430 print_dhcp_sigs(e->next);
434 /* parse the option field of the signature line */
435 static int parse_dhcp_sig_opts(dhcp_fp_entry *sig, char* p)
437 uint8_t optcnt = 0;
438 if (*p == '.')
439 p++;
441 while (*p) {
443 if (!isdigit(*(p))) {
444 fatal("Bogus DHCP value in line %d.\n", sig->line);
445 } else {
446 if (!isdigit(*(p+1))) {
447 sig->opt[optcnt] = atoi(p);
448 } else if (!isdigit(*(p+2))) {
449 sig->opt[optcnt] = atoi(p);
450 p++;
451 } else if (!isdigit(*(p+3))) {
452 sig->opt[optcnt] = atoi(p);
453 p++;
454 p++;
458 if (++optcnt >= MAXDHCPOPTS)
459 fatal
460 ("Too many DHCP options specified in config line %d.\n",
461 sig->line);
463 * Skip separators
465 do {
466 p++;
467 } while (*p && !isdigit(*p));
469 sig->optcnt = optcnt;
470 return 0;
473 /* parse the option field of the signature line */
474 static int parse_dhcp_sig_optreq(dhcp_fp_entry *sig, char* p)
476 uint8_t optreqcnt = 0;
478 if (*p == '.')
479 p++;
481 while (*p) {
482 if (!isdigit(*(p))) {
483 fatal("Bogus DHCP value in line %d.\n", sig->line);
484 } else {
485 if (!isdigit(*(p + 1))) {
486 sig->optreq[optreqcnt] = atoi(p);
487 } else if (!isdigit(*(p + 2))) {
488 sig->optreq[optreqcnt] = atoi(p);
489 p++;
490 } else if (!isdigit(*(p + 3))) {
491 sig->optreq[optreqcnt] = atoi(p);
492 p++;
493 p++;
497 if (++optreqcnt >= MAXDHCPOPTS)
498 fatal
499 ("Too many DHCP request options specified in config line %d.\n",
500 sig->line);
502 * Skip separators
504 do {
505 p++;
506 } while (*p && !isdigit(*p));
508 sig->optreqcnt = optreqcnt;
509 return 0;