summaryrefslogtreecommitdiff
path: root/usbredirparser/usbredirfilter.c
diff options
context:
space:
mode:
authorHans de Goede <hdegoede@redhat.com>2012-01-03 16:30:12 +0100
committerHans de Goede <hdegoede@redhat.com>2012-01-11 17:04:24 +0100
commita90bfaf170e9464299d31dd1121a1ef5a81195b0 (patch)
treec5f06aec8672476f24520d35d3a2bb6becb06d7d /usbredirparser/usbredirfilter.c
parentd03cbc4a402dc5318376471580ab5a596e455f6e (diff)
usbredirparser: Add usbredirfilter code
Add usbredirfilter code, to help apps exclude certain devices from redirection. See the new usbredirfilter.h for details. This commit also bumps the reported version to 0.3.3, so that apps using libusbredirparser can check for this version to check the presence of the new usbredirfilter functions. Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Diffstat (limited to 'usbredirparser/usbredirfilter.c')
-rw-r--r--usbredirparser/usbredirfilter.c251
1 files changed, 251 insertions, 0 deletions
diff --git a/usbredirparser/usbredirfilter.c b/usbredirparser/usbredirfilter.c
new file mode 100644
index 0000000..1b0323d
--- /dev/null
+++ b/usbredirparser/usbredirfilter.c
@@ -0,0 +1,251 @@
+/* usbredirfilter.h usb redirection filter header
+
+ Copyright 2012 Red Hat, Inc.
+
+ Red Hat Authors:
+ Hans de Goede <hdegoede@redhat.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include "usbredirfilter.h"
+
+int usbredirfilter_string_to_rules(
+ const char *filter_str, const char *token_sep, const char *rule_sep,
+ struct usbredirfilter_rule **rules_ret, int *rules_count_ret)
+{
+ char *rule, *rule_saveptr, *token, *token_saveptr, *ep;
+ struct usbredirfilter_rule *rules = NULL;
+ int i, rules_count, *values, ret = 0;
+ char *buf = NULL;
+ const char *r;
+
+ *rules_ret = NULL;
+ *rules_count_ret = 0;
+
+ /* Figure out how much rules there are in the file, so we know how
+ much memory we must allocate for the rules array.
+ Note this will come up with a slightly too large number if there are
+ empty rule strings in the set. */
+ r = filter_str;
+ rules_count = 0;
+ while (r) {
+ r = strchr(r, rule_sep[0]);
+ if (r)
+ r++;
+ rules_count++;
+ }
+
+ rules = calloc(rules_count, sizeof(struct usbredirfilter_rule));
+ if (!rules)
+ return -ENOMEM;
+
+ /* Make a copy since strtok mangles the string */
+ buf = strdup(filter_str);
+ if (!buf) {
+ ret = -ENOMEM;
+ goto leave;
+ }
+
+ /* And actually parse the string */
+ rules_count = 0;
+ rule = strtok_r(buf, rule_sep, &rule_saveptr);
+ while (rule) {
+ /* We treat the filter rule as an array of ints for easier parsing */
+ values = (int *)&rules[rules_count];
+ token = strtok_r(rule, token_sep, &token_saveptr);
+ for (i = 0; i < 5 && token; i++) {
+ values[i] = strtol(token, &ep, 0);
+ if (*ep)
+ break;
+ token = strtok_r(NULL, token_sep, &token_saveptr);
+ }
+ if (i != 5 || token != NULL ||
+ usbredirfilter_verify(&rules[rules_count], 1)) {
+ ret = -EINVAL;
+ goto leave;
+ }
+ rules_count++;
+ rule = strtok_r(NULL, rule_sep, &rule_saveptr);
+ }
+
+ *rules_ret = rules;
+ *rules_count_ret = rules_count;
+
+leave:
+ if (ret)
+ free(rules);
+ free(buf);
+ return ret;
+}
+
+char *usbredirfilter_rules_to_string(struct usbredirfilter_rule *rules,
+ int rules_count, const char *token_sep, const char *rule_sep)
+{
+ int i;
+ char *str, *p;
+
+ if (usbredirfilter_verify(rules, rules_count))
+ return NULL;
+
+ /* We need 28 bytes per rule in the worst case */
+ str = malloc(28 * rules_count + 1);
+ if (!str)
+ return NULL;
+
+ p = str;
+ for (i = 0; i < rules_count; i++) {
+ if (rules[i].device_class != -1)
+ p += sprintf(p, "0x%02x%c", rules[i].device_class, *token_sep);
+ else
+ p += sprintf(p, "-1%c", *token_sep);
+
+ if (rules[i].vendor_id != -1)
+ p += sprintf(p, "0x%04x%c", rules[i].vendor_id, *token_sep);
+ else
+ p += sprintf(p, "-1%c", *token_sep);
+
+ if (rules[i].product_id != -1)
+ p += sprintf(p, "0x%04x%c", rules[i].product_id, *token_sep);
+ else
+ p += sprintf(p, "-1%c", *token_sep);
+
+ if (rules[i].device_version_bcd != -1)
+ p += sprintf(p, "0x%04x%c", rules[i].device_version_bcd, *token_sep);
+ else
+ p += sprintf(p, "-1%c", *token_sep);
+
+ p += sprintf(p, "%d%c", rules[i].allow ? 1:0, *rule_sep);
+ }
+
+ return str;
+}
+
+static int usbredirfilter_check1(struct usbredirfilter_rule *rules,
+ int rules_count, int device_class, int vendor_id, int product_id,
+ int device_version_bcd, int default_allow)
+{
+ int i;
+
+ for (i = 0; i < rules_count; i++) {
+ if ((rules[i].device_class == -1 ||
+ rules[i].device_class == device_class) &&
+ (rules[i].vendor_id == -1 ||
+ rules[i].vendor_id == vendor_id) &&
+ (rules[i].product_id == -1 ||
+ rules[i].product_id == product_id) &&
+ (rules[i].device_version_bcd == -1 ||
+ rules[i].device_version_bcd == device_version_bcd)) {
+ /* Found a match ! */
+ return rules[i].allow ? 0 : -EPERM;
+ }
+ }
+
+ return default_allow ? 0 : -EPERM;
+}
+
+int usbredirfilter_check(struct usbredirfilter_rule *rules, int rules_count,
+ int device_class, int device_subclass, int device_protocol,
+ int *interface_class, int *interface_subclass, int *interface_protocol,
+ int interface_count, int vendor_id, int product_id, int device_version_bcd,
+ int flags)
+{
+ int i, rc;
+
+ if (usbredirfilter_verify(rules, rules_count))
+ return -EINVAL;
+
+ /* Check the device_class */
+ if (device_class != 0x00 && device_class != 0xef) {
+ rc = usbredirfilter_check1(rules, rules_count, device_class,
+ vendor_id, product_id, device_version_bcd,
+ flags & usbredirfilter_fl_default_allow);
+ if (rc)
+ return rc;
+ }
+
+ /* Check the interface classes */
+ for (i = 0; i < interface_count; i++) {
+ if (!(flags & usbredirfilter_fl_dont_skip_non_boot_hid) &&
+ interface_count > 1 && interface_class[i] == 0x03 &&
+ interface_subclass[i] == 0x00 && interface_protocol[i] == 0x00)
+ continue;
+
+ rc = usbredirfilter_check1(rules, rules_count, interface_class[i],
+ vendor_id, product_id, device_version_bcd,
+ flags & usbredirfilter_fl_default_allow);
+ if (rc)
+ return rc;
+ }
+
+ return 0;
+}
+
+int usbredirfilter_verify(struct usbredirfilter_rule *rules, int rules_count)
+{
+ int i;
+
+ for (i = 0; i < rules_count; i++) {
+ if (rules[i].device_class < -1 || rules[i].device_class > 255)
+ return -EINVAL;
+ if (rules[i].vendor_id < -1 || rules[i].vendor_id > 65535)
+ return -EINVAL;
+ if (rules[i].product_id < -1 || rules[i].product_id > 65535)
+ return -EINVAL;
+ if (rules[i].device_version_bcd < -1 ||
+ rules[i].device_version_bcd > 65535)
+ return -EINVAL;
+ }
+ return 0;
+}
+
+void usbredirfilter_print(struct usbredirfilter_rule *rules, int rules_count,
+ FILE *out)
+{
+ int i;
+ char device_class[16], vendor[16], product[16], version[16];
+
+ for (i = 0; i < rules_count; i++) {
+ if (rules[i].device_class != -1)
+ sprintf(device_class, " %02x", rules[i].device_class);
+ else
+ strcpy(device_class, "ANY");
+
+ if (rules[i].vendor_id != -1)
+ sprintf(vendor, "%04x", rules[i].vendor_id);
+ else
+ strcpy(vendor, " ANY");
+
+ if (rules[i].product_id != -1)
+ sprintf(product, "%04x", rules[i].product_id);
+ else
+ strcpy(product, " ANY");
+
+ if (rules[i].device_version_bcd != -1)
+ sprintf(version, "%2d.%02d",
+ ((rules[i].device_version_bcd & 0xf000) >> 12) * 10 +
+ ((rules[i].device_version_bcd & 0x0f00) >> 8),
+ ((rules[i].device_version_bcd & 0x000f) >> 4) * 10 +
+ ((rules[i].device_version_bcd & 0x000f)));
+ else
+ strcpy(version, " ANY");
+
+ fprintf(out, "Class %s ID %s:%s Version %s %s\n", device_class, vendor,
+ product, version, rules[i].allow ? "Allow":"Block");
+ }
+}