#include /*- * Written by Atsushi Murai * Copyright (c) 1998, System Planning and Engineering Co. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * TODO: * oClean up. * oConsidering for word alignment for other platform. */ #include __FBSDID("$FreeBSD$"); /* alias_nbt.c performs special processing for NetBios over TCP/IP sessions by UDP. Initial version: May, 1998 (Atsushi Murai ) See HISTORY file for record of revisions. */ /* Includes */ #ifdef _KERNEL #include #include #include #include #else #include #include #include #include #endif #include #include #include #include #ifdef _KERNEL #include #include #else #include #include #endif #define NETBIOS_NS_PORT_NUMBER 137 #define NETBIOS_DGM_PORT_NUMBER 138 static int AliasHandleUdpNbt(struct libalias *, struct ip *, struct alias_link *, struct in_addr *, u_short); static int AliasHandleUdpNbtNS(struct libalias *, struct ip *, struct alias_link *, struct in_addr *, u_short *, struct in_addr *, u_short *); static int fingerprint1(struct libalias *la, struct alias_data *ah) { if (ah->dport == NULL || ah->sport == NULL || ah->lnk == NULL || ah->aaddr == NULL || ah->aport == NULL) return (-1); if (ntohs(*ah->dport) == NETBIOS_DGM_PORT_NUMBER || ntohs(*ah->sport) == NETBIOS_DGM_PORT_NUMBER) return (0); return (-1); } static int protohandler1(struct libalias *la, struct ip *pip, struct alias_data *ah) { return (AliasHandleUdpNbt(la, pip, ah->lnk, ah->aaddr, *ah->aport)); } static int fingerprint2(struct libalias *la, struct alias_data *ah) { if (ah->dport == NULL || ah->sport == NULL || ah->lnk == NULL || ah->aaddr == NULL || ah->aport == NULL) return (-1); if (ntohs(*ah->dport) == NETBIOS_NS_PORT_NUMBER || ntohs(*ah->sport) == NETBIOS_NS_PORT_NUMBER) return (0); return (-1); } static int protohandler2in(struct libalias *la, struct ip *pip, struct alias_data *ah) { AliasHandleUdpNbtNS(la, pip, ah->lnk, ah->aaddr, ah->aport, ah->oaddr, ah->dport); return (0); } static int protohandler2out(struct libalias *la, struct ip *pip, struct alias_data *ah) { return (AliasHandleUdpNbtNS(la, pip, ah->lnk, &pip->ip_src, ah->sport, ah->aaddr, ah->aport)); } /* Kernel module definition. */ struct proto_handler handlers[] = { { .pri = 130, .dir = IN|OUT, .proto = UDP, .fingerprint = &fingerprint1, .protohandler = &protohandler1 }, { .pri = 140, .dir = IN, .proto = UDP, .fingerprint = &fingerprint2, .protohandler = &protohandler2in }, { .pri = 140, .dir = OUT, .proto = UDP, .fingerprint = &fingerprint2, .protohandler = &protohandler2out }, { EOH } }; static int mod_handler(module_t mod, int type, void *data) { int error; switch (type) { case MOD_LOAD: error = 0; LibAliasAttachHandlers(handlers); break; case MOD_UNLOAD: error = 0; LibAliasDetachHandlers(handlers); break; default: error = EINVAL; } return (error); } #ifdef _KERNEL static #endif moduledata_t alias_mod = { "alias_nbt", mod_handler, NULL }; #ifdef _KERNEL DECLARE_MODULE(alias_nbt, alias_mod, SI_SUB_DRIVERS, SI_ORDER_SECOND); MODULE_VERSION(alias_nbt, 1); MODULE_DEPEND(alias_nbt, libalias, 1, 1, 1); #endif typedef struct { struct in_addr oldaddr; u_short oldport; struct in_addr newaddr; u_short newport; u_short *uh_sum; } NBTArguments; typedef struct { unsigned char type; unsigned char flags; u_short id; struct in_addr source_ip; u_short source_port; u_short len; u_short offset; } NbtDataHeader; #define OpQuery 0 #define OpUnknown 4 #define OpRegist 5 #define OpRelease 6 #define OpWACK 7 #define OpRefresh 8 typedef struct { u_short nametrid; u_short dir: 1, opcode:4, nmflags:7, rcode:4; u_short qdcount; u_short ancount; u_short nscount; u_short arcount; } NbtNSHeader; #define FMT_ERR 0x1 #define SRV_ERR 0x2 #define IMP_ERR 0x4 #define RFS_ERR 0x5 #define ACT_ERR 0x6 #define CFT_ERR 0x7 #ifdef LIBALIAS_DEBUG static void PrintRcode(u_char rcode) { switch (rcode) { case FMT_ERR: printf("\nFormat Error."); case SRV_ERR: printf("\nSever failure."); case IMP_ERR: printf("\nUnsupported request error.\n"); case RFS_ERR: printf("\nRefused error.\n"); case ACT_ERR: printf("\nActive error.\n"); case CFT_ERR: printf("\nName in conflict error.\n"); default: printf("\n?%c?=%0x\n", '?', rcode); } } #endif /* Handling Name field */ static u_char * AliasHandleName(u_char * p, char *pmax) { u_char *s; u_char c; int compress; /* Following length field */ if (p == NULL || (char *)p >= pmax) return (NULL); if (*p & 0xc0) { p = p + 2; if ((char *)p > pmax) return (NULL); return ((u_char *) p); } while ((*p & 0x3f) != 0x00) { s = p + 1; if (*p == 0x20) compress = 1; else compress = 0; /* Get next length field */ p = (u_char *) (p + (*p & 0x3f) + 1); if ((char *)p > pmax) { p = NULL; break; } #ifdef LIBALIAS_DEBUG printf(":"); #endif while (s < p) { if (compress == 1) { c = (u_char) (((((*s & 0x0f) << 4) | (*(s + 1) & 0x0f)) - 0x11)); #ifdef LIBALIAS_DEBUG if (isprint(c)) printf("%c", c); else printf("<0x%02x>", c); #endif s += 2; } else { #ifdef LIBALIAS_DEBUG printf("%c", *s); #endif s++; } } #ifdef LIBALIAS_DEBUG printf(":"); fflush(stdout); #endif } /* Set up to out of Name field */ if (p == NULL || (char *)p >= pmax) p = NULL; else p++; return ((u_char *) p); } /* * NetBios Datagram Handler (IP/UDP) */ #define DGM_DIRECT_UNIQ 0x10 #define DGM_DIRECT_GROUP 0x11 #define DGM_BROADCAST 0x12 #define DGM_ERROR 0x13 #define DGM_QUERY 0x14 #define DGM_POSITIVE_RES 0x15 #define DGM_NEGATIVE_RES 0x16 static int AliasHandleUdpNbt( struct libalias *la, struct ip *pip, /* IP packet to examine/patch */ struct alias_link *lnk, struct in_addr *alias_address, u_short alias_port ) { struct udphdr *uh; NbtDataHeader *ndh; u_char *p = NULL; char *pmax; (void)la; (void)lnk; /* Calculate data length of UDP packet */ uh = (struct udphdr *)ip_next(pip); pmax = (char *)uh + ntohs(uh->uh_ulen); ndh = (NbtDataHeader *)udp_next(uh); if ((char *)(ndh + 1) > pmax) return (-1); #ifdef LIBALIAS_DEBUG printf("\nType=%02x,", ndh->type); #endif switch (ndh->type) { case DGM_DIRECT_UNIQ: case DGM_DIRECT_GROUP: case DGM_BROADCAST: p = (u_char *) ndh + 14; p = AliasHandleName(p, pmax); /* Source Name */ p = AliasHandleName(p, pmax); /* Destination Name */ break; case DGM_ERROR: p = (u_char *) ndh + 11; break; case DGM_QUERY: case DGM_POSITIVE_RES: case DGM_NEGATIVE_RES: p = (u_char *) ndh + 10; p = AliasHandleName(p, pmax); /* Destination Name */ break; } if (p == NULL || (char *)p > pmax) p = NULL; #ifdef LIBALIAS_DEBUG printf("%s:%d-->", inet_ntoa(ndh->source_ip), ntohs(ndh->source_port)); #endif /* Doing an IP address and Port number Translation */ if (uh->uh_sum != 0) { int acc; u_short *sptr; acc = ndh->source_port; acc -= alias_port; sptr = (u_short *) & (ndh->source_ip); acc += *sptr++; acc += *sptr; sptr = (u_short *) alias_address; acc -= *sptr++; acc -= *sptr; ADJUST_CHECKSUM(acc, uh->uh_sum); } ndh->source_ip = *alias_address; ndh->source_port = alias_port; #ifdef LIBALIAS_DEBUG printf("%s:%d\n", inet_ntoa(ndh->source_ip), ntohs(ndh->source_port)); fflush(stdout); #endif return ((p == NULL) ? -1 : 0); } /* Question Section */ #define QS_TYPE_NB 0x0020 #define QS_TYPE_NBSTAT 0x0021 #define QS_CLAS_IN 0x0001 typedef struct { u_short type; /* The type of Request */ u_short class; /* The class of Request */ } NBTNsQuestion; static u_char * AliasHandleQuestion( u_short count, NBTNsQuestion * q, char *pmax, NBTArguments * nbtarg) { (void)nbtarg; while (count != 0) { /* Name Filed */ q = (NBTNsQuestion *) AliasHandleName((u_char *) q, pmax); if (q == NULL || (char *)(q + 1) > pmax) { q = NULL; break; } /* Type and Class filed */ switch (ntohs(q->type)) { case QS_TYPE_NB: case QS_TYPE_NBSTAT: q = q + 1; break; default: #ifdef LIBALIAS_DEBUG printf("\nUnknown Type on Question %0x\n", ntohs(q->type)); #endif break; } count--; } /* Set up to out of Question Section */ return ((u_char *) q); } /* Resource Record */ #define RR_TYPE_A 0x0001 #define RR_TYPE_NS 0x0002 #define RR_TYPE_NULL 0x000a #define RR_TYPE_NB 0x0020 #define RR_TYPE_NBSTAT 0x0021 #define RR_CLAS_IN 0x0001 #define SizeOfNsResource 8 typedef struct { u_short type; u_short class; unsigned int ttl; u_short rdlen; } NBTNsResource; #define SizeOfNsRNB 6 typedef struct { u_short g: 1 , ont:2, resv:13; struct in_addr addr; } NBTNsRNB; static u_char * AliasHandleResourceNB( NBTNsResource * q, char *pmax, NBTArguments * nbtarg) { NBTNsRNB *nb; u_short bcount; if (q == NULL || (char *)(q + 1) > pmax) return (NULL); /* Check out a length */ bcount = ntohs(q->rdlen); /* Forward to Resource NB position */ nb = (NBTNsRNB *) ((u_char *) q + SizeOfNsResource); /* Processing all in_addr array */ #ifdef LIBALIAS_DEBUG printf("NB rec[%s", inet_ntoa(nbtarg->oldaddr)); printf("->%s, %dbytes] ", inet_ntoa(nbtarg->newaddr), bcount); #endif while (nb != NULL && bcount != 0) { if ((char *)(nb + 1) > pmax) { nb = NULL; break; } #ifdef LIBALIAS_DEBUG printf("<%s>", inet_ntoa(nb->addr)); #endif if (!bcmp(&nbtarg->oldaddr, &nb->addr, sizeof(struct in_addr))) { if (*nbtarg->uh_sum != 0) { int acc; u_short *sptr; sptr = (u_short *) & (nb->addr); acc = *sptr++; acc += *sptr; sptr = (u_short *) & (nbtarg->newaddr); acc -= *sptr++; acc -= *sptr; ADJUST_CHECKSUM(acc, *nbtarg->uh_sum); } nb->addr = nbtarg->newaddr; #ifdef LIBALIAS_DEBUG printf("O"); #endif } #ifdef LIBALIAS_DEBUG else { printf("."); } #endif nb = (NBTNsRNB *) ((u_char *) nb + SizeOfNsRNB); bcount -= SizeOfNsRNB; } if (nb == NULL || (char *)(nb + 1) > pmax) { nb = NULL; } return ((u_char *) nb); } #define SizeOfResourceA 6 typedef struct { struct in_addr addr; } NBTNsResourceA; static u_char * AliasHandleResourceA( NBTNsResource * q, char *pmax, NBTArguments * nbtarg) { NBTNsResourceA *a; u_short bcount; if (q == NULL || (char *)(q + 1) > pmax) return (NULL); /* Forward to Resource A position */ a = (NBTNsResourceA *) ((u_char *) q + sizeof(NBTNsResource)); /* Check out of length */ bcount = ntohs(q->rdlen); /* Processing all in_addr array */ #ifdef LIBALIAS_DEBUG printf("Arec [%s", inet_ntoa(nbtarg->oldaddr)); printf("->%s]", inet_ntoa(nbtarg->newaddr)); #endif while (bcount != 0) { if (a == NULL || (char *)(a + 1) > pmax) return (NULL); #ifdef LIBALIAS_DEBUG printf("..%s", inet_ntoa(a->addr)); #endif if (!bcmp(&nbtarg->oldaddr, &a->addr, sizeof(struct in_addr))) { if (*nbtarg->uh_sum != 0) { int acc; u_short *sptr; sptr = (u_short *) & (a->addr); /* Old */ acc = *sptr++; acc += *sptr; sptr = (u_short *) & nbtarg->newaddr; /* New */ acc -= *sptr++; acc -= *sptr; ADJUST_CHECKSUM(acc, *nbtarg->uh_sum); } a->addr = nbtarg->newaddr; } a++; /* XXXX */ bcount -= SizeOfResourceA; } if (a == NULL || (char *)(a + 1) > pmax) a = NULL; return ((u_char *) a); } typedef struct { u_short opcode:4, flags:8, resv:4; } NBTNsResourceNULL; static u_char * AliasHandleResourceNULL( NBTNsResource * q, char *pmax, NBTArguments * nbtarg) { NBTNsResourceNULL *n; u_short bcount; (void)nbtarg; if (q == NULL || (char *)(q + 1) > pmax) return (NULL); /* Forward to Resource NULL position */ n = (NBTNsResourceNULL *) ((u_char *) q + sizeof(NBTNsResource)); /* Check out of length */ bcount = ntohs(q->rdlen); /* Processing all in_addr array */ while (bcount != 0) { if ((char *)(n + 1) > pmax) { n = NULL; break; } n++; bcount -= sizeof(NBTNsResourceNULL); } if ((char *)(n + 1) > pmax) n = NULL; return ((u_char *) n); } static u_char * AliasHandleResourceNS( NBTNsResource * q, char *pmax, NBTArguments * nbtarg) { NBTNsResourceNULL *n; u_short bcount; (void)nbtarg; if (q == NULL || (char *)(q + 1) > pmax) return (NULL); /* Forward to Resource NULL position */ n = (NBTNsResourceNULL *) ((u_char *) q + sizeof(NBTNsResource)); /* Check out of length */ bcount = ntohs(q->rdlen); /* Resource Record Name Filed */ q = (NBTNsResource *) AliasHandleName((u_char *) n, pmax); /* XXX */ if (q == NULL || (char *)((u_char *) n + bcount) > pmax) return (NULL); else return ((u_char *) n + bcount); } typedef struct { u_short numnames; } NBTNsResourceNBSTAT; static u_char * AliasHandleResourceNBSTAT( NBTNsResource * q, char *pmax, NBTArguments * nbtarg) { NBTNsResourceNBSTAT *n; u_short bcount; (void)nbtarg; if (q == NULL || (char *)(q + 1) > pmax) return (NULL); /* Forward to Resource NBSTAT position */ n = (NBTNsResourceNBSTAT *) ((u_char *) q + sizeof(NBTNsResource)); /* Check out of length */ bcount = ntohs(q->rdlen); if (q == NULL || (char *)((u_char *) n + bcount) > pmax) return (NULL); else return ((u_char *) n + bcount); } static u_char * AliasHandleResource( u_short count, NBTNsResource * q, char *pmax, NBTArguments * nbtarg) { while (count != 0) { /* Resource Record Name Filed */ q = (NBTNsResource *) AliasHandleName((u_char *) q, pmax); if (q == NULL || (char *)(q + 1) > pmax) break; #ifdef LIBALIAS_DEBUG printf("type=%02x, count=%d\n", ntohs(q->type), count); #endif /* Type and Class filed */ switch (ntohs(q->type)) { case RR_TYPE_NB: q = (NBTNsResource *) AliasHandleResourceNB( q, pmax, nbtarg ); break; case RR_TYPE_A: q = (NBTNsResource *) AliasHandleResourceA( q, pmax, nbtarg ); break; case RR_TYPE_NS: q = (NBTNsResource *) AliasHandleResourceNS( q, pmax, nbtarg ); break; case RR_TYPE_NULL: q = (NBTNsResource *) AliasHandleResourceNULL( q, pmax, nbtarg ); break; case RR_TYPE_NBSTAT: q = (NBTNsResource *) AliasHandleResourceNBSTAT( q, pmax, nbtarg ); break; default: #ifdef LIBALIAS_DEBUG printf( "\nUnknown Type of Resource %0x\n", ntohs(q->type) ); fflush(stdout); #endif break; } count--; } return ((u_char *) q); } static int AliasHandleUdpNbtNS( struct libalias *la, struct ip *pip, /* IP packet to examine/patch */ struct alias_link *lnk, struct in_addr *alias_address, u_short * alias_port, struct in_addr *original_address, u_short * original_port) { struct udphdr *uh; NbtNSHeader *nsh; u_char *p; char *pmax; NBTArguments nbtarg; (void)la; (void)lnk; /* Set up Common Parameter */ nbtarg.oldaddr = *alias_address; nbtarg.oldport = *alias_port; nbtarg.newaddr = *original_address; nbtarg.newport = *original_port; /* Calculate data length of UDP packet */ uh = (struct udphdr *)ip_next(pip); nbtarg.uh_sum = &(uh->uh_sum); nsh = (NbtNSHeader *)udp_next(uh); p = (u_char *) (nsh + 1); pmax = (char *)uh + ntohs(uh->uh_ulen); if ((char *)(nsh + 1) > pmax) return (-1); #ifdef LIBALIAS_DEBUG printf(" [%s] ID=%02x, op=%01x, flag=%02x, rcode=%01x, qd=%04x" ", an=%04x, ns=%04x, ar=%04x, [%d]-->", nsh->dir ? "Response" : "Request", nsh->nametrid, nsh->opcode, nsh->nmflags, nsh->rcode, ntohs(nsh->qdcount), ntohs(nsh->ancount), ntohs(nsh->nscount), ntohs(nsh->arcount), (u_char *) p - (u_char *) nsh ); #endif /* Question Entries */ if (ntohs(nsh->qdcount) != 0) { p = AliasHandleQuestion( ntohs(nsh->qdcount), (NBTNsQuestion *) p, pmax, &nbtarg ); } /* Answer Resource Records */ if (ntohs(nsh->ancount) != 0) { p = AliasHandleResource( ntohs(nsh->ancount), (NBTNsResource *) p, pmax, &nbtarg ); } /* Authority Resource Recodrs */ if (ntohs(nsh->nscount) != 0) { p = AliasHandleResource( ntohs(nsh->nscount), (NBTNsResource *) p, pmax, &nbtarg ); } /* Additional Resource Recodrs */ if (ntohs(nsh->arcount) != 0) { p = AliasHandleResource( ntohs(nsh->arcount), (NBTNsResource *) p, pmax, &nbtarg ); } #ifdef LIBALIAS_DEBUG PrintRcode(nsh->rcode); #endif return ((p == NULL) ? -1 : 0); }