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