xref: /freebsd/sys/netinet/libalias/alias_nbt.c (revision daf1cffce2e07931f27c6c6998652e90df6ba87e)
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