alias_nbt.c revision 50476
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: head/sys/netinet/libalias/alias_nbt.c 50476 1999-08-28 00:22:10Z peter $
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
64typedef 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
72typedef 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
88typedef 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
106static 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 */
130static 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
205int 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
275typedef struct {
276	u_short	type;	/* The type of Request */
277	u_short	class;	/* The class of Request */
278} NBTNsQuestion;
279
280static u_char *
281AliasHandleQuestion(
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
324typedef struct {
325 	u_short type;
326 	u_short class;
327 	unsigned int ttl;
328 	u_short rdlen;
329} NBTNsResource;
330
331#define SizeOfNsRNB			6
332typedef struct {
333	u_short g:1, ont:2, resv:13;
334	struct	in_addr	addr;
335} NBTNsRNB;
336
337static u_char *
338AliasHandleResourceNB(
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
402typedef struct {
403	struct	in_addr	addr;
404} NBTNsResourceA;
405
406static u_char *
407AliasHandleResourceA(
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
459typedef struct {
460	u_short opcode:4, flags:8, resv:4;
461} NBTNsResourceNULL;
462
463static u_char *
464AliasHandleResourceNULL(
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
496static u_char *
497AliasHandleResourceNS(
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
523typedef struct {
524	u_short	numnames;
525} NBTNsResourceNBSTAT;
526
527static u_char *
528AliasHandleResourceNBSTAT(
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
551static u_char *
552AliasHandleResource(
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
621int 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