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