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