blob: 7c9adacb761d3b648592104bfb0f84d8e7724a47 [file] [log] [blame]
/*
* Copyright 2016 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <getopt.h>
#include <inttypes.h>
#include <regex.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
struct string_match {
char *vendor_class;
char *genus;
char *species;
};
/* Function generated by gperf for the exact_match lookup table. */
extern const struct string_match *exact_match (const char *str,
unsigned int len);
struct regex_match {
char *regex;
char *genus;
};
struct regex_match regex_matches[] = {
/*
* Examples:
* AastraIPPhone55i
* AastraIPPhone57iCT
* AastraIPPhone6737i
*/
{"AastraIPPhone([[:alnum:]]+)", "Aastra IP Phone"},
/* Examples:
* AXIS,Network Camera,M3006,5.40.13
* AXIS,Network Camera,P3346,5.20.1
* AXIS,Thermal Network Camera,Q1931-E,5.55.4.1
*/
{"AXIS,Network Camera,([^,]+)", "AXIS Network Camera"},
{"AXIS,Thermal Network Camera,([^,]+)", "AXIS Network Camera"},
/* Examples:
* Canon MF620C Series
*/
{"Canon (MF[[:alnum:]]+)", "Canon Printer"},
/* Examples:
* Cisco AP c1200
* Cisco AP c1240
*/
{"Cisco AP ([[:alnum:]]+)", "Cisco Wifi AP"},
/* Examples:
* Cisco Systems, Inc. IP Phone CP-7961G
* Cisco Systems, Inc. IP Phone CP-8861
*/
{"Cisco Systems, Inc. IP Phone ([[:alnum:]-]+)", "Cisco IP Phone"},
/* Examples:
* Cisco SPA504G
* Cisco SPA525G2
* CISCO SPA112
* ATA186-H6.0|V3.2.0|B041111A
*/
{"Cisco (SPA[[:alnum:]]+)", "Cisco IP Phone"},
{"CISCO (SPA[[:alnum:]]+)", "Cisco IP Phone"},
{"(ATA186)", "Cisco IP Phone"},
/* Examples:
* CPQRIB3
*/
{"(CPQRIB[[:digit:]]+)", "Compaq Remote Insight"},
/* Examples:
* Dell Color MFP E525w
*/
{"Dell Color MFP ([[:alnum:]]+)", "Dell Printer"},
/* Examples:
* Dell C1760nw Color Printer
* Dell C2660dn Color Laser
* Dell 2155cn Color MFP
*/
{"^Dell ([[:alnum:]]+) Color (Printer|Laser|MFP)", "Dell Printer"},
/* Examples:
* digium_D40_1_4_2_0_63880
*/
{"digium_([^_]+)", "Digium IP Phone"},
/* Examples:
* FortiAP-FP321C-AC-Discovery
* FortiAP-FP221B-AC-Discovery
* FortiAP-FP321C
* FortiWiFi-60D-POE
*/
{"FortiAP-([^-]+)", "Fortinet Wifi AP"},
{"FortiWiFi-([[:alnum:]-]+)", "Fortinet Wifi AP"},
/* Examples:
* Grandstream GXP1405 dslforum.org
* Grandstream GXP2124 dslforum.org
* Grandstream GXV3275 dslforum.org
* Grandstream HT702 dslforum.org
*/
{"Grandstream (GX[[:alnum:]]+)", "Grandstream IP Phone"},
{"Grandstream (HT[[:alnum:]]+)", "Grandstream VoIP Adapter"},
/* Grandstream Voice over IP adapters. Examples:
* HT500 dslforum.org
* HT7XX dslforum.org
* DP7XX dslforum.org
*/
{"^(HT[[:alnum:]]+) dslforum.org", "Grandstream VoIP Adapter"},
{"^(DP[[:alnum:]]+) dslforum.org", "Grandstream IP Phone"},
/* Examples:
* iPECS IP Edge 5000i-24G
*/
{"iPECS IP Edge ([[:alnum:]-]+)", "iPECS IP Phone"},
/* Examples:
* Juniper-ex2200-c-12p-2g
*/
{"Juniper-(ex[^-]+)", "Juniper Router"},
/* Examples:
* LINKSYS SPA-922
* LINKSYS SPA-942
*/
{"LINKSYS (SPA-[[:alnum:]]+)", "Linksys IP Phone"},
/* Examples:
* MotorolaAP.AP7131
*/
{"MotorolaAP.([[:alnum:]]+)", "Motorola Wifi AP"},
/* Examples:
* NECDT700
*/
{"(NECDT[[:alnum:]]+)", "NEC IP Phone"},
/* Examples:
* 6=qPolycomSoundPointIP-SPIP_1234567-12345-001
* 6=tPolycomSoundStationIP-SSIP_12345678-12345-001
*/
{".*PolycomSoundPointIP-([^-]+)", "Polycom IP Phone"},
/* Examples:
* Polycom-SPIP335
* Polycom-SPIP550
* Polycom-SSIP7000
* Polycom-VVX310
* Polycom-VVX500
* Polycom-VVX600
*/
{"Polycom-(SPIP[[:alnum:]]+)", "Polycom IP Phone"},
{"Polycom-(SSIP[[:alnum:]]+)", "Polycom IP Phone"},
{"Polycom-(VVX[[:alnum:]]+)", "Polycom IP Phone"},
/* Examples:
* Rabbit-TCPIP:Z-World:DHCP-Test:1.2.0
* Rabbit2000-TCPIP:Z-World:Testfoo:1.1.3
*/
{"(Rabbit)-TCPIP", "Rabbit Microcontroller"},
{"(Rabbit2000)-TCPIP", "Rabbit Microcontroller"},
/* Examples:
* ReadyNet_WRT500
*/
{"ReadyNet_(WRT[[:alnum:]]+)", "ReadyNet Wifi AP"},
/* Examples:
* SAMSUNG SCX-6x45
*/
{"SAMSUNG (SCX-[[:alnum:]]+)", "Samsung Network MFP"},
/* Examples:
* SF200-24P
* SG 200-08
* SG 200-26
* SG 300-10
* SG 300-20
* SG200-26
* SG200-50P
* SG300-10
*/
{"(SF200-[[:alnum:]]+)", "Cisco Managed Switch"},
{"(SG 200-[[:alnum:]]+)", "Cisco Managed Switch"},
{"(SG200-[[:alnum:]]+)", "Cisco Managed Switch"},
{"(SG 300-[[:alnum:]]+)", "Cisco Managed Switch"},
{"(SG300-[[:alnum:]]+)", "Cisco Managed Switch"},
/* Examples:
* snom-m3-SIP/02.11//18-Aug-10 15:36
* snom320
* snom710
*/
{"(snom[^/]+)", "Snom IP Phone"},
/* Examples:
* telsey-stb-f8
*/
{"telsey-stb-([[:alnum:]]+)", "Telsey Media Player"},
{NULL, NULL}
};
/* Copy a string with no funny schtuff allowed; only alphanumerics + space
* plus a few characters considered safe. */
static void no_mischief_strncpy(char *dst, const char *src, size_t n)
{
size_t i;
for (i = 0; i < n; ++i) {
unsigned char s = src[i];
int is_lower = (s >= 'a' && s <= 'z');
int is_upper = (s >= 'A' && s <= 'Z');
int is_digit = (s >= '0' && s <= '9');
int is_safe = (s == '_' || s == '-' || s == '.');
if (s == '\0') {
dst[i] = '\0';
break;
} else if (is_lower || is_upper || is_digit || is_safe) {
dst[i] = s;
} else if (s == ' ' || s == '\t') {
dst[i] = ' ';
} else {
dst[i] = '_';
}
}
dst[n - 1] = '\0';
}
/*
* Check for vendor options pattern populated by a number of
* printer manufacturers:
*
* Mfg=DELL;Typ=Printer;Mod=Dell 2330dn Laser Printer;Ser=0123AB5;
* Mfg=FujiXerox;Typ=AIO;Mod=WorkCentre 6027;Ser=P1A234567
* Mfg=Hewlett Packard;Typ=Printer;Mod=HP LaserJet 400 M401n;Ser=ABCDE01234;
* mfg=Xerox;typ=MFP;mod=WorkCentre 3220;ser=ABC012345;loc=
*/
int check_for_printer(const char *vendor_class,
char *genus, size_t genus_len,
char *species, size_t species_len)
{
regex_t r_vendor, r_type, r_model;
regmatch_t match[2];
char *vendor = NULL, *type = NULL, *model = NULL;
int rc = 1;
if (regcomp(&r_vendor, "mfg=([^;]+)", REG_EXTENDED | REG_ICASE) ||
regcomp(&r_type, "typ=([^;]+)", REG_EXTENDED | REG_ICASE) ||
regcomp(&r_model, "mod=([^;]+)", REG_EXTENDED | REG_ICASE)) {
fprintf(stderr, "%s: regcomp failed!\n", __FUNCTION__);
exit(1);
}
if (regexec(&r_vendor, vendor_class, 2, match, 0) == 0) {
int len = match[1].rm_eo - match[1].rm_so;
vendor = strndup(vendor_class + match[1].rm_so, len);
}
if (regexec(&r_type, vendor_class, 2, match, 0) == 0) {
int len = match[1].rm_eo - match[1].rm_so;
type = strndup(vendor_class + match[1].rm_so, len);
}
if (regexec(&r_model, vendor_class, 2, match, 0) == 0) {
int len = match[1].rm_eo - match[1].rm_so;
model = strndup(vendor_class + match[1].rm_so, len);
}
if (vendor && type) {
char buf[128];
snprintf(buf, sizeof(buf), "%s %s", vendor, type);
no_mischief_strncpy(genus, buf, genus_len);
rc = 0;
}
if (model) {
no_mischief_strncpy(species, model, species_len);
rc = 0;
}
if (vendor) free(vendor);
if (type) free(type);
if (model) free(model);
regfree(&r_vendor);
regfree(&r_type);
regfree(&r_model);
return(rc);
}
int lookup_vc(const char *vendor_class,
char *genus, size_t genus_len,
char *species, size_t species_len)
{
const struct string_match *p;
const struct regex_match *r;
int slen = strlen(vendor_class);
if ((p = exact_match(vendor_class, slen)) != NULL) {
no_mischief_strncpy(genus, p->genus, genus_len);
no_mischief_strncpy(species, p->species, species_len);
return(0);
}
for (r = &regex_matches[0]; r->regex != NULL; ++r) {
regex_t rx;
regmatch_t match[2];
if (regcomp(&rx, r->regex, REG_EXTENDED | REG_ICASE)) {
fprintf(stderr, "%s: regcomp failed!\n", r->regex);
exit(1);
}
if (regexec(&rx, vendor_class, 2, match, 0) == 0) {
int len = match[1].rm_eo - match[1].rm_so;
char *model = strndup(vendor_class + match[1].rm_so, len);
no_mischief_strncpy(species, model, species_len);
free(model);
snprintf(genus, genus_len, "%s", r->genus);
return(0);
}
regfree(&rx);
}
if (check_for_printer(vendor_class,
genus, genus_len, species, species_len) == 0) {
return(0);
}
return(1);
}
void usage(const char *progname)
{
fprintf(stderr, "usage: %s -v vendor_string -l label\n", progname);
exit(1);
}
int main(int argc, char **argv)
{
struct option long_options[] = {
{"label", required_argument, 0, 'l'},
{"vendor", required_argument, 0, 'v'},
{0, 0, 0, 0},
};
int c;
const char *label = NULL;
const char *vendor = NULL;
char genus[80];
char species[80];
setlinebuf(stdout);
alarm(30);
while ((c = getopt_long(argc, argv, "l:v:", long_options, NULL)) != -1) {
switch (c) {
case 'l':
label = optarg;
break;
case 'v':
vendor = optarg;
break;
default:
usage(argv[0]);
}
}
if (optind < argc || vendor == NULL || label == NULL)
usage(argv[0]);
memset(genus, 0, sizeof(genus));
memset(species, 0, sizeof(species));
if (lookup_vc(vendor, genus, sizeof(genus), species, sizeof(species)) == 0) {
printf("dhcpv %s %s;%s\n", label, genus, species);
}
exit(0);
}