blob: ee0ca24dcbe2e0f530e208d8a0743e879d630659 [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 *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 string_match substring_matches[] = {
/*
* Examples:
* AastraIPPhone55i
* AastraIPPhone57iCT
* AastraIPPhone6737i
*/
{"AastraIPPhone", "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", "Canon Printer"},
/* Examples:
* Cisco AP c1200
* Cisco AP c1240
*/
{"Cisco AP", "Cisco Wifi AP"},
/* Examples:
* Cisco Systems, Inc. IP Phone CP-7961G
* Cisco Systems, Inc. IP Phone CP-8861
*/
{"Cisco Systems, Inc. IP Phone", "Cisco IP Phone"},
/* Examples:
* Cisco SPA504G
* Cisco SPA525G2
* CISCO SPA112
* ATA186-H6.0|V3.2.0|B041111A
*/
{"Cisco SPA", "Cisco IP Phone"},
{"CISCO SPA", "Cisco IP Phone"},
{"ATA186", "Cisco IP Phone"},
/* Examples:
* CPQRIB3
*/
{"CPQRIB", "Compaq Remote Insight"},
/* Examples:
* Dell Color MFP E525w
*/
{"Dell Color 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", "Fortinet Wifi AP"},
/* Examples:
* Grandstream GXP1405 dslforum.org
* Grandstream GXP2124 dslforum.org
* Grandstream GXV3275 dslforum.org
* Grandstream HT702 dslforum.org
*/
{"Grandstream GXP", "Grandstream IP Phone"},
{"Grandstream GXV", "Grandstream IP Phone"},
{"Grandstream HT", "Grandstream VoIP adapter"},
/* Examples:
* iPECS IP Edge 5000i-24G
*/
{"iPECS IP Edge", "iPECS IP PHONE"},
/* Examples:
* Juniper-ex2200-c-12p-2g
*/
{"Juniper-ex", "Juniper router"},
/* Examples:
* LINKSYS SPA-922
* LINKSYS SPA-942
*/
{"LINKSYS SPA", "Linksys IP Phone"},
/* Examples:
* MotorolaAP.AP7131
*/
{"MotorolaAP", "Motorola Wifi AP"},
/* Examples:
* NECDT700
*/
{"NECDT", "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", "Polycom IP Phone"},
{"Polycom-SSIP", "Polycom IP Phone"},
{"Polycom-VVX", "Polycom IP Phone"},
/* Examples:
* Rabbit2000-TCPIP:Z-World:Testfoo:1.1.3
* Rabbit-TCPIP:Z-World:DHCP-Test:1.2.0
*/
{"Rabbit-TCPIP", "Rabbit Microcontroller"},
{"Rabbit2000-TCPIP", "Rabbit Microcontroller"},
/* Examples:
* ReadyNet_WRT500
*/
{"ReadyNet_WRT", "ReadyNet Wifi AP"},
/* Examples:
* SAMSUNG SCX-6x45
*/
{"SAMSUNG SCX", "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", "Cisco Managed Switch"},
{"SG 200", "Cisco Managed Switch"},
{"SG200", "Cisco Managed Switch"},
{"SG 300", "Cisco Managed Switch"},
{"SG300", "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", "Telsey Media Player"},
{NULL, NULL}
};
/* Copy a string with no funny schtuff allowed; only alphanumerics + space. */
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');
if (s == '\0') {
dst[i] = '\0';
break;
} else if (is_lower || is_upper || is_digit) {
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 *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(species, buf, species_len);
rc = 0;
} else if (model) {
no_mischief_strncpy(species, model, species_len);
rc = 0;
}
if (vendor) free(vendor);
if (type) free(type);
if (model) free(model);
return(rc);
}
/*
* Check a few patterns from common vendors with lots of model
* numbers.
*/
int check_specials(const char *vendor_class, char *species,
size_t species_len)
{
regex_t r_dellprinter, r_grandstream;
/*
* Dell printers. Examples:
* Dell C1760nw Color Printer
* Dell C2660dn Color Laser
* Dell 2155cn Color MFP
*/
if (regcomp(&r_dellprinter, "^Dell \\S+ Color (Printer|Laser|MFP)",
REG_EXTENDED | REG_ICASE | REG_NOSUB)) {
fprintf(stderr, "%s: regcomp failed!\n", __FUNCTION__);
exit(1);
}
if (regexec(&r_dellprinter, vendor_class, 0, NULL, 0) == 0) {
snprintf(species, species_len, "Dell Printer");
return(0);
}
/*
* Grandstream Voice over IP adapters. Examples:
* HT500 dslforum.org
* HT7XX dslforum.org
*/
if (regcomp(&r_grandstream, "^HT.* dslforum.org",
REG_EXTENDED | REG_ICASE | REG_NOSUB)) {
fprintf(stderr, "%s: regcomp failed!\n", __FUNCTION__);
exit(1);
}
if (regexec(&r_grandstream, vendor_class, 0, NULL, 0) == 0) {
snprintf(species, species_len, "Grandstream VoIP adapter");
return(0);
}
/*
* Grandstream IP phones. Examples:
* DP7XX dslforum.org
*/
if (regcomp(&r_grandstream, "^DP.* dslforum.org",
REG_EXTENDED | REG_ICASE | REG_NOSUB)) {
fprintf(stderr, "%s: regcomp failed!\n", __FUNCTION__);
exit(1);
}
if (regexec(&r_grandstream, vendor_class, 0, NULL, 0) == 0) {
snprintf(species, species_len, "Grandstream IP phone");
return(0);
}
return(1);
}
int lookup_vc(const char *vendor_class, char *species, size_t species_len)
{
const struct string_match *p;
int slen = strlen(vendor_class);
if ((p = exact_match(vendor_class, slen)) != NULL) {
no_mischief_strncpy(species, p->species, species_len);
return(0);
}
p = &substring_matches[0];
while (p->vendor_class != NULL) {
if (strstr(vendor_class, p->vendor_class) != NULL) {
no_mischief_strncpy(species, p->species, species_len);
return(0);
}
p++;
}
if (check_for_printer(vendor_class, species, species_len) == 0) {
return(0);
}
if (check_specials(vendor_class, 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 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(species, 0, sizeof(species));
if (lookup_vc(vendor, species, sizeof(species)) == 0) {
printf("dhcpv %s %s\n", label, species);
}
exit(0);
}