
/*
 * nbstatus.c - This file contains code of 'nbstatus' utility which uses
 *              PROCOM NetBEUI to get NetBIOS STATUS information of a NetBIOS
 *              name on the network. It displays only information that is
 *              supported by STATUS service of PROCOM NetBEUI.
 *
 * Usage:
 *	If it is run without arguments, it shows its usage form. The
 *	'StatusName' is a unique name that is registered by a machine on the
 *	network. The 'PortNumber' is an optional argument that is located at
 *	end of NetBIOS name. For example "nbstatus SAMBA 29" will find Master
 *	Browser of SAMBA work group on the network and shows its information.
 *	The 'RawOutputFile' is an optional argument that specifies filename
 *	which used for storing RAW received information from remote node; If
 *	this option is used, the 'PortNumber' must exist to avoid ambiguity.
 *	But, if user have not any 'PortNumber' for a name, a ' ' must exists
 *	instead. A '*' instead of 'StatusName' means that user needs the local
 *	node information.
 *
 * Copyright (c) 1997 by Procom Technology,Inc.
 *
 * This program can be redistributed or modified under the terms of the 
 * GNU General Public License as published by the Free Software Foundation.
 * This program is distributed without any warranty or implied warranty
 * of merchantability or fitness for a particular purpose.
 *
 * See the GNU General Public License for more details.
 *
 */
 

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <linux/if.h>
#include <linux/af_netb.h>


#define PARAMETER_TYPE_MASK	0xF0
#define NAME_TYPE_MASK		0x80
#define NAME_STATUS_MASK	0x07


/*
 * Function: print_report
 *	Prints the received STATUS information to standard output.
 *
 * Parameters:
 *	ns : pointer to netbeui_status structure that contains gotten STATUS
 *	     information.
 *
 * Returns: none
 */

static void
print_report (struct netbeui_status *ns)
{
	int i,
	    non,
	    errval = 0;
	__u8   *tmp, *endp, sl = 0 , top = 2;
	char   *atp = NULL;
	struct nb_status_names   *nsp;
	char   ata[5][11] = { /* Adapter Types Array */
	                   "3174 PEER",
	                   "IBM FDDI",
	                   "ETHERNET",
	                   "PC NETWORK",
	                   "TOKEN RING"
	                  },
	       nsa[5][40] = { /* Name Status Array */
	                   "trying to register",
	                   "registered",
	                   "deregistered",
	                   "detected duplicate",
	                   "detected dup. & deregister pending"
	                  };

	printf("\nStatus information from %s\n", (ns->called_name[0] == '*') ?
	                                         "local host" : ns->called_name);
	printf("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n");


	printf("Length of status information: %d bytes\n", ns->buff_len);

	printf("\nAdapter's encoded address: ");
	for (i=0 , tmp = ns->status_buff ; i < 6 ; i++ , tmp++)
		printf("%2.2X", *tmp);

	switch (*tmp) {
		case 1:
			sl = *(tmp + 3);
			break;

		case 2:
		case 3:
		case 4:
		case 5:
			switch (*(tmp + 2)) {
				case _3174_PEER :
				case IBM_FDDI   :
				case ETHERNET   :
				case PC_NETWORK :
				case TOKEN_RING :
					atp = ata[(*(tmp + 2)) - 0xFB];
					break;

				default :
					atp = NULL;
			}

			top = (*(tmp + 3) & PARAMETER_TYPE_MASK) >> 4;
			sl = *(tmp + 3) & (~PARAMETER_TYPE_MASK);
			break;

		default:
			++errval; /* ERROR */
	}

	if (atp)
		printf("\nAdapter type: %s", atp);
	printf("\n\nSoftware version: %d.%d", *tmp, sl);
	switch (top) {
		case 1:
			printf(" (with support of old NCB parameters)");
			break;

		case 2:
			printf(" (with support of new NCB parameters)");
			break;

		default:
			++errval; /* ERROR */
	}

	tmp += 4;

	printf("\nDuration of reporting period: %d minutes", *((__u16 *) tmp));

	tmp += 38; /* These information not supported by us */

	printf("\n\nMaximum datagram packet size: %d", *((__u16 *) tmp));

	tmp += 6; /* These information not supported by us */

	printf("\nMaximum number of pending sessions: %d", *((__u16 *) tmp));

	tmp += 2;

	printf("\nMaximum size of session data packet: %d", *((__u16 *) tmp));

	tmp += 2;

	non = *((__u16 *) tmp);
	printf("\n\nNumber of names in name table: %d\n\n", non);

	if (ns->buff_len > NB_MIN_STATUS_BUFF_LEN) {
		printf("*****************************************************");
		printf("**************************\n");
		printf("                              N A M E  T A B L E\n");
		printf("*****************************************************");
		printf("**************************\n");
		printf("NameNo  Name                         Type    Status \n");
		printf("-----------------------------------------------------");
		printf("--------------------------\n");

		endp = (__u8 *) (ns->status_buff + ns->buff_len);
		for (tmp += 2 ; tmp < endp ; tmp += 18) {
			int   j = 12;

			nsp = (struct nb_status_names *) tmp;
			printf(" %-5d  ", nsp->name_number);
		
			for (i = 0 ; i < NB_NAME_LEN ; i++) {
				if (isprint(nsp->name[i]))
					putchar(nsp->name[i]);
				else {
					printf("<%2.2x>", (unsigned char) nsp->name[i]);
					j -= 3;
				}
			}
			while (j > 0) {
				j -= 3;
				printf("   ");
			}
		
			if (nsp->name_status & NAME_TYPE_MASK)
				printf(" GROUP   ");
			else
				printf(" UNIQUE  ");

			sl = nsp->name_status & NAME_STATUS_MASK;
			if (sl)
				sl -= 3;
			printf("%s\n", nsa[sl]);
		}
	}

	if (errval) {
		fprintf(stderr, "\nWarning: The status information at least");
		fprintf(stderr, " has %d errors.\n", errval);
	}

	return;
}


