/***************************************************************** ** ** @(#) portrange.c (c) Jan 2010 by hoz hznet.de ** ** Calculates port ranges specified by ** draft-boucadair-dhc-port-range-01 ** or ** draft-boucadair-pppext-portrange-option-00 ** ** Try "portrange -h" for a list of options ** *****************************************************************/ # include # include # include # include # include #define VERSION "0.2" #if 1 /* use manual config option instead of configure for now */ # define HAVE_GETOPT_LONG 1 #else # ifdef HAVE_CONFIG_H # include # endif #endif # define MAX_PORTS 0xFFFFL #if defined(HAVE_GETOPT_LONG) && HAVE_GETOPT_LONG # include #endif /** typedefs **/ typedef unsigned int uint; typedef unsigned short ushort; typedef unsigned long ulong; /***************************************************************** ** function declaration *****************************************************************/ static void print_ports (FILE *fp, ushort pr_mask, ushort pr_value, int base); static void print_range (FILE *fp, ushort pr_value, long last, long first, int base); static int cntbits (ushort value); static long bstr2int (const char *s); static long str2int (const char *s); static int is_valid_prv (ushort prvalue, ushort prmask); static void usage (char *mesg); static const char *int2bstr (ulong val, int bits, int space); static int printval (FILE *fp, const char *mesg, ushort val, int base); /***************************************************************** ** macros & global vars *****************************************************************/ # define DEC 01 # define OCT 02 # define HEX 04 # define BIN 010 # define short_options "ahbBdoxlpVs:v:" #if defined(HAVE_GETOPT_LONG) && HAVE_GETOPT_LONG static struct option long_options[] = { {"binary", no_argument, NULL, 'b'}, {"decimal", no_argument, NULL, 'd'}, {"octal", no_argument, NULL, 'o'}, {"hexadecimal", no_argument, NULL, 'h'}, {"list-values", no_argument, NULL, 'l'}, {"list-ports", no_argument, NULL, 'p'}, {"list-all", no_argument, NULL, 'a'}, {"version", no_argument, NULL, 'V'}, {"space-char", required_argument, NULL, 's'}, {"port-range-value", required_argument, NULL, 'v'}, {"help", no_argument, NULL, 'h'}, {0, 0, 0, 0} }; #endif static const char *progname; static int base = 0; static int space = '\0'; static ushort pr_value = 0; static int list_all = 0; static int list_values = 0; static int list_ports = 0; /***************************************************************** ** main () *****************************************************************/ int main (int argc, char *argv[]) { const char *p; ushort pr_mask = 0050; long i; int c; #if defined(HAVE_GETOPT_LONG) && HAVE_GETOPT_LONG int opt_index; #endif char errstr[255+1]; progname = *argv; if ( (p = strrchr (progname, '/')) ) progname = ++p; opterr = 0; #if defined(HAVE_GETOPT_LONG) && HAVE_GETOPT_LONG while ( (c = getopt_long (argc, argv, short_options, long_options, &opt_index)) != -1 ) #else while ( (c = getopt (argc, argv, short_options)) != -1 ) #endif { switch ( c ) { case 's': /* space char */ space = *optarg; break; case 'v': /* mask value */ pr_value = str2int (optarg); #if 0 if ( pr_value < 0 ) usage ("illegal port range value\n"); #endif break; case 'a': /* list all */ list_all = 1; break; case 'l': list_values = 1; break; case 'p': list_ports = 1; break; case 'b': base |= BIN; break; case 'B': base |= BIN; space = ' '; break; case 'd': base |= DEC; break; case 'o': base |= OCT; break; case 'x': base |= HEX; break; case 'V': /* version info */ fprintf (stderr, "%s version %s (c) Jan 2010 by hoz hznet.de\n", progname, VERSION); exit (1); break; case 'h': usage (NULL); break; case '?': if ( isprint (optopt) ) snprintf (errstr, sizeof(errstr), "Unknown option \"-%c\".\n", optopt); else snprintf (errstr, sizeof (errstr), "Unknown option char \\x%x.\n", optopt); usage (errstr); break; default: abort(); } } if ( base == 0 ) base |= DEC; if ( (argc - optind) <= 0 ) /* no arguments left ? */ usage ("port range mask required"); if ( list_all ) list_values = list_ports = 0; pr_mask = str2int (argv[optind]); if ( !is_valid_prv (pr_value, pr_mask) ) { fprintf (stderr, "port range value (0x%x) doesn't match port range mask (0x%x)\n", pr_value, pr_mask); usage (""); } if ( (list_values == 0 && list_ports == 0) || list_values ) { printval (stdout, "port range mask ", pr_mask, base); c = cntbits (pr_mask); fprintf (stdout, " allow %u users to use a range of %u ports\n", 1 << c, 1 << (16-c)); } if ( list_values ) { printval (stdout, "port range value", 0, base); putc ('\n', stdout); for ( i = 0; i <= MAX_PORTS; i++ ) if ( (i & pr_mask) != 0 && (i & ~pr_mask) == 0 ) { printval (stdout, "port range value", i, base); putc ('\n', stdout); } } if ( list_ports ) { printf ("list of port ranges for port range value %d\n", pr_value); print_ports (stdout, pr_mask, pr_value, base); } if ( list_all ) { for ( i = 0; i <= MAX_PORTS; i++ ) if ( i == 0 || ((i & pr_mask) != 0 && (i & ~pr_mask) == 0) ) print_ports (stdout, pr_mask, i, base); } return 0; } /***************************************************************** ** print_ports () *****************************************************************/ static void print_ports (FILE *fp, ushort pr_mask, ushort pr_value, int base) { long i; long first; long last; last = pr_value - 1; first = pr_value; for ( i = 0; i <= MAX_PORTS; i++ ) if ( (i & pr_mask) == pr_value ) { if ( last + 1 < i ) { // fprintf (stderr, "i = %ld first = %ld last = %ld\n", i, first, last); print_range (fp, pr_value, last, first, base); first = i; } last = i; } print_range (fp, pr_value, last, first, base); } /***************************************************************** ** print_range() *****************************************************************/ static void print_range (FILE *fp, ushort pr_value, long last, long first, int base) { printval (fp, "", pr_value, DEC); #if 0 fprintf (fp, "\t(%ld ports)", last - first + 1); #endif base &= ~DEC; /* clear DEC bit in base */ printval (fp, "\t", first, DEC); printval (fp, " to", last, DEC); if ( base ) { printval (fp, "\t", first, base); printval (fp, " -", last, base); } putc ('\n', fp); } # define sopt_usage(mesg, value) fprintf (stderr, mesg, value) #if defined(HAVE_GETOPT_LONG) && HAVE_GETOPT_LONG # define lopt_usage(mesg, value) fprintf (stderr, mesg, value) # define loptstr(lstr, sstr) lstr #else # define lopt_usage(mesg, value) # define loptstr(lstr, sstr) sstr #endif /***************************************************************** ** usage() *****************************************************************/ static void usage (char *mesg) { if ( mesg && *mesg ) fprintf (stderr, "error: %s\n", mesg); fprintf (stderr, "usage: %s [-b[-s c]|-B][-d][-o][-x] [-l] [-p] [-v prv] \n", progname); fprintf (stderr, "\t-v %s\n", loptstr (",\n\t --port-range-value=", "")); fprintf (stderr, "\t-p%s\tlist all port ranges\n", loptstr (", --list-ports", "")); fprintf (stderr, "\t-l%s\tlist all port range values\n", loptstr (", --list-values", "")); fprintf (stderr, "\t-a%s\tlist all port range value combinations\n", loptstr (", --list-all\t", "")); fprintf (stderr, "\t-b%s\tprint binary values\n", loptstr (", --binary\t", "")); fprintf (stderr, "\t-s %s\tuse as spacing char for -b\n", loptstr (", --space=", "")); fprintf (stderr, "\t-B%s\tprint binary values with spaces (same as -b -s \" \")\n", loptstr (", --bin-space\t", "")); fprintf (stderr, "\t-d%s\tprint decimal values(default)\n", loptstr (", --decimal\t", "")); fprintf (stderr, "\t-o%s\tprint octal values\n", loptstr (", --octal\t", "")); fprintf (stderr, "\t-x%s\tprint hex values\n", loptstr (", --hexadecimal", "")); fprintf (stderr, "\t-V%s\tprint version\n", loptstr (", --version\t", "")); fprintf (stderr, "\tport range value (prv) and mask can be given as decimal, octal (0),\n"); fprintf (stderr, "\thex (0x) or binary (0b or b) value\n"); fprintf (stderr, "examples:\n"); fprintf (stderr, "\t%s -B 0x03c0\n", progname); fprintf (stderr, "\t%s -l 0x03c0\n", progname); fprintf (stderr, "\t%s -p -B 0x03c0\n", progname); fprintf (stderr, "\t%s -v 0x4000 -p \"b0100 0000 0000 0000\"\n", progname); exit (1); } /***************************************************************** ** cntbits() *****************************************************************/ static int cntbits (ushort value) { int bit; for ( bit = 0; value; value >>= 1 ) if ( value & 01 ) bit++; return bit; } /***************************************************************** ** is_valid_prv() *****************************************************************/ static int is_valid_prv (ushort prvalue, ushort prmask) { return (prmask | prvalue) == prmask; } /***************************************************************** ** printval() *****************************************************************/ static int printval (FILE *fp, const char *mesg, ushort val, int base) { int len; len = fprintf (fp, "%s", mesg); if ( (base & DEC) == DEC ) len += fprintf (fp, " %5u", val); if ( (base & OCT) == OCT ) len += fprintf (fp, " 0%06o", val); if ( (base & HEX) == HEX ) len += fprintf (fp, " 0x%04x", val); if ( (base & BIN) == BIN ) len += fprintf (fp, " %s", int2bstr (val, 16, space)); return len; } /***************************************************************** ** str2int() parse integer string based on different ** number systems ** dec: "1234" ** hex: "0xabc" ** oct: "01234" or "o1234" ** bin: "b1010" or "0b010" ** returns long value or -1L on error *****************************************************************/ static long str2int (const char *s) { long val; while ( isspace (*s) ) s++; val = -1L; switch ( *s ) { case 'o': /* lower case o */ case '0': /* starting with zero means octal */ s++; if ( *s == '\0' ) return 0L; /* fall through */ case 'b': /* this is for binary digits */ switch ( *s ) { case 'x': /* hex digits */ sscanf (++s, "%lx", &val); break; case 'b': val = bstr2int (s); break; default: sscanf (s, "%lo", &val); break; } break; default: sscanf (s, "%ld", &val); break; } return val; } /***************************************************************** ** bstr2int() parse a string of binary digits (up to 16bits) ** returns the long value of the digit string *****************************************************************/ static long bstr2int (const char *s) { long val; while ( isspace (*s) || *s == 'b' ) s++; for ( val = 0; *s; s++ ) if ( *s == '0' || *s == '1' ) { val |= (*s == '1'); val <<= 1; } if ( val != 0 ) val >>= 1; return val; } /***************************************************************** ** int2bstr() convert a unsigned long value to a string ** of binary digits with 'bits' length ** a spacing char is introduced after 4 digits ** returns a pointer to a static string *****************************************************************/ static const char *int2bstr (ulong val, int bits, int space) { static char bstr[(sizeof (ulong) * 8) + 3 + 1]; char *p; int withdelim = 0; if ( space ) withdelim = 3; if ( bits > sizeof (ulong) * 8 ) return "int2bstr(): bits too big"; p = bstr + bits + withdelim; *p-- = '\0'; while ( p >= bstr ) { *p-- = ((val & 01) == 1) ? '1' : '0'; val >>= 1; if ( withdelim && (--bits) % 4 == 0 ) *p-- = space; } #if defined(DBG) && DBG fprintf (stderr, "bstr = %d len = %d\n", (sizeof (ushort) * 8) + 3 + 1, strlen (bstr)); #endif return bstr; }