Xref: utzoo comp.os.os2.misc:31 comp.protocols.tcp-ip:12359 comp.protocols.tcp-ip.ibmpc:3431 Path: utzoo!attcan!uunet!aplcen!samsung!rex!ames!excelan!bbaker From: bbaker@na.novell.com (Brad Baker) Newsgroups: comp.os.os2.misc,comp.protocols.tcp-ip,comp.protocols.tcp-ip.ibmpc Subject: REPOST: NBA Netbios Broadcast Agent (2of2) Message-ID: <1607@excelan.COM> Date: 26 Jul 90 17:08:48 GMT Sender: news@excelan.COM Reply-To: bbaker@na.novell.com (Brad Baker) Followup-To: comp.os.os2 Organization: Novell, San Jose, CA. Lines: 678 REPOST: In straight ascii file 2of2 (source) Sources for NBA (OS2 and Unix) Netbios Broadcast Agent Effectively internets the Netbios Name service. This program is useful for effectively combining netbios networks that are separated on the internet. It could also be used to connect to a remote Unix SMB server allowing for file and print services across the internet. Written to the BSD 4.3 sockets interface. It has been compiled and run on 68XXX and SPARC Unix machines, as well as OS/2, with no code changes. The necessary files are: makefile (os2) name.h (os2,unix) nba.h (os2,unix) nba.c (os2,unix) See the documentation header in nba.c for more extensive info. /* ---------------------------------------------------------------------------- Copyright (C) 1990 Novell, Inc. This software may be freely copied and distributed, provided the above copyright notice is included. It may not be sold, in whole or in part, without the prior written consent of Novell, Inc. ---------------------------------------------------------------------------- NBA.C Netbios Broadcast Agent by Brad Baker This is SAMPLE source code, NOT a Novell product, and will not be supported by the technical staff of Novell. DESCRIPTION: This program provides an example of how to forward name service broadcasts to remote netbios networks connected via gateways or routers which do not usually forward such packets. It is designed to be run in either Server mode or Agent mode. Server mode requires that this program (NBA) be run on the local network. It receives netbios name query broadcasts (status=0x110) and replies with a query response (status=0x8500), if the query name and internet address entry is found in the HOSTS file. No reply packet is sent if the name is not found in the HOSTS file. Agent mode involves running one NBA on the local net, and one on the remote network. The local NBA receives broadcasts on the local network, saves pertinent packet information such as client address, port, transaction ID, etc., in a control block (header). The packet is then prepended with the NBA header, and then forwarded to the remote NBA. When it is received, the remote NBA strips off the header, saves (enqueues) the header, and then the original (de-encapsulated) netbios packet is broadcast on the remote network. When the remote NBA receive a reply, if at all, to the broadcast, the header information is dequeued based on the the transaction id of the reply packet. The destination address, port, and tid of the reply packet is then set to the (client) values stored in the dequeued NBA header and the resulting packet is sent (directed) back to the the client node which made the original broadcast. This forwarding of the broadcast and reply packets, in effect, internets the name service. FEATURES/LIMITATIONS: * NBA is written to be very portable. It will run on 680xx and Sparc Unix machines as well as 80286/386 OS2 machines and should be easily ported to DOS. * This program makes extensive use of the HOSTS file. Entries must exist for BROADCAST, LOCALHOST, as well as any names requiring a response when running NBA in server mode. If subnetting is in use on the network the BROADCAST address of the form WW.YY.0xff.0xff may not work on some BSD implementations. In this case an appropriate subnet broadcast address should be used. A subnet broadcast address has the form: WW.XX.YY.ZZ where WW.XX = the network portion address ZZ = the host portion address (all set) YY = for a node with a subnet mask of 0xfc, this defines the upper 6 bits of the byte as the subnet portion (set to the subnet value) and the lower 2 bits (both set) as part of the host portion. example: subnet mask = 0xfc network = 0x82.0x39 broadcast on subnet 1 = 0x82.0x39.0x07.0xff * In order to run NBA (port 137) on Unix requires super-user privilige The port value should only be changed during testing, and should always be 137 as this is the UDP name service port. * When running NBA in server mode, entries in the HOSTS file should be in upper case. * Although an agent may receive NBA packets from any number of remote NBA agents, it will only forward broadcasts to one remote NBA agent. In other words, an NBA will not simultaneously forward a packet to multiple remote NBA's. This can be accomplished by running multiple NBA's on the local net (on different machines) each of which will receive broadcasts, but forward the packets to different remote agents/networks. ENHANCEMENTS: * Change the queuing mechanism to a more dynamic one. At present the queue is a static array. * Make this program run as a detached process, TSR, or PM application with an improved user interface. * Provide for the support of multiple remote agents as described in the LIMITATIONS section above. BUILD TOOLS: OS2 This program was built using Novell's Lan Workplace for OS/2 (BSD 4.3 sockets), MSC 5.10, MS link 5.01.21, POLYTRON Polymake V3.1. UNIX C compiler BUILD INSTRUCTIONS: required files are: nba.c, nba.h, name.h, makefile(os2) unix> cc nba.c os2> make nba USAGE: nba [[-d] [[-a]{ipaddr}] [[-p]{port}] [[-w]{wport}]] where ipaddr = the Internet Address of the remote NBA Agent. port = the port used for name service transactions. Default is 137 (UDP Name Service Port) wkport = the "well known port" used for NBA transactions. Default is 3000 -d = Debug flag. Debug mode prints messages to STDERR. examples: >nba runs in silent server mode, port 137 >nba -d runs in debug server mode, port 137 >nba -d -a130.50.5.115 -p2000 -w5000 runs in agent mode (for testing), port 2000, NBA port 5000, forwarding broadcasts to the remote agent at address 130.50.5.115. */ #include #include #include #include #include #include #include #include #include #include "name.h" #include "nba.h" #ifdef OS2 #include #endif struct sockaddr_in sock = {AF_INET}; struct sockaddr_in bcast = {AF_INET}; struct sockaddr_in from = {AF_INET}; struct namepkt nspkt; struct ba_pkt bapkt; struct ba_header baheader; struct ba_header baq[BAQSIZE]; struct hostent *host; struct in_addr inetaddr; struct in_addr inet_makeaddr(); ULONG host_addr = 0L; ULONG bcast_addr = 0L; ULONG agent_addr = 0L; ULONG ipaddr; ULONG ipnet; USHORT baqindex = 0; int nspkt_size, bapkt_size; USHORT port = NAME_SERVICE_UDP_PORT; /* the ns listen port */ USHORT wkport = NBA_WK_PORT; /* the ba listen port */ ULONG batidx = 0; int len = sizeof(from); int debug = 0; int sig(); int fd, fda; char myname[16]; char othername[16]; BOOL use_host_file = FALSE; fd_set sockset; main(argc, argv) int argc; char **argv; { char *s; int nfound; int setres, optval; signal(SIGINT /* SIGHUP */ , sig); setbuf(stdout, 0); setbuf(stderr, 0); while (--argc > 0 && (*++argv)[0] == '-') { s = argv[0] + 1; switch (*s) { case 'd': debug = 1; fprintf(stderr, "debug enabled\n"); break; case 'p': port = atoi(++s); break; case 'w': wkport = atoi(++s); break; case 'a': #ifdef OS2 ipnet = inet_addr(++s); ipaddr = inet_network(s); ipaddr = htons((short) ipaddr); agent_addr = (ipaddr << 16) | ipnet; /* inetaddr = inet_makeaddr(ipnet,ipaddr); */ /* agent_addr = inetaddr.S_un.S_addr; */ #else agent_addr = inet_addr(++s); #endif break; default: fprintf(stderr, "usage: %s nba [[-d] [[-a]{ipaddr}] [[-p]{port}] [[-w]{wport}]]\n", argv[0]); exit(1); } } if (!agent_addr) use_host_file = TRUE; /* get local network information */ if (gethostname(myname, sizeof(myname) - 1)) { perror("nba: gethostname()"); exit(1); } if (debug) fprintf(stderr, "nba: myname = %s\n", myname); if (strlen(myname) > 15) { fprintf(stderr, "nba: - name too long\n"); exit(1); } host = gethostbyname(myname); if (host) { bcopy(host->h_addr, (char *) &host_addr, host->h_length); } else { perror("nba: gethostbyname() \n"); exit(1); } if (debug) fprintf(stderr, "host_addr :0x%lx\n", ntohl(host_addr)); if (debug && (agent_addr)) fprintf(stderr, "agent_addr :0x%lx\n", ntohl(agent_addr)); /* get broadcast IP Address */ host = gethostbyname("broadcast"); if (host) { bcopy(host->h_addr, (char *) &bcast_addr, host->h_length); } else { perror("nba: gethostbyname() \n"); exit(1); } cvt(myname); /* convert to upper case */ /* get and bind a socket for netbios name service */ if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { perror("nba: socket()"); exit(1); } sock.sin_port = htons(port); sock.sin_addr.S_un.S_addr = 0l; if (bind(fd, &sock, sizeof(sock)) < 0) { perror("nba: bind()"); exit(1); } /* get and bind a socket for agent receive */ if ((fda = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { perror("nba: socket()"); exit(1); } sock.sin_port = htons(wkport); sock.sin_addr.S_un.S_addr = host_addr; if (bind(fda, &sock, sizeof(sock)) < 0) { perror("nba: bind()"); exit(1); } /* process all Broadcast Repeater (BA) and Name Service (NS) packets */ while (1) { FD_ZERO(&sockset); FD_SET(fd, &sockset); FD_SET(fda, &sockset); /* wait for a pkt on one of the two sockets */ if ((nfound = select(32, &sockset, NULL, NULL, NULL)) < 0) { perror("nba: select()"); exit(1); } /* test for NS socket has data */ if (FD_ISSET(fd, &sockset)) { process_ns(); /* process NS packets */ } /* test for BA socket has data */ if (FD_ISSET(fda, &sockset)) { process_ba(); /* process BA packets */ } } } /* ba */ /* * process_ba() - process incoming BA agent packets */ process_ba() { int i, chsent; /* read the socket */ if ((bapkt_size = recvfrom(fda, &bapkt, sizeof(struct ba_pkt), 0, &from, &len)) < 0) { perror("nba: recvfrom()"); exit(1); } if (debug) { fprintf(stderr, "|-- NBA PKT -------------------------------\n"); fprintf(stderr, "| size = 0x%x\n", bapkt_size); fprintf(stderr, "| port = 0x%x\n", ntohs(from.sin_port)); fprintf(stderr, "| addr = 0x%lx\n", ntohl(from.sin_addr.S_un.S_addr)); fprintf(stderr, "|----------------------------------------\n"); } /* set up the agent tid and change the pkt tid */ bapkt.baheader.ba_tid = htonl(++batidx); bapkt.namepkt.header.nm_tid = NLtoNS(bapkt.baheader.ba_tid); /* enqueue the header */ while (baenq(&bapkt)) { }; /* keep trying until success (a header * expires) */ /* forward (broadcast) the packet to local net */ sock.sin_port = htons(port); sock.sin_addr.S_un.S_addr = bcast_addr; if ((chsent = sendto(fd, &bapkt.namepkt, (int) (bapkt_size - BAHEADERSIZE), 0, &sock, SOCKADDRSIZE)) < 0) { perror("nba: sendto()"); exit(1); } if (debug) fprintf(stderr, ">>> Broadcast 0x%x bytes to Port:0x%x Addr:0x%lx\n", chsent, ntohs(sock.sin_port), ntohl(sock.sin_addr.S_un.S_addr)); } /* process_ba */ /* * process_ns() - process incoming name service packets */ process_ns() { int i, chsent; struct ba_header *phdr; /* read the socket */ if ((nspkt_size = recvfrom(fd, &bapkt.namepkt, sizeof(struct namepkt), 0, &from, &len)) < 0) { perror("nba: recvfrom()"); exit(1); } /* if broadcast encapsulate the NS packet and send to BA agent */ if (!(ntohs(bapkt.namepkt.header.status) & NS_RES_MASK)) { /* skip the broadcast if it came from this node */ if (from.sin_addr.S_un.S_addr != host_addr) { if (debug) { fprintf(stderr, "|-- NETBIOS PKT --------------------------\n"); fprintf(stderr, "| size = 0x%x\n", nspkt_size); fprintf(stderr, "| port = 0x%x\n", ntohs(from.sin_port)); fprintf(stderr, "| addr = 0x%lx\n", ntohl(from.sin_addr.S_un.S_addr)); fprintf(stderr, "| tid = 0x%x\n", ntohs(bapkt.namepkt.header.nm_tid)); fprintf(stderr, "| type = 0x%x\n", ntohs(bapkt.namepkt.header.status)); fprintf(stderr, "|--------------------------------------\n"); } /* * if we are running in server mode process the pkt * locally */ if (use_host_file) { respond_local(&bapkt.namepkt); return; } /* set up the baheader */ baheader.client_addr = from.sin_addr.S_un.S_addr; baheader.client_port = NStoNL(from.sin_port); baheader.host_addr = host_addr; baheader.ns_tid = NStoNL(bapkt.namepkt.header.nm_tid); /* * prepend the packet (in bapkt.namepkt) with the * baheader */ bcopy((char *) &baheader, (char *) &bapkt, BAHEADERSIZE); /* send the packet to the other agent */ sock.sin_port = htons(wkport); /* BA "well known" port */ sock.sin_addr.S_un.S_addr = agent_addr; /* BA agent address */ if ((chsent = sendto(fd, &bapkt, (int) (nspkt_size + BAHEADERSIZE), 0, &sock, SOCKADDRSIZE)) < 0) { perror("nba: sendto()"); exit(1); } if (debug) fprintf(stderr, ">>> Sent 0x%x bytes to Port:0x%x Addr:0x%lx\n", chsent, ntohs(sock.sin_port), ntohl(sock.sin_addr.S_un.S_addr)); } } /* not a broadcast. Lookup in the queue, set up reply pkt if found */ else { if (debug) { fprintf(stderr, "|-- NETBIOS PKT --------------------------\n"); fprintf(stderr, "| size = 0x%x\n", nspkt_size); fprintf(stderr, "| port = 0x%x\n", ntohs(from.sin_port)); fprintf(stderr, "| addr = 0x%lx\n", ntohl(from.sin_addr.S_un.S_addr)); fprintf(stderr, "| tid = 0x%x\n", ntohs(bapkt.namepkt.header.nm_tid)); fprintf(stderr, "| type = 0x%x\n", ntohs(bapkt.namepkt.header.status)); fprintf(stderr, "|--------------------------------------\n"); } /* dequeue based on the nm_tid */ if (!(badeq(NStoNL(bapkt.namepkt.header.nm_tid), &bapkt))) { /* restore the pkt tid */ bapkt.namepkt.header.nm_tid = NLtoNS(bapkt.baheader.ns_tid); /* send the packet to the remote client */ sock.sin_port = NLtoNS(bapkt.baheader.client_port); sock.sin_addr.S_un.S_addr = bapkt.baheader.client_addr; if ((chsent = sendto(fd, &bapkt.namepkt, nspkt_size, 0, &sock, SOCKADDRSIZE)) < 0) { perror("nba: sendto()"); exit(1); } if (debug) fprintf(stderr, ">>> Sent 0x%x bytes to Port:0x%x Addr:0x%lx\n", chsent, ntohs(sock.sin_port), ntohl(sock.sin_addr.S_un.S_addr)); } } } /* process_ns */ /* * baenq - enqueue a BA header. returns: 0 if success At present the queue is * just an array of headers. */ int baenq(header) struct ba_header *header; { int i; ULONG t; /* place the header at an empty (expired) location */ for (i = 0; i < BAQSIZE; i++) { time(&t); if ((t - baq[i].time) > BAPKTLIFE) { bcopy((char *) header, (char *) &baq[i], BAHEADERSIZE); baq[i].time = t; /* time stamp the header */ return (0); } } if (debug) fprintf(stderr, "XXX Queue full.\n"); return (1); } /* * badeq - dequeue a baheader based on a BATID returns: 0 if success At * present the queue is just an array of headers. */ int badeq(tid, header) ULONG tid; struct ba_header *header; { int i; ULONG t; /* search for header ba_tid matching the tid */ for (i = 0; i < BAQSIZE; i++) { time(&t); /* skip the dead ones */ if ((t - baq[i].time) < BAPKTLIFE) { if (baq[i].ba_tid == tid) { bcopy((char *) header, (char *) &baq[i], BAHEADERSIZE); return (0); } } } if (debug) fprintf(stderr, "XXX Header not found.\n"); return (1); } /* * cvt - convert MYNAME to upper case */ cvt(src) char *src; { int i; for (i = 0; i < strlen(src); i++) { if (isalpha(src[i])) src[i] = toupper(src[i]); } /* fill with blanks */ for (; i <= 15; i++) src[i] = ' '; } /* * sig - signal handling */ sig() { #ifdef OS2 soclose(fd); soclose(fda); #endif exit(1); } /* * dns2nb - convert DNS name to NETBIOS name */ dns2nb(dnsp, nbp) char *dnsp; /* ptr to a DNS name */ char *nbp; /* ptr to a 16 byte buffer */ { unsigned char idx; unsigned char left; unsigned char right; /* * a compressed Netbios name is always 32 bytes long, so we can loop * for a fixed amount, as long as the initial length byte is skipped */ for (dnsp++, idx = 0; (idx < SZ_NCBNAME); idx++, nbp++) { left = (*dnsp++ - 'A') << 4; right = (*dnsp++ - 'A'); *nbp = left + right; } } /* end dns2nb() */ /* * nb2dns - convert NETBIOS name to DNS (first level encoded) name */ nb2dns(nbp, dnsp) char *dnsp; /* ptr to a 32 byte DNS name */ char *nbp; /* ptr to a 16 byte buffer */ { unsigned char idx; unsigned char left; unsigned char right; for (idx = 0; (idx < SZ_NCBNAME); idx++, nbp++, dnsp++) { *dnsp = (*nbp >> 4) + 'A'; *(++dnsp) = (*nbp & 0x0f) + 'A'; } } /* end dns2nb() */ /* * respond_local - look up name in the hosts file and respond to the query if * found. */ respond_local(nspkt) struct namepkt *nspkt; { char nbname[16]; char temp[17]; char *chp; struct rr_trailer *trailer; struct rr_info *info; int count; dns2nb(nspkt->records, nbname); /* strip the trailing spaces (null terminate it) */ for (chp = nbname; isalpha(*chp); chp++); *chp = 0; if (debug) fprintf(stderr, "Hosts file look-up of \"%s\" \n", nbname); /* get the address from the hosts file */ host = gethostbyname(nbname); if (host) { if (debug) fprintf(stderr, "Address found : 0x%lx\n", ntohl(*(long *) host->h_addr)); trailer = (struct rr_trailer *) ((char *) nspkt + nspkt_size - 4); nspkt->header.status = htons(NM_QRY_RES); nspkt->header.qdcount = 0; nspkt->header.ancount = htons(1); trailer->ttl = htons(1); trailer->length = htons(6); info = (struct rr_info *) trailer; info->flags = 0; info->nbaddr = ntohl(*(long *) host->h_addr); /* send the response packet (back) to the client */ if ((count = sendto(fd, nspkt, nspkt_size + sizeof(struct rr_info) - 4, 0, &from, sizeof(struct sockaddr))) < 0) { perror("nsd: sendto()"); exit(1); } if (debug) fprintf(stderr, ">>> Sent 0x%x bytes to Port:0x%x Addr:0x%lx\n", count, ntohs(from.sin_port), ntohl(from.sin_addr.S_un.S_addr)); } else { /* gethostbyname() failed */ if (debug) fprintf(stderr, "Address not found.\n"); } }