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