xref: /freebsd/sys/netinet/libalias/alias_nbt.c (revision 2357939bc239bd5334a169b62313806178dd8f30)
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 *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
278 typedef struct {
279 	u_short		type;	/* The type of Request */
280 	u_short		class;	/* The class of Request */
281 }		NBTNsQuestion;
282 
283 static u_char  *
284 AliasHandleQuestion(
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
326 typedef struct {
327 	u_short		type;
328 	u_short		class;
329 	unsigned int	ttl;
330 	u_short		rdlen;
331 }		NBTNsResource;
332 
333 #define SizeOfNsRNB			6
334 typedef struct {
335 	u_short		g:	1  , ont:2, resv:13;
336 	struct in_addr	addr;
337 }		NBTNsRNB;
338 
339 static u_char  *
340 AliasHandleResourceNB(
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
402 typedef struct {
403 	struct in_addr	addr;
404 }		NBTNsResourceA;
405 
406 static u_char  *
407 AliasHandleResourceA(
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 
458 typedef struct {
459 	u_short		opcode:4, flags:8, resv:4;
460 }		NBTNsResourceNULL;
461 
462 static u_char  *
463 AliasHandleResourceNULL(
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 
495 static u_char  *
496 AliasHandleResourceNS(
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 
522 typedef struct {
523 	u_short		numnames;
524 }		NBTNsResourceNBSTAT;
525 
526 static u_char  *
527 AliasHandleResourceNBSTAT(
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 
550 static u_char  *
551 AliasHandleResource(
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 
620 int
621 AliasHandleUdpNbtNS(
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