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