alias_nbt.c revision 131699
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 131699 2004-07-06 12:13:28Z 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 *lnk,
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	(void)la;
218	(void)lnk;
219
220	/* Calculate data length of UDP packet */
221	uh = (struct udphdr *)ip_next(pip);
222	pmax = (char *)uh + ntohs(uh->uh_ulen);
223
224	ndh = (NbtDataHeader *)udp_next(uh);
225	if ((char *)(ndh + 1) > pmax)
226		return (-1);
227#ifdef DEBUG
228	printf("\nType=%02x,", ndh->type);
229#endif
230	switch (ndh->type) {
231	case DGM_DIRECT_UNIQ:
232	case DGM_DIRECT_GROUP:
233	case DGM_BROADCAST:
234		p = (u_char *) ndh + 14;
235		p = AliasHandleName(p, pmax);	/* Source Name */
236		p = AliasHandleName(p, pmax);	/* Destination Name */
237		break;
238	case DGM_ERROR:
239		p = (u_char *) ndh + 11;
240		break;
241	case DGM_QUERY:
242	case DGM_POSITIVE_RES:
243	case DGM_NEGATIVE_RES:
244		p = (u_char *) ndh + 10;
245		p = AliasHandleName(p, pmax);	/* Destination Name */
246		break;
247	}
248	if (p == NULL || (char *)p > pmax)
249		p = NULL;
250#ifdef DEBUG
251	printf("%s:%d-->", inet_ntoa(ndh->source_ip), ntohs(ndh->source_port));
252#endif
253	/* Doing an IP address and Port number Translation */
254	if (uh->uh_sum != 0) {
255		int acc;
256		u_short *sptr;
257
258		acc = ndh->source_port;
259		acc -= alias_port;
260		sptr = (u_short *) & (ndh->source_ip);
261		acc += *sptr++;
262		acc += *sptr;
263		sptr = (u_short *) alias_address;
264		acc -= *sptr++;
265		acc -= *sptr;
266		ADJUST_CHECKSUM(acc, uh->uh_sum);
267	}
268	ndh->source_ip = *alias_address;
269	ndh->source_port = alias_port;
270#ifdef DEBUG
271	printf("%s:%d\n", inet_ntoa(ndh->source_ip), ntohs(ndh->source_port));
272	fflush(stdout);
273#endif
274	return ((p == NULL) ? -1 : 0);
275}
276
277/* Question Section */
278#define QS_TYPE_NB		0x0020
279#define QS_TYPE_NBSTAT	0x0021
280#define QS_CLAS_IN		0x0001
281typedef struct {
282	u_short		type;	/* The type of Request */
283	u_short		class;	/* The class of Request */
284}		NBTNsQuestion;
285
286static u_char  *
287AliasHandleQuestion(
288    u_short count,
289    NBTNsQuestion * q,
290    char *pmax,
291    NBTArguments * nbtarg)
292{
293
294	(void)nbtarg;
295
296	while (count != 0) {
297		/* Name Filed */
298		q = (NBTNsQuestion *) AliasHandleName((u_char *) q, pmax);
299
300		if (q == NULL || (char *)(q + 1) > pmax) {
301			q = NULL;
302			break;
303		}
304		/* Type and Class filed */
305		switch (ntohs(q->type)) {
306		case QS_TYPE_NB:
307		case QS_TYPE_NBSTAT:
308			q = q + 1;
309			break;
310		default:
311#ifdef DEBUG
312			printf("\nUnknown Type on Question %0x\n", ntohs(q->type));
313#endif
314			break;
315		}
316		count--;
317	}
318
319	/* Set up to out of Question Section */
320	return ((u_char *) q);
321}
322
323/* Resource Record */
324#define RR_TYPE_A		0x0001
325#define RR_TYPE_NS		0x0002
326#define RR_TYPE_NULL	0x000a
327#define RR_TYPE_NB		0x0020
328#define RR_TYPE_NBSTAT	0x0021
329#define RR_CLAS_IN		0x0001
330#define SizeOfNsResource	8
331typedef struct {
332	u_short		type;
333	u_short		class;
334	unsigned int	ttl;
335	u_short		rdlen;
336}		NBTNsResource;
337
338#define SizeOfNsRNB			6
339typedef struct {
340	u_short		g:	1  , ont:2, resv:13;
341	struct in_addr	addr;
342}		NBTNsRNB;
343
344static u_char  *
345AliasHandleResourceNB(
346    NBTNsResource * q,
347    char *pmax,
348    NBTArguments * nbtarg)
349{
350	NBTNsRNB *nb;
351	u_short bcount;
352
353	if (q == NULL || (char *)(q + 1) > pmax)
354		return (NULL);
355	/* Check out a length */
356	bcount = ntohs(q->rdlen);
357
358	/* Forward to Resource NB position */
359	nb = (NBTNsRNB *) ((u_char *) q + SizeOfNsResource);
360
361	/* Processing all in_addr array */
362#ifdef DEBUG
363	printf("NB rec[%s", inet_ntoa(nbtarg->oldaddr));
364	printf("->%s, %dbytes] ", inet_ntoa(nbtarg->newaddr), bcount);
365#endif
366	while (nb != NULL && bcount != 0) {
367		if ((char *)(nb + 1) > pmax) {
368			nb = NULL;
369			break;
370		}
371#ifdef DEBUG
372		printf("<%s>", inet_ntoa(nb->addr));
373#endif
374		if (!bcmp(&nbtarg->oldaddr, &nb->addr, sizeof(struct in_addr))) {
375			if (*nbtarg->uh_sum != 0) {
376				int acc;
377				u_short *sptr;
378
379				sptr = (u_short *) & (nb->addr);
380				acc = *sptr++;
381				acc += *sptr;
382				sptr = (u_short *) & (nbtarg->newaddr);
383				acc -= *sptr++;
384				acc -= *sptr;
385				ADJUST_CHECKSUM(acc, *nbtarg->uh_sum);
386			}
387			nb->addr = nbtarg->newaddr;
388#ifdef DEBUG
389			printf("O");
390#endif
391		}
392#ifdef DEBUG
393		else {
394			printf(".");
395		}
396#endif
397		nb = (NBTNsRNB *) ((u_char *) nb + SizeOfNsRNB);
398		bcount -= SizeOfNsRNB;
399	}
400	if (nb == NULL || (char *)(nb + 1) > pmax) {
401		nb = NULL;
402	}
403	return ((u_char *) nb);
404}
405
406#define SizeOfResourceA		6
407typedef struct {
408	struct in_addr	addr;
409}		NBTNsResourceA;
410
411static u_char  *
412AliasHandleResourceA(
413    NBTNsResource * q,
414    char *pmax,
415    NBTArguments * nbtarg)
416{
417	NBTNsResourceA *a;
418	u_short bcount;
419
420	if (q == NULL || (char *)(q + 1) > pmax)
421		return (NULL);
422
423	/* Forward to Resource A position */
424	a = (NBTNsResourceA *) ((u_char *) q + sizeof(NBTNsResource));
425
426	/* Check out of length */
427	bcount = ntohs(q->rdlen);
428
429	/* Processing all in_addr array */
430#ifdef DEBUG
431	printf("Arec [%s", inet_ntoa(nbtarg->oldaddr));
432	printf("->%s]", inet_ntoa(nbtarg->newaddr));
433#endif
434	while (bcount != 0) {
435		if (a == NULL || (char *)(a + 1) > pmax)
436			return (NULL);
437#ifdef DEBUG
438		printf("..%s", inet_ntoa(a->addr));
439#endif
440		if (!bcmp(&nbtarg->oldaddr, &a->addr, sizeof(struct in_addr))) {
441			if (*nbtarg->uh_sum != 0) {
442				int acc;
443				u_short *sptr;
444
445				sptr = (u_short *) & (a->addr);	/* Old */
446				acc = *sptr++;
447				acc += *sptr;
448				sptr = (u_short *) & nbtarg->newaddr;	/* New */
449				acc -= *sptr++;
450				acc -= *sptr;
451				ADJUST_CHECKSUM(acc, *nbtarg->uh_sum);
452			}
453			a->addr = nbtarg->newaddr;
454		}
455		a++;		/* XXXX */
456		bcount -= SizeOfResourceA;
457	}
458	if (a == NULL || (char *)(a + 1) > pmax)
459		a = NULL;
460	return ((u_char *) a);
461}
462
463typedef struct {
464	u_short		opcode:4, flags:8, resv:4;
465}		NBTNsResourceNULL;
466
467static u_char  *
468AliasHandleResourceNULL(
469    NBTNsResource * q,
470    char *pmax,
471    NBTArguments * nbtarg)
472{
473	NBTNsResourceNULL *n;
474	u_short bcount;
475
476	(void)nbtarg;
477
478	if (q == NULL || (char *)(q + 1) > pmax)
479		return (NULL);
480
481	/* Forward to Resource NULL position */
482	n = (NBTNsResourceNULL *) ((u_char *) q + sizeof(NBTNsResource));
483
484	/* Check out of length */
485	bcount = ntohs(q->rdlen);
486
487	/* Processing all in_addr array */
488	while (bcount != 0) {
489		if ((char *)(n + 1) > pmax) {
490			n = NULL;
491			break;
492		}
493		n++;
494		bcount -= sizeof(NBTNsResourceNULL);
495	}
496	if ((char *)(n + 1) > pmax)
497		n = NULL;
498
499	return ((u_char *) n);
500}
501
502static u_char  *
503AliasHandleResourceNS(
504    NBTNsResource * q,
505    char *pmax,
506    NBTArguments * nbtarg)
507{
508	NBTNsResourceNULL *n;
509	u_short bcount;
510
511	(void)nbtarg;
512
513	if (q == NULL || (char *)(q + 1) > pmax)
514		return (NULL);
515
516	/* Forward to Resource NULL position */
517	n = (NBTNsResourceNULL *) ((u_char *) q + sizeof(NBTNsResource));
518
519	/* Check out of length */
520	bcount = ntohs(q->rdlen);
521
522	/* Resource Record Name Filed */
523	q = (NBTNsResource *) AliasHandleName((u_char *) n, pmax);	/* XXX */
524
525	if (q == NULL || (char *)((u_char *) n + bcount) > pmax)
526		return (NULL);
527	else
528		return ((u_char *) n + bcount);
529}
530
531typedef struct {
532	u_short		numnames;
533}		NBTNsResourceNBSTAT;
534
535static u_char  *
536AliasHandleResourceNBSTAT(
537    NBTNsResource * q,
538    char *pmax,
539    NBTArguments * nbtarg)
540{
541	NBTNsResourceNBSTAT *n;
542	u_short bcount;
543
544	(void)nbtarg;
545
546	if (q == NULL || (char *)(q + 1) > pmax)
547		return (NULL);
548
549	/* Forward to Resource NBSTAT position */
550	n = (NBTNsResourceNBSTAT *) ((u_char *) q + sizeof(NBTNsResource));
551
552	/* Check out of length */
553	bcount = ntohs(q->rdlen);
554
555	if (q == NULL || (char *)((u_char *) n + bcount) > pmax)
556		return (NULL);
557	else
558		return ((u_char *) n + bcount);
559}
560
561static u_char  *
562AliasHandleResource(
563    u_short count,
564    NBTNsResource * q,
565    char *pmax,
566    NBTArguments
567    * nbtarg)
568{
569	while (count != 0) {
570		/* Resource Record Name Filed */
571		q = (NBTNsResource *) AliasHandleName((u_char *) q, pmax);
572
573		if (q == NULL || (char *)(q + 1) > pmax)
574			break;
575#ifdef DEBUG
576		printf("type=%02x, count=%d\n", ntohs(q->type), count);
577#endif
578
579		/* Type and Class filed */
580		switch (ntohs(q->type)) {
581		case RR_TYPE_NB:
582			q = (NBTNsResource *) AliasHandleResourceNB(
583			    q,
584			    pmax,
585			    nbtarg
586			    );
587			break;
588		case RR_TYPE_A:
589			q = (NBTNsResource *) AliasHandleResourceA(
590			    q,
591			    pmax,
592			    nbtarg
593			    );
594			break;
595		case RR_TYPE_NS:
596			q = (NBTNsResource *) AliasHandleResourceNS(
597			    q,
598			    pmax,
599			    nbtarg
600			    );
601			break;
602		case RR_TYPE_NULL:
603			q = (NBTNsResource *) AliasHandleResourceNULL(
604			    q,
605			    pmax,
606			    nbtarg
607			    );
608			break;
609		case RR_TYPE_NBSTAT:
610			q = (NBTNsResource *) AliasHandleResourceNBSTAT(
611			    q,
612			    pmax,
613			    nbtarg
614			    );
615			break;
616		default:
617#ifdef DEBUG
618			printf(
619			    "\nUnknown Type of Resource %0x\n",
620			    ntohs(q->type)
621			    );
622#endif
623			break;
624		}
625		count--;
626	}
627	fflush(stdout);
628	return ((u_char *) q);
629}
630
631int
632AliasHandleUdpNbtNS(
633    struct libalias *la,
634    struct ip *pip,		/* IP packet to examine/patch */
635    struct alias_link *lnk,
636    struct in_addr *alias_address,
637    u_short * alias_port,
638    struct in_addr *original_address,
639    u_short * original_port)
640{
641	struct udphdr *uh;
642	NbtNSHeader *nsh;
643	u_char *p;
644	char *pmax;
645	NBTArguments nbtarg;
646
647	(void)la;
648	(void)lnk;
649
650	/* Set up Common Parameter */
651	nbtarg.oldaddr = *alias_address;
652	nbtarg.oldport = *alias_port;
653	nbtarg.newaddr = *original_address;
654	nbtarg.newport = *original_port;
655
656	/* Calculate data length of UDP packet */
657	uh = (struct udphdr *)ip_next(pip);
658	nbtarg.uh_sum = &(uh->uh_sum);
659	nsh = (NbtNSHeader *)udp_next(uh);
660	p = (u_char *) (nsh + 1);
661	pmax = (char *)uh + ntohs(uh->uh_ulen);
662
663	if ((char *)(nsh + 1) > pmax)
664		return (-1);
665
666#ifdef DEBUG
667	printf(" [%s] ID=%02x, op=%01x, flag=%02x, rcode=%01x, qd=%04x"
668	    ", an=%04x, ns=%04x, ar=%04x, [%d]-->",
669	    nsh->dir ? "Response" : "Request",
670	    nsh->nametrid,
671	    nsh->opcode,
672	    nsh->nmflags,
673	    nsh->rcode,
674	    ntohs(nsh->qdcount),
675	    ntohs(nsh->ancount),
676	    ntohs(nsh->nscount),
677	    ntohs(nsh->arcount),
678	    (u_char *) p - (u_char *) nsh
679	    );
680#endif
681
682	/* Question Entries */
683	if (ntohs(nsh->qdcount) != 0) {
684		p = AliasHandleQuestion(
685		    ntohs(nsh->qdcount),
686		    (NBTNsQuestion *) p,
687		    pmax,
688		    &nbtarg
689		    );
690	}
691	/* Answer Resource Records */
692	if (ntohs(nsh->ancount) != 0) {
693		p = AliasHandleResource(
694		    ntohs(nsh->ancount),
695		    (NBTNsResource *) p,
696		    pmax,
697		    &nbtarg
698		    );
699	}
700	/* Authority Resource Recodrs */
701	if (ntohs(nsh->nscount) != 0) {
702		p = AliasHandleResource(
703		    ntohs(nsh->nscount),
704		    (NBTNsResource *) p,
705		    pmax,
706		    &nbtarg
707		    );
708	}
709	/* Additional Resource Recodrs */
710	if (ntohs(nsh->arcount) != 0) {
711		p = AliasHandleResource(
712		    ntohs(nsh->arcount),
713		    (NBTNsResource *) p,
714		    pmax,
715		    &nbtarg
716		    );
717	}
718#ifdef DEBUG
719	PrintRcode(nsh->rcode);
720#endif
721	return ((p == NULL) ? -1 : 0);
722}
723