/*
 * Function: main
 *	Retreives information from requested name and calls 'print_report()'
 *	to displays them.
 *
 * Parameters:
 *	argc : number of passed command line arguments.
 *	argv : an array of pointers to command line arguments.
 *
 * Returns: int
 *	zero     : if requested information displayed successfully.
 *	negative : if an unrecoverable error occurs.
 */

int
main (int argc, char *argv[])
{
	FILE   *f;
	char   errmsg[80];
	__u8   *tmp, file_exist, port_exist;
	int   i,
	      s,
	      rc,
	      len;
	struct netbeui_status   *ns;

	if (argc < 2) {
		fprintf(stderr, "\nProcom NetBEUI get STATUS utility (ver 1.1)\n");
		fprintf(stderr, "\t(C)Copyright Procom Technology Inc. 1997 \n");
		fprintf(stderr, "\nUsage:\n       %s  <StatusName> [<PortNumber>] [<RawOutputFile>]\n\n", argv[0]);
		return -1;
	}

	port_exist = (argc > 2);
	file_exist = (argc > 3);

	ns = (struct netbeui_status *) malloc(sizeof(struct netbeui_status) +
	                                             NB_MAX_STATUS_BUFF_LEN);
	if (!ns) {
		fprintf(stderr, "Out of memory for netbeui_status allocation.\n");
		return -2;
	}

	s = socket(AF_NETBEUI, SOCK_DGRAM, 0);
	if (s == -1) {
		sprintf(errmsg, "%s (Creating of NetBEUI socket fails)", argv[0]);
		perror(errmsg);
		return -3;
	}

	len = strlen(argv[1]);
	len = (len < NB_NAME_LEN) ? len : NB_NAME_LEN;
	memcpy(ns->called_name, argv[1], len);
	for (i=0 ; i < (NB_NAME_LEN - len) ; i++) /* Padding with blanks */
		ns->called_name[i+len] = ' ';
	if (port_exist && (argv[2][0] != ' '))
		ns->called_name[NB_NAME_LEN - 1] = atoi(argv[2]);

	ns->reserved = 0;
	ns->buff_len = NB_MAX_STATUS_BUFF_LEN;

	rc = ioctl(s, NBIOCGSTATUS, ns);
	if (rc)
		switch (errno) {
			case ENONET:
				fprintf(stderr, "%s: No network device found! \n", argv[0]);
				return -4;

			case ETIMEDOUT:
				fprintf(stderr, "Note: The complete information was not received\n");
				fprintf(stderr, "      during NetBEUI timeout.\n");
				break;

			default:
				perror(argv[0]);
				return -5;
		}

	if (file_exist) {
		f = fopen(argv[3], "w");
		if (!f) {
			sprintf(errmsg, "%s (Openning of file %s fails)", argv[0], argv[3]);
			perror(errmsg);
			return -6;
		}

		for (i=0 , tmp = ns->status_buff ; i < ns->buff_len ; i++ , tmp++)
			fputc(*tmp, f);

		fclose(f);
	}

	print_report(ns);

	free(ns);

	return 0;
}
