| /* |
| * 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> |
| |
| |
| 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]; |
| |
| 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); |
| } |