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