1/*
2 * Copyright (c) 2000-2002 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License").  You may not use this file except in compliance with the
9 * License.  Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
11 *
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
17 * License for the specific language governing rights and limitations
18 * under the License.
19 *
20 * @APPLE_LICENSE_HEADER_END@
21 */
22/*-
23 * Written by Atsushi Murai <amurai@spec.co.jp>
24 * Copyright (c) 1998, System Planning and Engineering Co.
25 * All rights reserved.
26 *
27 * Redistribution and use in source and binary forms, with or without
28 * modification, are permitted provided that the following conditions
29 * are met:
30 * 1. Redistributions of source code must retain the above copyright
31 *    notice, this list of conditions and the following disclaimer.
32 * 2. Redistributions in binary form must reproduce the above copyright
33 *    notice, this list of conditions and the following disclaimer in the
34 *    documentation and/or other materials provided with the distribution.
35 *
36 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
37 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
38 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
39 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
40 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
41 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
42 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
43 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
44 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
45 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
46 * SUCH DAMAGE.
47 *
48 * Based upon:
49 * $FreeBSD: src/lib/libalias/alias_nbt.c,v 1.4.2.3 2001/08/01 09:52:26 obrien Exp $
50 *
51 *  TODO:
52 *       oClean up.
53 *       oConsidering for word alignment for other platform.
54 */
55/*
56    alias_nbt.c performs special processing for NetBios over TCP/IP
57    sessions by UDP.
58
59    Initial version:  May, 1998  (Atsushi Murai <amurai@spec.co.jp>)
60
61    See HISTORY file for record of revisions.
62*/
63
64/* Includes */
65#include <ctype.h>
66#include <stdio.h>
67#include <string.h>
68#include <sys/types.h>
69#include <netinet/in_systm.h>
70#include <netinet/in.h>
71#include <arpa/inet.h>
72#include <netinet/ip.h>
73#include <netinet/udp.h>
74#include <netinet/tcp.h>
75
76#include "alias_local.h"
77
78typedef struct {
79	struct in_addr		oldaddr;
80	u_short 			oldport;
81	struct in_addr		newaddr;
82	u_short 			newport;
83	u_short 			*uh_sum;
84} NBTArguments;
85
86typedef struct {
87	unsigned char   type;
88	unsigned char   flags;
89	u_short  		id;
90	struct in_addr  source_ip;
91	u_short			source_port;
92	u_short			len;
93	u_short			offset;
94} NbtDataHeader;
95
96#define OpQuery		0
97#define OpUnknown	4
98#define OpRegist	5
99#define OpRelease	6
100#define OpWACK		7
101#define OpRefresh	8
102typedef struct {
103	u_short			nametrid;
104	u_short 		dir:1, opcode:4, nmflags:7, rcode:4;
105	u_short			qdcount;
106	u_short			ancount;
107	u_short			nscount;
108	u_short			arcount;
109} NbtNSHeader;
110
111#define FMT_ERR		0x1
112#define SRV_ERR		0x2
113#define IMP_ERR		0x4
114#define RFS_ERR		0x5
115#define ACT_ERR		0x6
116#define CFT_ERR		0x7
117
118
119#ifdef DEBUG
120static void PrintRcode( u_char rcode )  {
121
122	switch (rcode) {
123		case FMT_ERR:
124			printf("\n  Format Error.\n");
125			break;
126		case SRV_ERR:
127			printf("\n  Server failure.\n");
128			break;
129		case IMP_ERR:
130			printf("\n  Unsupported request error.\n");
131			break;
132		case RFS_ERR:
133			printf("\n  Refused error.\n");
134			break;
135		case ACT_ERR:
136			printf("\n  Active error.\n");
137			break;
138		case CFT_ERR:
139			printf("\n  Name in conflict error.\n");
140			break;
141		default:
142			printf("\n  \?\?\?=%0x\n", rcode );
143			break;
144	}
145}
146#endif
147
148
149/* Handling Name field */
150static u_char *AliasHandleName ( u_char *p, char *pmax ) {
151
152	u_char *s;
153	u_char c;
154	int		compress;
155
156	/* Following length field */
157
158	if (p == NULL || (char *)p >= pmax)
159		return(NULL);
160
161	if (*p & 0xc0 ) {
162		p = p + 2;
163		if ((char *)p > pmax)
164			return(NULL);
165		return ((u_char *)p);
166	}
167	while ( ( *p & 0x3f) != 0x00 ) {
168		s = p + 1;
169		if ( *p == 0x20 )
170			compress = 1;
171		else
172			compress = 0;
173
174	 	/* Get next length field */
175		p = (u_char *)(p + (*p & 0x3f) + 1);
176		if ((char *)p > pmax) {
177			p = NULL;
178			break;
179		}
180#ifdef DEBUG
181		printf(":");
182#endif
183		while (s < p) {
184			if ( compress == 1 ) {
185				c = (u_char )(((((*s & 0x0f) << 4) | (*(s+1) & 0x0f)) - 0x11));
186#ifdef DEBUG
187				if (isprint( c ) )
188					printf("%c", c );
189				else
190					printf("<0x%02x>", c );
191#endif
192				s +=2;
193			} else {
194#ifdef DEBUG
195				printf("%c", *s);
196#endif
197				s++;
198			}
199		}
200#ifdef DEBUG
201		printf(":");
202#endif
203		fflush(stdout);
204    }
205
206	/* Set up to out of Name field */
207	if (p == NULL || (char *)p >= pmax)
208	    p = NULL;
209	else
210	    p++;
211	return ((u_char *)p);
212}
213
214/*
215 * NetBios Datagram Handler (IP/UDP)
216 */
217#define DGM_DIRECT_UNIQ		0x10
218#define DGM_DIRECT_GROUP	0x11
219#define DGM_BROADCAST		0x12
220#define DGM_ERROR			0x13
221#define DGM_QUERY			0x14
222#define DGM_POSITIVE_RES	0x15
223#define DGM_NEGATIVE_RES	0x16
224
225int AliasHandleUdpNbt(
226	struct ip 		  	*pip,	 /* IP packet to examine/patch */
227	struct alias_link 	*link,
228	struct in_addr		*alias_address,
229    u_short 		alias_port
230) {
231    struct udphdr *	uh;
232    NbtDataHeader 	*ndh;
233    u_char		*p = NULL;
234    char		*pmax;
235
236    /* Calculate data length of UDP packet */
237    uh =  (struct udphdr *) ((char *) pip + (pip->ip_hl << 2));
238    pmax = (char *)uh + ntohs( uh->uh_ulen );
239
240	ndh = (NbtDataHeader *)((char *)uh + (sizeof (struct udphdr)));
241    if ((char *)(ndh + 1) > pmax)
242	    return(-1);
243#ifdef DEBUG
244	printf("Type=%02x,", ndh->type );
245#endif
246	switch ( ndh->type ) {
247		case DGM_DIRECT_UNIQ:
248		case DGM_DIRECT_GROUP:
249		case DGM_BROADCAST:
250			p = (u_char *)ndh + 14;
251		    p = AliasHandleName ( p, pmax ); /* Source Name */
252		    p = AliasHandleName ( p, pmax ); /* Destination Name */
253			break;
254		case DGM_ERROR:
255			p = (u_char *)ndh + 11;
256			break;
257		case DGM_QUERY:
258		case DGM_POSITIVE_RES:
259		case DGM_NEGATIVE_RES:
260			p = (u_char *)ndh + 10;
261		    p = AliasHandleName ( p, pmax ); /* Destination Name */
262			break;
263	}
264    if (p == NULL || (char *)p > pmax)
265	    p = NULL;
266#ifdef DEBUG
267	printf("%s:%d-->", inet_ntoa(ndh->source_ip), ntohs(ndh->source_port) );
268#endif
269	/* Doing a IP address and Port number Translation */
270	if ( uh->uh_sum != 0 ) {
271		int				acc;
272		u_short			*sptr;
273		acc  = ndh->source_port;
274		acc -= alias_port;
275		sptr = (u_short *) &(ndh->source_ip);
276		acc += *sptr++;
277		acc += *sptr;
278		sptr = (u_short *) alias_address;
279		acc -= *sptr++;
280		acc -= *sptr;
281		ADJUST_CHECKSUM(acc, uh->uh_sum);
282	}
283    ndh->source_ip = *alias_address;
284    ndh->source_port = alias_port;
285#ifdef DEBUG
286	printf("%s:%d\n", inet_ntoa(ndh->source_ip), ntohs(ndh->source_port) );
287	fflush(stdout);
288#endif
289    return((p == NULL) ? -1 : 0);
290}
291/* Question Section */
292#define QS_TYPE_NB		0x0020
293#define QS_TYPE_NBSTAT	0x0021
294#define QS_CLAS_IN		0x0001
295typedef struct {
296	u_short	type;	/* The type of Request */
297	u_short	class;	/* The class of Request */
298} NBTNsQuestion;
299
300static u_char *
301AliasHandleQuestion(
302    u_short count,
303							NBTNsQuestion *q,
304    char *pmax,
305							NBTArguments  *nbtarg)
306{
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
317		/* Type and Class filed */
318		switch ( ntohs(q->type) ) {
319			case QS_TYPE_NB:
320			case QS_TYPE_NBSTAT:
321				q= q+1;
322			break;
323			default:
324#ifdef DEBUG
325				printf("\nUnknown Type on Question %0x\n", ntohs(q->type) );
326#endif
327			break;
328		}
329		count--;
330	}
331
332	/* Set up to out of Question Section */
333	return ((u_char *)q);
334}
335
336/* Resource Record */
337#define RR_TYPE_A		0x0001
338#define RR_TYPE_NS		0x0002
339#define RR_TYPE_NULL	0x000a
340#define RR_TYPE_NB		0x0020
341#define RR_TYPE_NBSTAT	0x0021
342#define RR_CLAS_IN		0x0001
343#define SizeOfNsResource	8
344typedef struct {
345 	u_short type;
346 	u_short class;
347 	unsigned int ttl;
348 	u_short rdlen;
349} NBTNsResource;
350
351#define SizeOfNsRNB			6
352typedef struct {
353	u_short g:1, ont:2, resv:13;
354	struct	in_addr	addr;
355} NBTNsRNB;
356
357static u_char *
358AliasHandleResourceNB(
359    NBTNsResource *q,
360    char *pmax,
361							   NBTArguments  *nbtarg)
362{
363	NBTNsRNB	*nb;
364	u_short bcount;
365
366	if (q == NULL || (char *)(q + 1) > pmax)
367		return(NULL);
368	/* Check out a length */
369	bcount = ntohs(q->rdlen);
370
371	/* Forward to Resource NB position */
372	nb = (NBTNsRNB *)((u_char *)q + SizeOfNsResource);
373
374	/* Processing all in_addr array */
375#ifdef DEBUG
376	printf("  NB rec[%s", inet_ntoa(nbtarg->oldaddr));
377            printf("->%s, %d bytes] ",inet_ntoa(nbtarg->newaddr ), bcount);
378#endif
379	while ( nb != NULL && bcount != 0 )  {
380		if ((char *)(nb + 1) > pmax) {
381			nb = NULL;
382			break;
383		}
384#ifdef DEBUG
385		printf("<%s>", inet_ntoa(nb->addr) );
386#endif
387		if (!bcmp(&nbtarg->oldaddr,&nb->addr, sizeof(struct in_addr) ) ) {
388			if ( *nbtarg->uh_sum != 0 ) {
389            	int acc;
390            	u_short *sptr;
391
392            	sptr = (u_short *) &(nb->addr);
393            	acc = *sptr++;
394            	acc += *sptr;
395            	sptr = (u_short *) &(nbtarg->newaddr);
396            	acc -= *sptr++;
397            	acc -= *sptr;
398            	ADJUST_CHECKSUM(acc, *nbtarg->uh_sum);
399			}
400
401			nb->addr = nbtarg->newaddr;
402#ifdef DEBUG
403			printf("O");
404#endif
405		}
406#ifdef DEBUG
407		 else {
408			printf(".");
409		}
410#endif
411		nb=(NBTNsRNB *)((u_char *)nb + SizeOfNsRNB);
412	 	bcount -= SizeOfNsRNB;
413	}
414	if (nb == NULL || (char *)(nb + 1) > pmax) {
415		nb = NULL;
416	}
417
418	return ((u_char *)nb);
419}
420
421#define SizeOfResourceA		6
422typedef struct {
423	struct	in_addr	addr;
424} NBTNsResourceA;
425
426static u_char *
427AliasHandleResourceA(
428    NBTNsResource *q,
429    char *pmax,
430						 	  NBTArguments  *nbtarg)
431{
432	NBTNsResourceA	*a;
433	u_short bcount;
434
435	if (q == NULL || (char *)(q + 1) > pmax)
436		return(NULL);
437
438	/* Forward to Resource A position */
439	a = (NBTNsResourceA *)( (u_char *)q + sizeof(NBTNsResource) );
440
441	/* Check out of length */
442	bcount = ntohs(q->rdlen);
443
444	/* Processing all in_addr array */
445#ifdef DEBUG
446	printf("Arec [%s", inet_ntoa(nbtarg->oldaddr));
447        printf("->%s]",inet_ntoa(nbtarg->newaddr ));
448#endif
449	while ( bcount != 0 )  {
450		if (a == NULL || (char *)(a + 1) > pmax)
451			return(NULL);
452#ifdef DEBUG
453		printf("..%s", inet_ntoa(a->addr) );
454#endif
455		if ( !bcmp(&nbtarg->oldaddr, &a->addr, sizeof(struct in_addr) ) ) {
456			if ( *nbtarg->uh_sum != 0 ) {
457            	int acc;
458            	u_short *sptr;
459
460            	sptr = (u_short *) &(a->addr);		 /* Old */
461            	acc = *sptr++;
462            	acc += *sptr;
463            	sptr = (u_short *) &nbtarg->newaddr; /* New */
464            	acc -= *sptr++;
465            	acc -= *sptr;
466            	ADJUST_CHECKSUM(acc, *nbtarg->uh_sum);
467			}
468
469			a->addr = nbtarg->newaddr;
470		}
471		a++;	/*XXXX*/
472		bcount -= SizeOfResourceA;
473	}
474	if (a == NULL || (char *)(a + 1) > pmax)
475		a =  NULL;
476	return ((u_char *)a);
477}
478
479typedef struct {
480	u_short opcode:4, flags:8, resv:4;
481} NBTNsResourceNULL;
482
483static u_char *
484AliasHandleResourceNULL(
485    NBTNsResource *q,
486    char *pmax,
487						 	     NBTArguments  *nbtarg)
488{
489	NBTNsResourceNULL	*n;
490	u_short bcount;
491
492	if (q == NULL || (char *)(q + 1) > pmax)
493		return(NULL);
494
495	/* Forward to Resource NULL position */
496	n = (NBTNsResourceNULL *)( (u_char *)q + sizeof(NBTNsResource) );
497
498	/* Check out of length */
499	bcount = ntohs(q->rdlen);
500
501	/* Processing all in_addr array */
502	while ( bcount != 0 )  {
503		if ((char *)(n + 1) > pmax) {
504			n = NULL;
505			break;
506		}
507		n++;
508		bcount -= sizeof(NBTNsResourceNULL);
509	}
510	if ((char *)(n + 1) > pmax)
511		n = NULL;
512
513	return ((u_char *)n);
514}
515
516static u_char *
517AliasHandleResourceNS(
518    NBTNsResource *q,
519    char *pmax,
520						 	     NBTArguments  *nbtarg)
521{
522	NBTNsResourceNULL	*n;
523	u_short bcount;
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	if (q == NULL || (char *)(q + 1) > pmax)
557		return(NULL);
558
559	/* Forward to Resource NBSTAT position */
560	n = (NBTNsResourceNBSTAT *)( (u_char *)q + sizeof(NBTNsResource) );
561
562	/* Check out of length */
563	bcount = ntohs(q->rdlen);
564
565	if (q == NULL || (char *)((u_char *)n + bcount) > pmax)
566		return(NULL);
567	else
568	return ((u_char *)n + bcount);
569}
570
571static u_char *
572AliasHandleResource(
573    u_short count,
574							NBTNsResource *q,
575    char *pmax,
576    NBTArguments
577    *nbtarg)
578{
579	while ( count != 0 ) {
580		/* Resource Record Name Filed */
581		q = (NBTNsResource *)AliasHandleName( (u_char *)q, pmax );
582
583		if (q == NULL || (char *)(q + 1) > pmax)
584			break;
585#ifdef DEBUG
586		printf("\n  type=%02x, count=%d\n", ntohs(q->type), count );
587#endif
588
589		/* Type and Class filed */
590		switch ( ntohs(q->type) ) {
591			case RR_TYPE_NB:
592				q = (NBTNsResource *)AliasHandleResourceNB(
593				    q,
594				    pmax,
595				    nbtarg
596				);
597				break;
598			case RR_TYPE_A:
599				q = (NBTNsResource *)AliasHandleResourceA(
600				    q,
601				    pmax,
602				    nbtarg
603				);
604				break;
605			case RR_TYPE_NS:
606				q = (NBTNsResource *)AliasHandleResourceNS(
607				    q,
608				    pmax,
609				    nbtarg
610				);
611				break;
612			case RR_TYPE_NULL:
613				q = (NBTNsResource *)AliasHandleResourceNULL(
614				    q,
615				    pmax,
616				    nbtarg
617				);
618				break;
619			case RR_TYPE_NBSTAT:
620				q = (NBTNsResource *)AliasHandleResourceNBSTAT(
621				    q,
622				    pmax,
623				    nbtarg
624				);
625				break;
626			default:
627#ifdef DEBUG
628				printf(
629				    "\nUnknown Type of Resource %0x\n",
630				    ntohs(q->type)
631				);
632#endif
633				break;
634		}
635		count--;
636	}
637	fflush(stdout);
638	return ((u_char *)q);
639}
640
641int AliasHandleUdpNbtNS(
642	struct ip 		  	*pip,	 /* IP packet to examine/patch */
643	struct alias_link 	*link,
644	struct in_addr		*alias_address,
645	u_short 			*alias_port,
646	struct in_addr		*original_address,
647	u_short 			*original_port )
648{
649    struct udphdr *	uh;
650	NbtNSHeader	  * nsh;
651	u_char		  * p;
652	char		*pmax;
653	NBTArguments    nbtarg;
654
655	/* Set up Common Parameter */
656	nbtarg.oldaddr	=	*alias_address;
657	nbtarg.oldport	=	*alias_port;
658	nbtarg.newaddr	=	*original_address;
659	nbtarg.newport	=	*original_port;
660
661    /* Calculate data length of UDP packet */
662    uh =  (struct udphdr *) ((char *) pip + (pip->ip_hl << 2));
663	nbtarg.uh_sum	=	&(uh->uh_sum);
664	nsh = (NbtNSHeader *)((char *)uh + (sizeof(struct udphdr)));
665	p = (u_char *)(nsh + 1);
666    pmax = (char *)uh + ntohs( uh->uh_ulen );
667
668    if ((char *)(nsh + 1) > pmax)
669	return(-1);
670
671#ifdef DEBUG
672    printf(" [%s] ID=%02x, op=%01x, flag=%02x, rcode=%01x, qd=%04x"
673	   ", an=%04x, ns=%04x, ar=%04x, [%d]-->",
674		nsh->dir ? "Response": "Request",
675		nsh->nametrid,
676		nsh->opcode,
677		nsh->nmflags,
678		nsh->rcode,
679		ntohs(nsh->qdcount),
680		ntohs(nsh->ancount),
681		ntohs(nsh->nscount),
682		ntohs(nsh->arcount),
683		(int)((u_char *)p -(u_char *)nsh)
684    );
685#endif
686
687	/* Question Entries */
688	if (ntohs(nsh->qdcount) !=0 ) {
689	p = AliasHandleQuestion(
690	    ntohs(nsh->qdcount),
691	    (NBTNsQuestion *)p,
692	    pmax,
693	    &nbtarg
694	);
695	}
696
697	/* Answer Resource Records */
698	if (ntohs(nsh->ancount) !=0 ) {
699	p = AliasHandleResource(
700	    ntohs(nsh->ancount),
701	    (NBTNsResource *)p,
702	    pmax,
703	    &nbtarg
704	);
705	}
706
707	/* Authority Resource Recodrs */
708	if (ntohs(nsh->nscount) !=0 ) {
709	p = AliasHandleResource(
710	    ntohs(nsh->nscount),
711	    (NBTNsResource *)p,
712	    pmax,
713	    &nbtarg
714	);
715	}
716
717	/* Additional Resource Recodrs */
718	if (ntohs(nsh->arcount) !=0 ) {
719	p = AliasHandleResource(
720	    ntohs(nsh->arcount),
721	    (NBTNsResource *)p,
722	    pmax,
723	    &nbtarg
724	);
725	}
726
727#ifdef DEBUG
728	if (nsh->rcode)
729	 	PrintRcode(nsh->rcode);
730#endif
731    return ((p == NULL) ? -1 : 0);
732}
733