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