Skip to content

Instantly share code, notes, and snippets.

@decal
Last active April 29, 2018 08:37
Show Gist options
  • Save decal/ea44cb107d8e5038fb73e10b2bceb682 to your computer and use it in GitHub Desktop.
Save decal/ea44cb107d8e5038fb73e10b2bceb682 to your computer and use it in GitHub Desktop.
🔢 Parse `nmap -p` style port range list string input into a linked list..
/*
* Parse `nmap -p` style port range list string input into a linked list..
*
* Written By: Derek Callaway [decal (AT) sdf {D0T} org]
* Compile As: gcc -O2 -Wall -pedantic -o portrange portrange.c
* Updated On: Fri Apr 27 01:14:29 DST 2018
*/
#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
#include<sysexits.h>
#include<string.h>
#include<ctype.h>
#include<error.h>
#include<errno.h>
#include<assert.h>
/* Undefining this causes assert(3) to perform conditional testing */
#define NDEBUG 1
/* This line is required for output */
static const bool is_debug = 1;
/* Port number ranges linked list; single port has equal lo and hi values */
typedef struct port_numbers {
unsigned short portlo;
unsigned short porthi;
struct port_numbers *nextpn;
} PORT_NUMBERS, *PPORT_NUMBERS, **PPPORT_NUMBERS;
/* Count the total number of ports */
size_t count_ports(const PPORT_NUMBERS pnums) {
register PPORT_NUMBERS pport = pnums;
register size_t count = -1;
assert(pnums);
for(pport = pnums;pport;pport = pport->nextpn) {
count += pport->porthi - pport->portlo;
count++;
}
return count;
}
/* Exit if a library function gives an erroneous return value */
static void fatal(const char *s) {
perror(s);
exit(EXIT_FAILURE);
}
/* Primary parsing function of port range list string input */
PORT_NUMBERS *parse_ports(const char *portstr) {
const char *current_range = portstr;
char *endptr = NULL;
long int rangestart = 0, rangeend = 0;
assert(portstr);
PORT_NUMBERS *portp = calloc(1, sizeof *portp);
if(!portp)
error_at_line(1, errno, __FILE__, __LINE__, "calloc: %s", strerror(errno));
PORT_NUMBERS *pnret = portp;
do {
while(*current_range && isspace((int) (unsigned char) *current_range))
current_range++;
if(isdigit((int) (unsigned char) *current_range)) {
rangestart = strtol(current_range, &endptr, 10);
if(rangestart == ERANGE)
error_at_line(1, errno, __FILE__, __LINE__, "strtol: %s", strerror(errno));
if (rangestart < 0 || rangestart > 65535)
fatal("Parse error: Ports must be between 0 and 65535 inclusive");
current_range = endptr;
while(isspace((int)(unsigned char)*current_range))
current_range++;
} else
fatal("Parse error: An example of proper portlist form is \"21-25,53,80\"");
/* Now I have a rangestart, time to go after rangeend */
if (!*current_range || *current_range == ',') {
/* Single port specification */
rangeend = rangestart;
} else if (*current_range == '-') {
current_range++;
if (isdigit((int) (unsigned char) *current_range)) {
rangeend = strtol(current_range, &endptr, 10);
if(rangeend == ERANGE)
error_at_line(1, errno, __FILE__, __LINE__, "strtol: %s", strerror(errno));
if (rangeend < 0 || rangeend > 65535 || rangeend < rangestart)
fatal("Parse error: Ports must be between 0 and 65535 inclusive");
current_range = endptr;
} else
fatal("Parse error: An example of proper portlist form is \"21-25,53,80\"");
} else
fatal("Parse error: An example of proper portlist form is \"21-25,53,80\"");
/* Now I have a rangestart and a rangeend, so I can add these ports */
portp->portlo = (unsigned short)rangestart;
while(rangestart <= rangeend)
rangestart++;
portp->porthi = (unsigned short)rangeend;
portp->nextpn = calloc(1, sizeof *(portp->nextpn));
if(!portp->nextpn)
error_at_line(1, errno, __FILE__, __LINE__, "calloc: %s", strerror(errno));
portp = portp->nextpn;
/* Find the next range */
while(isspace((int) (unsigned char) *current_range))
current_range++;
if (*current_range && *current_range != ',')
fatal("Parse error: An example of proper portlist form is \"21-25,53,80\"");
if (*current_range == ',')
current_range++;
} while(current_range && *current_range);
return pnret;
}
/* Program entry point */
int main(int argc, char *argv[], char *envp[]) {
register size_t acnt = 0;
/* Show usage when lacking arguments */
if(argc < 2) {
fprintf(stderr, "usage: %s PORTSTR\n", *argv);
fputs(" PORTSTR port range string\n", stderr);
exit(EX_USAGE);
}
/* Pass command-line argument to parsing function */
PORT_NUMBERS *const pnms = parse_ports(argv[1]);
register const PORT_NUMBERS *pptr = pnms;
/* Display results */
while(pptr) {
if(is_debug)
fprintf(stderr, "portlo: %hu porthi: %hu\n", pptr->portlo, pptr->porthi);
pptr = pptr->nextpn;
}
acnt += count_ports(pnms);
if(is_debug)
fprintf(stderr, "count: %lu\n", acnt);
/* Exit process gracefully */
exit(EXIT_SUCCESS);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment