xref: /freebsd/sys/netinet/libalias/alias_nbt.c (revision 6fd05b64b5b65dd4ba9b86482a0634a5f0b96c29)
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$");
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 
57 typedef 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 
65 typedef 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
81 typedef 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
99 static void
100 PrintRcode(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 */
126 static u_char  *
127 AliasHandleName(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 
203 int
204 AliasHandleUdpNbt(
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
281 typedef struct {
282 	u_short		type;	/* The type of Request */
283 	u_short		class;	/* The class of Request */
284 }		NBTNsQuestion;
285 
286 static u_char  *
287 AliasHandleQuestion(
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
331 typedef struct {
332 	u_short		type;
333 	u_short		class;
334 	unsigned int	ttl;
335 	u_short		rdlen;
336 }		NBTNsResource;
337 
338 #define SizeOfNsRNB			6
339 typedef struct {
340 	u_short		g:	1  , ont:2, resv:13;
341 	struct in_addr	addr;
342 }		NBTNsRNB;
343 
344 static u_char  *
345 AliasHandleResourceNB(
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
407 typedef struct {
408 	struct in_addr	addr;
409 }		NBTNsResourceA;
410 
411 static u_char  *
412 AliasHandleResourceA(
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 
463 typedef struct {
464 	u_short		opcode:4, flags:8, resv:4;
465 }		NBTNsResourceNULL;
466 
467 static u_char  *
468 AliasHandleResourceNULL(
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 
502 static u_char  *
503 AliasHandleResourceNS(
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 
531 typedef struct {
532 	u_short		numnames;
533 }		NBTNsResourceNBSTAT;
534 
535 static u_char  *
536 AliasHandleResourceNBSTAT(
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 
561 static u_char  *
562 AliasHandleResource(
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 
631 int
632 AliasHandleUdpNbtNS(
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