1 /* 2 * Written by Atsushi Murai <amurai@spec.co.jp> 3 * 4 * Copyright (C) 1998, System Planning and Engineering Co. All rights reserverd. 5 * 6 * Redistribution and use in source and binary forms are permitted 7 * provided that the above copyright notice and this paragraph are 8 * duplicated in all such forms and that any documentation, 9 * advertising materials, and other materials related to such 10 * distribution and use acknowledge that the software was developed 11 * by the System Planning and Engineering Co. The name of the 12 * SPEC may not be used to endorse or promote products derived 13 * from this software without specific prior written permission. 14 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 15 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 16 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. 17 * 18 * $FreeBSD$ 19 * 20 * TODO: 21 * oClean up. 22 * oConsidering for word alignment for other platform. 23 */ 24 /* 25 alias_nbt.c performs special processing for NetBios over TCP/IP 26 sessions by UDP. 27 28 Initial version: May, 1998 (Atsushi Murai <amurai@spec.co.jp>) 29 30 See HISTORY file for record of revisions. 31 */ 32 33 /* Includes */ 34 #include <ctype.h> 35 #include <stdio.h> 36 #include <string.h> 37 #include <sys/types.h> 38 #include <netinet/in_systm.h> 39 #include <netinet/in.h> 40 #include <arpa/inet.h> 41 #include <netinet/ip.h> 42 #include <netinet/udp.h> 43 #include <netinet/tcp.h> 44 45 #include "alias_local.h" 46 47 #define ADJUST_CHECKSUM(acc, cksum) { \ 48 acc += cksum; \ 49 if (acc < 0) \ 50 { \ 51 acc = -acc; \ 52 acc = (acc >> 16) + (acc & 0xffff); \ 53 acc += acc >> 16; \ 54 cksum = (u_short) ~acc; \ 55 } \ 56 else \ 57 { \ 58 acc = (acc >> 16) + (acc & 0xffff); \ 59 acc += acc >> 16; \ 60 cksum = (u_short) acc; \ 61 } \ 62 } 63 64 typedef struct { 65 struct in_addr oldaddr; 66 u_short oldport; 67 struct in_addr newaddr; 68 u_short newport; 69 u_short *uh_sum; 70 } NBTArguments; 71 72 typedef struct { 73 unsigned char type; 74 unsigned char flags; 75 u_short id; 76 struct in_addr source_ip; 77 u_short source_port; 78 u_short len; 79 u_short offset; 80 } NbtDataHeader; 81 82 #define OpQuery 0 83 #define OpUnknown 4 84 #define OpRegist 5 85 #define OpRelease 6 86 #define OpWACK 7 87 #define OpRefresh 8 88 typedef struct { 89 u_short nametrid; 90 u_short dir:1, opcode:4, nmflags:7, rcode:4; 91 u_short qdcount; 92 u_short ancount; 93 u_short nscount; 94 u_short arcount; 95 } NbtNSHeader; 96 97 #define FMT_ERR 0x1 98 #define SRV_ERR 0x2 99 #define IMP_ERR 0x4 100 #define RFS_ERR 0x5 101 #define ACT_ERR 0x6 102 #define CFT_ERR 0x7 103 104 105 #ifdef DEBUG 106 static void PrintRcode( u_char rcode ) { 107 108 switch (rcode) { 109 case FMT_ERR: 110 printf("\nFormat Error."); 111 case SRV_ERR: 112 printf("\nSever failure."); 113 case IMP_ERR: 114 printf("\nUnsupported request error.\n"); 115 case RFS_ERR: 116 printf("\nRefused error.\n"); 117 case ACT_ERR: 118 printf("\nActive error.\n"); 119 case CFT_ERR: 120 printf("\nName in conflict error.\n"); 121 default: 122 printf("\n???=%0x\n", rcode ); 123 124 } 125 } 126 #endif 127 128 129 /* Handling Name field */ 130 static u_char *AliasHandleName ( u_char *p, char *pmax ) { 131 132 u_char *s; 133 u_char c; 134 int compress; 135 136 /* Following length field */ 137 138 if (p == NULL || (char *)p >= pmax) 139 return(NULL); 140 141 if (*p & 0xc0 ) { 142 p = p + 2; 143 if ((char *)p > pmax) 144 return(NULL); 145 return ((u_char *)p); 146 } 147 while ( ( *p & 0x3f) != 0x00 ) { 148 s = p + 1; 149 if ( *p == 0x20 ) 150 compress = 1; 151 else 152 compress = 0; 153 154 /* Get next length field */ 155 p = (u_char *)(p + (*p & 0x3f) + 1); 156 if ((char *)p > pmax) { 157 p = NULL; 158 break; 159 } 160 #ifdef DEBUG 161 printf(":"); 162 #endif 163 while (s < p) { 164 if ( compress == 1 ) { 165 c = (u_char )(((((*s & 0x0f) << 4) | (*(s+1) & 0x0f)) - 0x11)); 166 #ifdef DEBUG 167 if (isprint( c ) ) 168 printf("%c", c ); 169 else 170 printf("<0x%02x>", c ); 171 #endif 172 s +=2; 173 } else { 174 #ifdef DEBUG 175 printf("%c", *s); 176 #endif 177 s++; 178 } 179 } 180 #ifdef DEBUG 181 printf(":"); 182 #endif 183 fflush(stdout); 184 } 185 186 /* Set up to out of Name field */ 187 if (p == NULL || (char *)p >= pmax) 188 p = NULL; 189 else 190 p++; 191 return ((u_char *)p); 192 } 193 194 /* 195 * NetBios Datagram Handler (IP/UDP) 196 */ 197 #define DGM_DIRECT_UNIQ 0x10 198 #define DGM_DIRECT_GROUP 0x11 199 #define DGM_BROADCAST 0x12 200 #define DGM_ERROR 0x13 201 #define DGM_QUERY 0x14 202 #define DGM_POSITIVE_RES 0x15 203 #define DGM_NEGATIVE_RES 0x16 204 205 int AliasHandleUdpNbt( 206 struct ip *pip, /* IP packet to examine/patch */ 207 struct alias_link *link, 208 struct in_addr *alias_address, 209 u_short alias_port 210 ) { 211 struct udphdr * uh; 212 NbtDataHeader *ndh; 213 u_char *p = NULL; 214 char *pmax; 215 216 /* Calculate data length of UDP packet */ 217 uh = (struct udphdr *) ((char *) pip + (pip->ip_hl << 2)); 218 pmax = (char *)uh + ntohs( uh->uh_ulen ); 219 220 ndh = (NbtDataHeader *)((char *)uh + (sizeof (struct udphdr))); 221 if ((char *)(ndh + 1) > pmax) 222 return(-1); 223 #ifdef DEBUG 224 printf("\nType=%02x,", ndh->type ); 225 #endif 226 switch ( ndh->type ) { 227 case DGM_DIRECT_UNIQ: 228 case DGM_DIRECT_GROUP: 229 case DGM_BROADCAST: 230 p = (u_char *)ndh + 14; 231 p = AliasHandleName ( p, pmax ); /* Source Name */ 232 p = AliasHandleName ( p, pmax ); /* Destination Name */ 233 break; 234 case DGM_ERROR: 235 p = (u_char *)ndh + 11; 236 break; 237 case DGM_QUERY: 238 case DGM_POSITIVE_RES: 239 case DGM_NEGATIVE_RES: 240 p = (u_char *)ndh + 10; 241 p = AliasHandleName ( p, pmax ); /* Destination Name */ 242 break; 243 } 244 if (p == NULL || (char *)p > pmax) 245 p = NULL; 246 #ifdef DEBUG 247 printf("%s:%d-->", inet_ntoa(ndh->source_ip), ntohs(ndh->source_port) ); 248 #endif 249 /* Doing a IP address and Port number Translation */ 250 if ( uh->uh_sum != 0 ) { 251 int acc; 252 u_short *sptr; 253 acc = ndh->source_port; 254 acc -= alias_port; 255 sptr = (u_short *) &(ndh->source_ip); 256 acc += *sptr++; 257 acc += *sptr; 258 sptr = (u_short *) alias_address; 259 acc -= *sptr++; 260 acc -= *sptr; 261 ADJUST_CHECKSUM(acc, uh->uh_sum) 262 } 263 ndh->source_ip = *alias_address; 264 ndh->source_port = alias_port; 265 #ifdef DEBUG 266 printf("%s:%d\n", inet_ntoa(ndh->source_ip), ntohs(ndh->source_port) ); 267 fflush(stdout); 268 #endif 269 return((p == NULL) ? -1 : 0); 270 } 271 /* Question Section */ 272 #define QS_TYPE_NB 0x0020 273 #define QS_TYPE_NBSTAT 0x0021 274 #define QS_CLAS_IN 0x0001 275 typedef struct { 276 u_short type; /* The type of Request */ 277 u_short class; /* The class of Request */ 278 } NBTNsQuestion; 279 280 static u_char * 281 AliasHandleQuestion( 282 u_short count, 283 NBTNsQuestion *q, 284 char *pmax, 285 NBTArguments *nbtarg) 286 { 287 288 while ( count != 0 ) { 289 /* Name Filed */ 290 q = (NBTNsQuestion *)AliasHandleName((u_char *)q, pmax); 291 292 if (q == NULL || (char *)(q + 1) > pmax) { 293 q = NULL; 294 break; 295 } 296 297 /* Type and Class filed */ 298 switch ( ntohs(q->type) ) { 299 case QS_TYPE_NB: 300 case QS_TYPE_NBSTAT: 301 q= q+1; 302 break; 303 default: 304 #ifdef DEBUG 305 printf("\nUnknown Type on Question %0x\n", ntohs(q->type) ); 306 #endif 307 break; 308 } 309 count--; 310 } 311 312 /* Set up to out of Question Section */ 313 return ((u_char *)q); 314 } 315 316 /* Resource Record */ 317 #define RR_TYPE_A 0x0001 318 #define RR_TYPE_NS 0x0002 319 #define RR_TYPE_NULL 0x000a 320 #define RR_TYPE_NB 0x0020 321 #define RR_TYPE_NBSTAT 0x0021 322 #define RR_CLAS_IN 0x0001 323 #define SizeOfNsResource 8 324 typedef struct { 325 u_short type; 326 u_short class; 327 unsigned int ttl; 328 u_short rdlen; 329 } NBTNsResource; 330 331 #define SizeOfNsRNB 6 332 typedef struct { 333 u_short g:1, ont:2, resv:13; 334 struct in_addr addr; 335 } NBTNsRNB; 336 337 static u_char * 338 AliasHandleResourceNB( 339 NBTNsResource *q, 340 char *pmax, 341 NBTArguments *nbtarg) 342 { 343 NBTNsRNB *nb; 344 u_short bcount; 345 346 if (q == NULL || (char *)(q + 1) > pmax) 347 return(NULL); 348 /* Check out a length */ 349 bcount = ntohs(q->rdlen); 350 351 /* Forward to Resource NB position */ 352 nb = (NBTNsRNB *)((u_char *)q + SizeOfNsResource); 353 354 /* Processing all in_addr array */ 355 #ifdef DEBUG 356 printf("NB rec[%s", inet_ntoa(nbtarg->oldaddr)); 357 printf("->%s, %dbytes] ",inet_ntoa(nbtarg->newaddr ), bcount); 358 #endif 359 while ( nb != NULL && bcount != 0 ) { 360 if ((char *)(nb + 1) > pmax) { 361 nb = NULL; 362 break; 363 } 364 #ifdef DEBUG 365 printf("<%s>", inet_ntoa(nb->addr) ); 366 #endif 367 if (!bcmp(&nbtarg->oldaddr,&nb->addr, sizeof(struct in_addr) ) ) { 368 if ( *nbtarg->uh_sum != 0 ) { 369 int acc; 370 u_short *sptr; 371 372 sptr = (u_short *) &(nb->addr); 373 acc = *sptr++; 374 acc += *sptr; 375 sptr = (u_short *) &(nbtarg->newaddr); 376 acc -= *sptr++; 377 acc -= *sptr; 378 ADJUST_CHECKSUM(acc, *nbtarg->uh_sum) 379 } 380 381 nb->addr = nbtarg->newaddr; 382 #ifdef DEBUG 383 printf("O"); 384 #endif 385 } 386 #ifdef DEBUG 387 else { 388 printf("."); 389 } 390 #endif 391 nb=(NBTNsRNB *)((u_char *)nb + SizeOfNsRNB); 392 bcount -= SizeOfNsRNB; 393 } 394 if (nb == NULL || (char *)(nb + 1) > pmax) { 395 nb = NULL; 396 } 397 398 return ((u_char *)nb); 399 } 400 401 #define SizeOfResourceA 6 402 typedef struct { 403 struct in_addr addr; 404 } NBTNsResourceA; 405 406 static u_char * 407 AliasHandleResourceA( 408 NBTNsResource *q, 409 char *pmax, 410 NBTArguments *nbtarg) 411 { 412 NBTNsResourceA *a; 413 u_short bcount; 414 415 if (q == NULL || (char *)(q + 1) > pmax) 416 return(NULL); 417 418 /* Forward to Resource A position */ 419 a = (NBTNsResourceA *)( (u_char *)q + sizeof(NBTNsResource) ); 420 421 /* Check out of length */ 422 bcount = ntohs(q->rdlen); 423 424 /* Processing all in_addr array */ 425 #ifdef DEBUG 426 printf("Arec [%s", inet_ntoa(nbtarg->oldaddr)); 427 printf("->%s]",inet_ntoa(nbtarg->newaddr )); 428 #endif 429 while ( bcount != 0 ) { 430 if (a == NULL || (char *)(a + 1) > pmax) 431 return(NULL); 432 #ifdef DEBUG 433 printf("..%s", inet_ntoa(a->addr) ); 434 #endif 435 if ( !bcmp(&nbtarg->oldaddr, &a->addr, sizeof(struct in_addr) ) ) { 436 if ( *nbtarg->uh_sum != 0 ) { 437 int acc; 438 u_short *sptr; 439 440 sptr = (u_short *) &(a->addr); /* Old */ 441 acc = *sptr++; 442 acc += *sptr; 443 sptr = (u_short *) &nbtarg->newaddr; /* New */ 444 acc -= *sptr++; 445 acc -= *sptr; 446 ADJUST_CHECKSUM(acc, *nbtarg->uh_sum) 447 } 448 449 a->addr = nbtarg->newaddr; 450 } 451 a++; /*XXXX*/ 452 bcount -= SizeOfResourceA; 453 } 454 if (a == NULL || (char *)(a + 1) > pmax) 455 a = NULL; 456 return ((u_char *)a); 457 } 458 459 typedef struct { 460 u_short opcode:4, flags:8, resv:4; 461 } NBTNsResourceNULL; 462 463 static u_char * 464 AliasHandleResourceNULL( 465 NBTNsResource *q, 466 char *pmax, 467 NBTArguments *nbtarg) 468 { 469 NBTNsResourceNULL *n; 470 u_short bcount; 471 472 if (q == NULL || (char *)(q + 1) > pmax) 473 return(NULL); 474 475 /* Forward to Resource NULL position */ 476 n = (NBTNsResourceNULL *)( (u_char *)q + sizeof(NBTNsResource) ); 477 478 /* Check out of length */ 479 bcount = ntohs(q->rdlen); 480 481 /* Processing all in_addr array */ 482 while ( bcount != 0 ) { 483 if ((char *)(n + 1) > pmax) { 484 n = NULL; 485 break; 486 } 487 n++; 488 bcount -= sizeof(NBTNsResourceNULL); 489 } 490 if ((char *)(n + 1) > pmax) 491 n = NULL; 492 493 return ((u_char *)n); 494 } 495 496 static u_char * 497 AliasHandleResourceNS( 498 NBTNsResource *q, 499 char *pmax, 500 NBTArguments *nbtarg) 501 { 502 NBTNsResourceNULL *n; 503 u_short bcount; 504 505 if (q == NULL || (char *)(q + 1) > pmax) 506 return(NULL); 507 508 /* Forward to Resource NULL position */ 509 n = (NBTNsResourceNULL *)( (u_char *)q + sizeof(NBTNsResource) ); 510 511 /* Check out of length */ 512 bcount = ntohs(q->rdlen); 513 514 /* Resource Record Name Filed */ 515 q = (NBTNsResource *)AliasHandleName( (u_char *)n, pmax ); /* XXX */ 516 517 if (q == NULL || (char *)((u_char *)n + bcount) > pmax) 518 return(NULL); 519 else 520 return ((u_char *)n + bcount); 521 } 522 523 typedef struct { 524 u_short numnames; 525 } NBTNsResourceNBSTAT; 526 527 static u_char * 528 AliasHandleResourceNBSTAT( 529 NBTNsResource *q, 530 char *pmax, 531 NBTArguments *nbtarg) 532 { 533 NBTNsResourceNBSTAT *n; 534 u_short bcount; 535 536 if (q == NULL || (char *)(q + 1) > pmax) 537 return(NULL); 538 539 /* Forward to Resource NBSTAT position */ 540 n = (NBTNsResourceNBSTAT *)( (u_char *)q + sizeof(NBTNsResource) ); 541 542 /* Check out of length */ 543 bcount = ntohs(q->rdlen); 544 545 if (q == NULL || (char *)((u_char *)n + bcount) > pmax) 546 return(NULL); 547 else 548 return ((u_char *)n + bcount); 549 } 550 551 static u_char * 552 AliasHandleResource( 553 u_short count, 554 NBTNsResource *q, 555 char *pmax, 556 NBTArguments 557 *nbtarg) 558 { 559 while ( count != 0 ) { 560 /* Resource Record Name Filed */ 561 q = (NBTNsResource *)AliasHandleName( (u_char *)q, pmax ); 562 563 if (q == NULL || (char *)(q + 1) > pmax) 564 break; 565 #ifdef DEBUG 566 printf("type=%02x, count=%d\n", ntohs(q->type), count ); 567 #endif 568 569 /* Type and Class filed */ 570 switch ( ntohs(q->type) ) { 571 case RR_TYPE_NB: 572 q = (NBTNsResource *)AliasHandleResourceNB( 573 q, 574 pmax, 575 nbtarg 576 ); 577 break; 578 case RR_TYPE_A: 579 q = (NBTNsResource *)AliasHandleResourceA( 580 q, 581 pmax, 582 nbtarg 583 ); 584 break; 585 case RR_TYPE_NS: 586 q = (NBTNsResource *)AliasHandleResourceNS( 587 q, 588 pmax, 589 nbtarg 590 ); 591 break; 592 case RR_TYPE_NULL: 593 q = (NBTNsResource *)AliasHandleResourceNULL( 594 q, 595 pmax, 596 nbtarg 597 ); 598 break; 599 case RR_TYPE_NBSTAT: 600 q = (NBTNsResource *)AliasHandleResourceNBSTAT( 601 q, 602 pmax, 603 nbtarg 604 ); 605 break; 606 default: 607 #ifdef DEBUG 608 printf( 609 "\nUnknown Type of Resource %0x\n", 610 ntohs(q->type) 611 ); 612 #endif 613 break; 614 } 615 count--; 616 } 617 fflush(stdout); 618 return ((u_char *)q); 619 } 620 621 int AliasHandleUdpNbtNS( 622 struct ip *pip, /* IP packet to examine/patch */ 623 struct alias_link *link, 624 struct in_addr *alias_address, 625 u_short *alias_port, 626 struct in_addr *original_address, 627 u_short *original_port ) 628 { 629 struct udphdr * uh; 630 NbtNSHeader * nsh; 631 u_char * p; 632 char *pmax; 633 NBTArguments nbtarg; 634 635 /* Set up Common Parameter */ 636 nbtarg.oldaddr = *alias_address; 637 nbtarg.oldport = *alias_port; 638 nbtarg.newaddr = *original_address; 639 nbtarg.newport = *original_port; 640 641 /* Calculate data length of UDP packet */ 642 uh = (struct udphdr *) ((char *) pip + (pip->ip_hl << 2)); 643 nbtarg.uh_sum = &(uh->uh_sum); 644 nsh = (NbtNSHeader *)((char *)uh + (sizeof(struct udphdr))); 645 p = (u_char *)(nsh + 1); 646 pmax = (char *)uh + ntohs( uh->uh_ulen ); 647 648 if ((char *)(nsh + 1) > pmax) 649 return(-1); 650 651 #ifdef DEBUG 652 printf(" [%s] ID=%02x, op=%01x, flag=%02x, rcode=%01x, qd=%04x" 653 ", an=%04x, ns=%04x, ar=%04x, [%d]-->", 654 nsh->dir ? "Response": "Request", 655 nsh->nametrid, 656 nsh->opcode, 657 nsh->nmflags, 658 nsh->rcode, 659 ntohs(nsh->qdcount), 660 ntohs(nsh->ancount), 661 ntohs(nsh->nscount), 662 ntohs(nsh->arcount), 663 (u_char *)p -(u_char *)nsh 664 ); 665 #endif 666 667 /* Question Entries */ 668 if (ntohs(nsh->qdcount) !=0 ) { 669 p = AliasHandleQuestion( 670 ntohs(nsh->qdcount), 671 (NBTNsQuestion *)p, 672 pmax, 673 &nbtarg 674 ); 675 } 676 677 /* Answer Resource Records */ 678 if (ntohs(nsh->ancount) !=0 ) { 679 p = AliasHandleResource( 680 ntohs(nsh->ancount), 681 (NBTNsResource *)p, 682 pmax, 683 &nbtarg 684 ); 685 } 686 687 /* Authority Resource Recodrs */ 688 if (ntohs(nsh->nscount) !=0 ) { 689 p = AliasHandleResource( 690 ntohs(nsh->nscount), 691 (NBTNsResource *)p, 692 pmax, 693 &nbtarg 694 ); 695 } 696 697 /* Additional Resource Recodrs */ 698 if (ntohs(nsh->arcount) !=0 ) { 699 p = AliasHandleResource( 700 ntohs(nsh->arcount), 701 (NBTNsResource *)p, 702 pmax, 703 &nbtarg 704 ); 705 } 706 707 #ifdef DEBUG 708 PrintRcode(nsh->rcode); 709 #endif 710 return ((p == NULL) ? -1 : 0); 711 } 712