| /* |
| * 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 = ®ex_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); |
| } |