VirtualBox

source: vbox/trunk/src/VBox/HostDrivers/VBoxUSB/USBFilter.cpp@ 100347

Last change on this file since 100347 was 98103, checked in by vboxsync, 2 years ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 61.6 KB
Line 
1/* $Id: USBFilter.cpp 98103 2023-01-17 14:15:46Z vboxsync $ */
2/** @file
3 * VirtualBox USB filter abstraction.
4 */
5
6/*
7 * Copyright (C) 2007-2023 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * The contents of this file may alternatively be used under the terms
26 * of the Common Development and Distribution License Version 1.0
27 * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
28 * in the VirtualBox distribution, in which case the provisions of the
29 * CDDL are applicable instead of those of the GPL.
30 *
31 * You may elect to license modified versions of this file under the
32 * terms and conditions of either the GPL or the CDDL or both.
33 *
34 * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
35 */
36
37
38/*********************************************************************************************************************************
39* Header Files *
40*********************************************************************************************************************************/
41#include <VBox/usbfilter.h>
42#include <VBox/usblib.h>
43#include <VBox/err.h>
44#include <VBox/log.h>
45#include <iprt/string.h>
46#include <iprt/assert.h>
47#include <iprt/ctype.h>
48
49
50/** @todo split this up for the sake of device drivers and such. */
51
52
53/**
54 * Initializes an USBFILTER structure.
55 *
56 * @param pFilter The filter to initialize.
57 * @param enmType The filter type. If not valid, the filter will not
58 * be properly initialized and all other calls will fail.
59 */
60USBLIB_DECL(void) USBFilterInit(PUSBFILTER pFilter, USBFILTERTYPE enmType)
61{
62 memset(pFilter, 0, sizeof(*pFilter));
63 AssertReturnVoid(enmType > USBFILTERTYPE_INVALID && enmType < USBFILTERTYPE_END);
64 pFilter->u32Magic = USBFILTER_MAGIC;
65 pFilter->enmType = enmType;
66 for (unsigned i = 0; i < RT_ELEMENTS(pFilter->aFields); i++)
67 pFilter->aFields[i].enmMatch = USBFILTERMATCH_IGNORE;
68}
69
70
71/**
72 * Make a clone of the specified filter.
73 *
74 * @param pFilter The target filter.
75 * @param pToClone The source filter.
76 */
77USBLIB_DECL(void) USBFilterClone(PUSBFILTER pFilter, PCUSBFILTER pToClone)
78{
79 memcpy(pFilter, pToClone, sizeof(*pToClone));
80}
81
82
83/**
84 * Deletes (invalidates) an USBFILTER structure.
85 *
86 * @param pFilter The filter to delete.
87 */
88USBLIB_DECL(void) USBFilterDelete(PUSBFILTER pFilter)
89{
90 pFilter->u32Magic = ~USBFILTER_MAGIC;
91 pFilter->enmType = USBFILTERTYPE_INVALID;
92 pFilter->offCurEnd = 0xfffff;
93}
94
95
96/**
97 * Skips blanks.
98 *
99 * @returns Next non-blank char in the string.
100 * @param psz The string.
101 */
102DECLINLINE(const char *) usbfilterSkipBlanks(const char *psz)
103{
104 while (RT_C_IS_BLANK(*psz))
105 psz++;
106 return psz;
107}
108
109
110/**
111 * Worker for usbfilterReadNumber that parses a hexadecimal number.
112 *
113 * @returns Same as usbfilterReadNumber, except for VERR_NO_DIGITS.
114 * @param pszExpr Where to start converting, first char is a valid digit.
115 * @param ppszExpr See usbfilterReadNumber.
116 * @param pu16Val See usbfilterReadNumber.
117 */
118static int usbfilterReadNumberHex(const char *pszExpr, const char **ppszExpr, uint16_t *pu16Val)
119{
120 int rc = VINF_SUCCESS;
121 uint32_t u32 = 0;
122 do
123 {
124 unsigned uDigit = *pszExpr >= 'a' && *pszExpr <= 'f'
125 ? *pszExpr - 'a' + 10
126 : *pszExpr >= 'A' && *pszExpr <= 'F'
127 ? *pszExpr - 'A' + 10
128 : *pszExpr - '0';
129 if (uDigit >= 16)
130 break;
131 u32 *= 16;
132 u32 += uDigit;
133 if (u32 > UINT16_MAX)
134 rc = VWRN_NUMBER_TOO_BIG;
135 } while (*++pszExpr);
136
137 *ppszExpr = usbfilterSkipBlanks(pszExpr);
138 *pu16Val = rc == VINF_SUCCESS ? u32 : UINT16_MAX;
139 return VINF_SUCCESS;
140}
141
142
143/**
144 * Worker for usbfilterReadNumber that parses a decimal number.
145 *
146 * @returns Same as usbfilterReadNumber, except for VERR_NO_DIGITS.
147 * @param pszExpr Where to start converting, first char is a valid digit.
148 * @param uBase The base - 8 or 16.
149 * @param ppszExpr See usbfilterReadNumber.
150 * @param pu16Val See usbfilterReadNumber.
151 */
152static int usbfilterReadNumberDecimal(const char *pszExpr, unsigned uBase, const char **ppszExpr, uint16_t *pu16Val)
153{
154 int rc = VINF_SUCCESS;
155 uint32_t u32 = 0;
156 do
157 {
158 unsigned uDigit = *pszExpr - '0';
159 if (uDigit >= uBase)
160 break;
161 u32 *= uBase;
162 u32 += uDigit;
163 if (u32 > UINT16_MAX)
164 rc = VWRN_NUMBER_TOO_BIG;
165 } while (*++pszExpr);
166
167 *ppszExpr = usbfilterSkipBlanks(pszExpr);
168 *pu16Val = rc == VINF_SUCCESS ? u32 : UINT16_MAX;
169 return rc;
170}
171
172
173/**
174 * Reads a number from a numeric expression.
175 *
176 * @returns IPRT status code.
177 * @retval VINF_SUCCESS if all is fine. *ppszExpr and *pu16Val are updated.
178 * @retval VWRN_NUMBER_TOO_BIG if the number exceeds unsigned 16-bit, both *ppszExpr and *pu16Val are updated.
179 * @retval VERR_NO_DIGITS if there aren't any digits.
180 *
181 * @param ppszExpr Pointer to the current expression pointer.
182 * This is advanced past the expression and trailing blanks on success.
183 * @param pu16Val Where to store the value on success.
184 */
185static int usbfilterReadNumber(const char **ppszExpr, uint16_t *pu16Val)
186{
187 const char *pszExpr = usbfilterSkipBlanks(*ppszExpr);
188 if (!RT_C_IS_DIGIT(*pszExpr))
189 return VERR_NO_DIGITS;
190
191 if (*pszExpr == '0')
192 {
193 if (pszExpr[1] == 'x' || pszExpr[1] == 'X')
194 {
195 if (!RT_C_IS_XDIGIT(pszExpr[2]))
196 return VERR_NO_DIGITS;
197 return usbfilterReadNumberHex(pszExpr + 2, ppszExpr, pu16Val);
198 }
199 if (RT_C_IS_ODIGIT(pszExpr[1]))
200 return usbfilterReadNumberDecimal(pszExpr + 1, 8, ppszExpr, pu16Val);
201 /* Solitary 0! */
202 if (RT_C_IS_DIGIT(pszExpr[1]))
203 return VERR_NO_DIGITS;
204 }
205 return usbfilterReadNumberDecimal(pszExpr, 10, ppszExpr, pu16Val);
206}
207
208
209/**
210 * Validates a numeric expression.
211 *
212 * @returns VBox status code.
213 * @retval VINF_SUCCESS if valid.
214 * @retval VERR_INVALID_PARAMETER if invalid.
215 * @retval VERR_NO_DIGITS if some expression is short of digits.
216 *
217 * @param pszExpr The numeric expression.
218 */
219static int usbfilterValidateNumExpression(const char *pszExpr)
220{
221 /*
222 * An empty expression is fine.
223 */
224 if (!*pszExpr)
225 return VINF_SUCCESS;
226
227 /*
228 * The string format is: "(<m>|([<m>]-[<n>]))|(<m>|([<m>]-[<n>]))+"
229 * where <m> and <n> are numbers in the decimal, hex (0xNNN) or octal (0NNN)
230 * form. Spaces are allowed around <m> and <n>.
231 */
232 unsigned cSubExpressions = 0;
233 while (*pszExpr)
234 {
235 /*
236 * Skip remnants of the previous expression and any empty expressions.
237 * ('|' is the expression separator.)
238 */
239 while (*pszExpr == '|' || RT_C_IS_BLANK(*pszExpr))
240 pszExpr++;
241 if (!*pszExpr)
242 break;
243
244 /*
245 * Parse the expression.
246 */
247 int rc;
248 uint16_t u16First = 0;
249 uint16_t u16Last = 0;
250 if (*pszExpr == '-')
251 {
252 /* -N */
253 pszExpr++;
254 rc = usbfilterReadNumber(&pszExpr, &u16Last);
255 }
256 else
257 {
258 /* M or M-N */
259 rc = usbfilterReadNumber(&pszExpr, &u16First);
260 if (RT_SUCCESS(rc))
261 {
262 if (*pszExpr == '-')
263 {
264 /* M-N */
265 pszExpr++;
266 rc = usbfilterReadNumber(&pszExpr, &u16Last);
267 }
268 else
269 {
270 /* M */
271 u16Last = u16First;
272 }
273 }
274 }
275 if (RT_FAILURE(rc))
276 return rc;
277
278 /*
279 * We should either be at the end of the string or at
280 * an expression separator (|).
281 */
282 if (*pszExpr && *pszExpr != '|' )
283 return VERR_INVALID_PARAMETER;
284
285 cSubExpressions++;
286 }
287
288 return cSubExpressions ? VINF_SUCCESS : VERR_INVALID_PARAMETER;
289}
290
291
292/**
293 * Validates a string pattern.
294 *
295 * @returns VBox status code.
296 * @retval VINF_SUCCESS if valid.
297 * @retval VERR_INVALID_PARAMETER if invalid.
298 *
299 * @param psz The string pattern.
300 */
301static int usbfilterValidateStringPattern(const char *psz)
302{
303 /*
304 * This is only becomes important if we start doing
305 * sets ([0-9]) and such like.
306 */
307 RT_NOREF1(psz);
308 return VINF_SUCCESS;
309}
310
311
312/**
313 * Thoroughly validates the USB Filter.
314 *
315 * @returns Appropriate VBox status code.
316 * @param pFilter The filter to validate.
317 */
318USBLIB_DECL(int) USBFilterValidate(PCUSBFILTER pFilter)
319{
320 if (!RT_VALID_PTR(pFilter))
321 return VERR_INVALID_POINTER;
322
323 if (pFilter->u32Magic != USBFILTER_MAGIC)
324 return VERR_INVALID_MAGIC;
325
326 if ( pFilter->enmType <= USBFILTERTYPE_INVALID
327 || pFilter->enmType >= USBFILTERTYPE_END)
328 {
329 Log(("USBFilter: %p - enmType=%d!\n", pFilter, pFilter->enmType));
330 return VERR_INVALID_PARAMETER;
331 }
332
333 if (pFilter->offCurEnd >= sizeof(pFilter->achStrTab))
334 {
335 Log(("USBFilter: %p - offCurEnd=%#x!\n", pFilter, pFilter->offCurEnd));
336 return VERR_INVALID_PARAMETER;
337 }
338
339 /* Validate that string value offsets are inside the string table. */
340 for (uint32_t i = 0; i < RT_ELEMENTS(pFilter->aFields); i++)
341 {
342 if ( USBFilterIsMethodUsingStringValue((USBFILTERMATCH)pFilter->aFields[i].enmMatch)
343 && pFilter->aFields[i].u16Value > pFilter->offCurEnd)
344 {
345 Log(("USBFilter: %p - bad offset=%#x\n", pFilter, pFilter->aFields[i].u16Value));
346 return VERR_INVALID_PARAMETER;
347 }
348 }
349
350 /*
351 * Validate the string table.
352 */
353 if (pFilter->achStrTab[0])
354 {
355 Log(("USBFilter: %p - bad null string\n", pFilter));
356 return VERR_INVALID_PARAMETER;
357 }
358
359 const char *psz = &pFilter->achStrTab[1];
360 while (psz < &pFilter->achStrTab[pFilter->offCurEnd])
361 {
362 const char *pszEnd = RTStrEnd(psz, &pFilter->achStrTab[sizeof(pFilter->achStrTab)] - psz);
363 if (!pszEnd)
364 {
365 Log(("USBFilter: %p - string at %#x isn't terminated!\n",
366 pFilter, psz - &pFilter->achStrTab[0]));
367 return VERR_INVALID_PARAMETER;
368 }
369
370 uint16_t off = (uint16_t)(uintptr_t)(psz - &pFilter->achStrTab[0]);
371 unsigned i;
372 for (i = 0; i < RT_ELEMENTS(pFilter->aFields); i++)
373 if ( USBFilterIsMethodUsingStringValue((USBFILTERMATCH)pFilter->aFields[i].enmMatch)
374 && pFilter->aFields[i].u16Value == off)
375 break;
376 if (i >= RT_ELEMENTS(pFilter->aFields))
377 {
378 Log(("USBFilter: %p - string at %#x isn't used by anyone! (%s)\n",
379 pFilter, psz - &pFilter->achStrTab[0], psz));
380 return VERR_INVALID_PARAMETER;
381 }
382
383 psz = pszEnd + 1;
384 }
385
386 if ((uintptr_t)(psz - &pFilter->achStrTab[0] - 1) != pFilter->offCurEnd)
387 {
388 Log(("USBFilter: %p - offCurEnd=%#x currently at %#x\n",
389 pFilter, pFilter->offCurEnd, psz - &pFilter->achStrTab[0] - 1));
390 return VERR_INVALID_PARAMETER;
391 }
392
393 while (psz < &pFilter->achStrTab[sizeof(pFilter->achStrTab)])
394 {
395 if (*psz)
396 {
397 Log(("USBFilter: %p - str tab isn't zero padded! %#x: %c\n",
398 pFilter, psz - &pFilter->achStrTab[0], *psz));
399 return VERR_INVALID_PARAMETER;
400 }
401 psz++;
402 }
403
404
405 /*
406 * Validate the fields.
407 */
408 int rc;
409 for (unsigned i = 0; i < RT_ELEMENTS(pFilter->aFields); i++)
410 {
411 switch (pFilter->aFields[i].enmMatch)
412 {
413 case USBFILTERMATCH_IGNORE:
414 case USBFILTERMATCH_PRESENT:
415 if (pFilter->aFields[i].u16Value)
416 {
417 Log(("USBFilter: %p - #%d/%d u16Value=%d expected 0!\n",
418 pFilter, i, pFilter->aFields[i].enmMatch, pFilter->aFields[i].u16Value));
419 return VERR_INVALID_PARAMETER;
420 }
421 break;
422
423 case USBFILTERMATCH_NUM_EXACT:
424 case USBFILTERMATCH_NUM_EXACT_NP:
425 if (!USBFilterIsNumericField((USBFILTERIDX)i))
426 {
427 Log(("USBFilter: %p - #%d / %d - not numeric field\n",
428 pFilter, i, pFilter->aFields[i].enmMatch));
429 return VERR_INVALID_PARAMETER;
430 }
431 break;
432
433 case USBFILTERMATCH_NUM_EXPRESSION:
434 case USBFILTERMATCH_NUM_EXPRESSION_NP:
435 if (!USBFilterIsNumericField((USBFILTERIDX)i))
436 {
437 Log(("USBFilter: %p - #%d / %d - not numeric field\n",
438 pFilter, i, pFilter->aFields[i].enmMatch));
439 return VERR_INVALID_PARAMETER;
440 }
441 if ( pFilter->aFields[i].u16Value >= pFilter->offCurEnd
442 && pFilter->offCurEnd)
443 {
444 Log(("USBFilter: %p - #%d / %d - off=%#x max=%#x\n",
445 pFilter, i, pFilter->aFields[i].enmMatch, pFilter->aFields[i].u16Value, pFilter->offCurEnd));
446 return VERR_INVALID_PARAMETER;
447 }
448 psz = &pFilter->achStrTab[pFilter->aFields[i].u16Value];
449 rc = usbfilterValidateNumExpression(psz);
450 if (RT_FAILURE(rc))
451 {
452 Log(("USBFilter: %p - #%d / %d - bad num expr: %s (rc=%Rrc)\n",
453 pFilter, i, pFilter->aFields[i].enmMatch, psz, rc));
454 return rc;
455 }
456 break;
457
458 case USBFILTERMATCH_STR_EXACT:
459 case USBFILTERMATCH_STR_EXACT_NP:
460 if (!USBFilterIsStringField((USBFILTERIDX)i))
461 {
462 Log(("USBFilter: %p - #%d / %d - not string field\n",
463 pFilter, i, pFilter->aFields[i].enmMatch));
464 return VERR_INVALID_PARAMETER;
465 }
466 if ( pFilter->aFields[i].u16Value >= pFilter->offCurEnd
467 && pFilter->offCurEnd)
468 {
469 Log(("USBFilter: %p - #%d / %d - off=%#x max=%#x\n",
470 pFilter, i, pFilter->aFields[i].enmMatch, pFilter->aFields[i].u16Value, pFilter->offCurEnd));
471 return VERR_INVALID_PARAMETER;
472 }
473 break;
474
475 case USBFILTERMATCH_STR_PATTERN:
476 case USBFILTERMATCH_STR_PATTERN_NP:
477 if (!USBFilterIsStringField((USBFILTERIDX)i))
478 {
479 Log(("USBFilter: %p - #%d / %d - not string field\n",
480 pFilter, i, pFilter->aFields[i].enmMatch));
481 return VERR_INVALID_PARAMETER;
482 }
483 if ( pFilter->aFields[i].u16Value >= pFilter->offCurEnd
484 && pFilter->offCurEnd)
485 {
486 Log(("USBFilter: %p - #%d / %d - off=%#x max=%#x\n",
487 pFilter, i, pFilter->aFields[i].enmMatch, pFilter->aFields[i].u16Value, pFilter->offCurEnd));
488 return VERR_INVALID_PARAMETER;
489 }
490 psz = &pFilter->achStrTab[pFilter->aFields[i].u16Value];
491 rc = usbfilterValidateStringPattern(psz);
492 if (RT_FAILURE(rc))
493 {
494 Log(("USBFilter: %p - #%d / %d - bad string pattern: %s (rc=%Rrc)\n",
495 pFilter, i, pFilter->aFields[i].enmMatch, psz, rc));
496 return rc;
497 }
498 break;
499
500 default:
501 Log(("USBFilter: %p - #%d enmMatch=%d!\n", pFilter, i, pFilter->aFields[i].enmMatch));
502 return VERR_INVALID_PARAMETER;
503 }
504 }
505
506 return VINF_SUCCESS;
507}
508
509
510/**
511 * Find the specified field in the string table.
512 *
513 * @returns Pointer to the string in the string table on success.
514 * NULL if the field is invalid or it doesn't have a string value.
515 * @param pFilter The filter.
516 * @param enmFieldIdx The field index.
517 */
518DECLINLINE(const char *) usbfilterGetString(PCUSBFILTER pFilter, USBFILTERIDX enmFieldIdx)
519{
520 if ((unsigned)enmFieldIdx < (unsigned)USBFILTERIDX_END)
521 {
522 switch (pFilter->aFields[enmFieldIdx].enmMatch)
523 {
524 case USBFILTERMATCH_NUM_EXPRESSION:
525 case USBFILTERMATCH_NUM_EXPRESSION_NP:
526 case USBFILTERMATCH_STR_EXACT:
527 case USBFILTERMATCH_STR_EXACT_NP:
528 case USBFILTERMATCH_STR_PATTERN:
529 case USBFILTERMATCH_STR_PATTERN_NP:
530 Assert(pFilter->aFields[enmFieldIdx].u16Value < sizeof(pFilter->achStrTab));
531 return &pFilter->achStrTab[pFilter->aFields[enmFieldIdx].u16Value];
532
533 default:
534 AssertMsgFailed(("%d\n", pFilter->aFields[enmFieldIdx].enmMatch));
535 case USBFILTERMATCH_IGNORE:
536 case USBFILTERMATCH_PRESENT:
537 case USBFILTERMATCH_NUM_EXACT:
538 case USBFILTERMATCH_NUM_EXACT_NP:
539 break;
540 }
541 }
542 return NULL;
543}
544
545
546/**
547 * Gets a number value of a field.
548 *
549 * The field must contain a numeric value.
550 *
551 * @returns The field value on success, -1 on failure (invalid input / not numeric).
552 * @param pFilter The filter.
553 * @param enmFieldIdx The field index.
554 */
555DECLINLINE(int) usbfilterGetNum(PCUSBFILTER pFilter, USBFILTERIDX enmFieldIdx)
556{
557 if ((unsigned)enmFieldIdx < (unsigned)USBFILTERIDX_END)
558 {
559 switch (pFilter->aFields[enmFieldIdx].enmMatch)
560 {
561 case USBFILTERMATCH_NUM_EXACT:
562 case USBFILTERMATCH_NUM_EXACT_NP:
563 return pFilter->aFields[enmFieldIdx].u16Value;
564
565 default:
566 AssertMsgFailed(("%d\n", pFilter->aFields[enmFieldIdx].enmMatch));
567 case USBFILTERMATCH_IGNORE:
568 case USBFILTERMATCH_PRESENT:
569 case USBFILTERMATCH_NUM_EXPRESSION:
570 case USBFILTERMATCH_NUM_EXPRESSION_NP:
571 case USBFILTERMATCH_STR_EXACT:
572 case USBFILTERMATCH_STR_EXACT_NP:
573 case USBFILTERMATCH_STR_PATTERN:
574 case USBFILTERMATCH_STR_PATTERN_NP:
575 break;
576 }
577 }
578 return -1;
579}
580
581
582/**
583 * Performs simple pattern matching.
584 *
585 * @returns true on match and false on mismatch.
586 * @param pszExpr The numeric expression.
587 * @param u16Value The value to match.
588 */
589static bool usbfilterMatchNumExpression(const char *pszExpr, uint16_t u16Value)
590{
591 /*
592 * The string format is: "(<m>|([<m>]-[<n>]))|(<m>|([<m>]-[<n>]))+"
593 * where <m> and <n> are numbers in the decimal, hex (0xNNN) or octal (0NNN)
594 * form. Spaces are allowed around <m> and <n>.
595 */
596 while (*pszExpr)
597 {
598 /*
599 * Skip remnants of the previous expression and any empty expressions.
600 * ('|' is the expression separator.)
601 */
602 while (*pszExpr == '|' || RT_C_IS_BLANK(*pszExpr))
603 pszExpr++;
604 if (!*pszExpr)
605 break;
606
607 /*
608 * Parse the expression.
609 */
610 int rc;
611 uint16_t u16First = 0;
612 uint16_t u16Last = 0;
613 if (*pszExpr == '-')
614 {
615 /* -N */
616 pszExpr++;
617 rc = usbfilterReadNumber(&pszExpr, &u16Last);
618 }
619 else
620 {
621 /* M or M-N */
622 rc = usbfilterReadNumber(&pszExpr, &u16First);
623 if (RT_SUCCESS(rc))
624 {
625 pszExpr = usbfilterSkipBlanks(pszExpr);
626 if (*pszExpr == '-')
627 {
628 /* M-N */
629 pszExpr++;
630 rc = usbfilterReadNumber(&pszExpr, &u16Last);
631 }
632 else
633 {
634 /* M */
635 u16Last = u16First;
636 }
637 }
638 }
639
640 /* On success, we should either be at the end of the string or
641 at an expression separator (|). */
642 if (RT_SUCCESS(rc) && *pszExpr && *pszExpr != '|' )
643 rc = VERR_INVALID_PARAMETER;
644 if (RT_SUCCESS(rc))
645 {
646 /*
647 * Swap the values if the order is mixed up.
648 */
649 if (u16First > u16Last)
650 {
651 uint16_t u16Tmp = u16First;
652 u16First = u16Last;
653 u16Last = u16Tmp;
654 }
655
656 /*
657 * Perform the compare.
658 */
659 if ( u16Value >= u16First
660 && u16Value <= u16Last)
661 return true;
662 }
663 else
664 {
665 /*
666 * Skip the bad expression.
667 * ('|' is the expression separator.)
668 */
669 while (*pszExpr && *pszExpr != '|')
670 pszExpr++;
671 }
672 }
673
674 return false;
675}
676
677
678/**
679 * Performs simple pattern matching.
680 *
681 * @returns true on match and false on mismatch.
682 * @param pszPattern The pattern to match against.
683 * @param psz The string to match.
684 */
685static bool usbfilterMatchStringPattern(const char *pszPattern, const char *psz)
686{
687 char ch;
688 while ((ch = *pszPattern++))
689 {
690 if (ch == '?')
691 {
692 /*
693 * Matches one char or end of string.
694 */
695 if (*psz)
696 psz++;
697 }
698 else if (ch == '*')
699 {
700 /*
701 * Matches zero or more characters.
702 */
703 /* skip subsequent wildcards */
704 while ( (ch = *pszPattern) == '*'
705 || ch == '?')
706 pszPattern++;
707 if (!ch)
708 /* Pattern ends with a '*' and thus matches the rest of psz. */
709 return true;
710
711 /* Find the length of the following exact pattern sequence. */
712 ssize_t cchMatch = 1;
713 while ( (ch = pszPattern[cchMatch]) != '\0'
714 && ch != '*'
715 && ch != '?')
716 cchMatch++;
717
718 /* Check if the exact pattern sequence is too long. */
719 ssize_t cch = strlen(psz);
720 cch -= cchMatch;
721 if (cch < 0)
722 return false;
723
724 /* Is the rest an exact match? */
725 if (!ch)
726 return memcmp(psz + cch, pszPattern, cchMatch) == 0;
727
728 /*
729 * This is where things normally starts to get recursive or ugly.
730 *
731 * Just to make life simple, we'll skip the nasty stuff and say
732 * that we will do a maximal wildcard match and forget about any
733 * alternative matches.
734 *
735 * If somebody is bored out of their mind one day, feel free to
736 * implement correct matching without using recursion.
737 */
738 ch = *pszPattern;
739 const char *pszMatch = NULL;
740 while ( cch-- >= 0
741 && *psz)
742 {
743 if ( *psz == ch
744 && !strncmp(psz, pszPattern, cchMatch))
745 pszMatch = psz;
746 psz++;
747 }
748 if (!pszMatch)
749 return false;
750
751 /* advance */
752 psz = pszMatch + cchMatch;
753 pszPattern += cchMatch;
754 }
755 else
756 {
757 /* exact match */
758 if (ch != *psz)
759 return false;
760 psz++;
761 }
762 }
763
764 return *psz == '\0';
765}
766
767
768/**
769 * Match a filter against a device.
770 *
771 * @returns true if they match, false if not.
772 *
773 * @param pFilter The filter to match with.
774 * @param pDevice The device data. This is a filter (type ignored) that
775 * contains 'exact' values for all present fields and 'ignore'
776 * values for the non-present fields.
777 *
778 * @remark Both the filter and the device are ASSUMED to be valid because
779 * we don't wish to waste any time in this function.
780 */
781USBLIB_DECL(bool) USBFilterMatch(PCUSBFILTER pFilter, PCUSBFILTER pDevice)
782{
783 return USBFilterMatchRated(pFilter, pDevice) > 0;
784}
785
786
787#if 0 /*def IN_RING0*/ /** @todo convert to proper logging. */
788extern "C" int printf(const char *format, ...);
789# define dprintf(a) printf a
790#else
791# define dprintf(a) do {} while (0)
792#endif
793
794/**
795 * Match a filter against a device and rate the result.
796 *
797 * @returns -1 if no match, matching rate between 1 and 100 (inclusive) if matched.
798 *
799 * @param pFilter The filter to match with.
800 * @param pDevice The device data. This is a filter (type ignored) that
801 * contains 'exact' values for all present fields and 'ignore'
802 * values for the non-present fields.
803 *
804 * @remark Both the filter and the device are ASSUMED to be valid because
805 * we don't wish to waste any time in this function.
806 */
807USBLIB_DECL(int) USBFilterMatchRated(PCUSBFILTER pFilter, PCUSBFILTER pDevice)
808{
809 unsigned iRate = 0;
810dprintf(("USBFilterMatchRated: %p %p\n", pFilter, pDevice));
811
812 for (unsigned i = 0; i < RT_ELEMENTS(pFilter->aFields); i++)
813 {
814 switch (pFilter->aFields[i].enmMatch)
815 {
816 case USBFILTERMATCH_IGNORE:
817 iRate += 2;
818 break;
819
820 case USBFILTERMATCH_PRESENT:
821 if (pDevice->aFields[i].enmMatch == USBFILTERMATCH_IGNORE)
822 {
823dprintf(("filter match[%d]: !present\n", i));
824 return -1;
825 }
826 iRate += 2;
827 break;
828
829 case USBFILTERMATCH_NUM_EXACT:
830 if ( pDevice->aFields[i].enmMatch == USBFILTERMATCH_IGNORE
831 || pFilter->aFields[i].u16Value != pDevice->aFields[i].u16Value)
832 {
833if (pDevice->aFields[i].enmMatch == USBFILTERMATCH_IGNORE)
834 dprintf(("filter match[%d]: !num_exact device=ignore\n", i));
835else
836 dprintf(("filter match[%d]: !num_exact %#x (filter) != %#x (device)\n", i, pFilter->aFields[i].u16Value, pDevice->aFields[i].u16Value));
837 return -1;
838 }
839 iRate += 2;
840 break;
841
842 case USBFILTERMATCH_NUM_EXACT_NP:
843 if ( pDevice->aFields[i].enmMatch != USBFILTERMATCH_IGNORE
844 && pFilter->aFields[i].u16Value != pDevice->aFields[i].u16Value)
845 {
846dprintf(("filter match[%d]: !num_exact_np %#x (filter) != %#x (device)\n", i, pFilter->aFields[i].u16Value, pDevice->aFields[i].u16Value));
847 return -1;
848 }
849 iRate += 2;
850 break;
851
852 case USBFILTERMATCH_NUM_EXPRESSION:
853 if ( pDevice->aFields[i].enmMatch == USBFILTERMATCH_IGNORE
854 || !usbfilterMatchNumExpression(usbfilterGetString(pFilter, (USBFILTERIDX)i),
855 pDevice->aFields[i].u16Value))
856 {
857dprintf(("filter match[%d]: !num_expression\n", i));
858 return -1;
859 }
860 iRate += 1;
861 break;
862
863 case USBFILTERMATCH_NUM_EXPRESSION_NP:
864 if ( pDevice->aFields[i].enmMatch != USBFILTERMATCH_IGNORE
865 && !usbfilterMatchNumExpression(usbfilterGetString(pFilter, (USBFILTERIDX)i),
866 pDevice->aFields[i].u16Value))
867 {
868dprintf(("filter match[%d]: !num_expression_no\n", i));
869 return -1;
870 }
871 iRate += 1;
872 break;
873
874 case USBFILTERMATCH_STR_EXACT:
875 if ( pDevice->aFields[i].enmMatch == USBFILTERMATCH_IGNORE
876 || strcmp(usbfilterGetString(pFilter, (USBFILTERIDX)i),
877 usbfilterGetString(pDevice, (USBFILTERIDX)i)))
878 {
879dprintf(("filter match[%d]: !str_exact\n", i));
880 return -1;
881 }
882 iRate += 2;
883 break;
884
885 case USBFILTERMATCH_STR_EXACT_NP:
886 if ( pDevice->aFields[i].enmMatch != USBFILTERMATCH_IGNORE
887 && strcmp(usbfilterGetString(pFilter, (USBFILTERIDX)i),
888 usbfilterGetString(pDevice, (USBFILTERIDX)i)))
889 {
890dprintf(("filter match[%d]: !str_exact_np\n", i));
891 return -1;
892 }
893 iRate += 2;
894 break;
895
896 case USBFILTERMATCH_STR_PATTERN:
897 if ( pDevice->aFields[i].enmMatch == USBFILTERMATCH_IGNORE
898 || !usbfilterMatchStringPattern(usbfilterGetString(pFilter, (USBFILTERIDX)i),
899 usbfilterGetString(pDevice, (USBFILTERIDX)i)))
900 {
901dprintf(("filter match[%d]: !str_pattern\n", i));
902 return -1;
903 }
904 iRate += 1;
905 break;
906
907 case USBFILTERMATCH_STR_PATTERN_NP:
908 if ( pDevice->aFields[i].enmMatch != USBFILTERMATCH_IGNORE
909 && !usbfilterMatchStringPattern(usbfilterGetString(pFilter, (USBFILTERIDX)i),
910 usbfilterGetString(pDevice, (USBFILTERIDX)i)))
911 {
912dprintf(("filter match[%d]: !str_pattern_np\n", i));
913 return -1;
914 }
915 iRate += 1;
916 break;
917
918 default:
919 AssertMsgFailed(("#%d: %d\n", i, pFilter->aFields[i].enmMatch));
920 return -1;
921 }
922 }
923
924 /* iRate is the range 0..2*cFields - recalc to percent. */
925dprintf(("filter match: iRate=%d", iRate));
926 return iRate == 2 * RT_ELEMENTS(pFilter->aFields)
927 ? 100
928 : (iRate * 100) / (2 * RT_ELEMENTS(pFilter->aFields));
929}
930
931
932/**
933 * Match a filter against a USBDEVICE.
934 *
935 * @returns true if they match, false if not.
936 *
937 * @param pFilter The filter to match with.
938 * @param pDevice The device to match.
939 *
940 * @remark Both the filter and the device are ASSUMED to be valid because
941 * we don't wish to waste any time in this function.
942 */
943USBLIB_DECL(bool) USBFilterMatchDevice(PCUSBFILTER pFilter, PUSBDEVICE pDevice)
944{
945 for (unsigned i = 0; i < RT_ELEMENTS(pFilter->aFields); i++)
946 {
947 switch (pFilter->aFields[i].enmMatch)
948 {
949 case USBFILTERMATCH_IGNORE:
950 break;
951
952 case USBFILTERMATCH_PRESENT:
953 {
954 const char *psz;
955 switch (i)
956 {
957 case USBFILTERIDX_MANUFACTURER_STR: psz = pDevice->pszManufacturer; break;
958 case USBFILTERIDX_PRODUCT_STR: psz = pDevice->pszProduct; break;
959 case USBFILTERIDX_SERIAL_NUMBER_STR: psz = pDevice->pszSerialNumber; break;
960 default: psz = ""; break;
961 }
962 if (!psz)
963 return false;
964 break;
965 }
966
967 case USBFILTERMATCH_NUM_EXACT:
968 case USBFILTERMATCH_NUM_EXACT_NP:
969 case USBFILTERMATCH_NUM_EXPRESSION:
970 case USBFILTERMATCH_NUM_EXPRESSION_NP:
971 {
972 uint16_t u16Value;
973 switch (i)
974 {
975 case USBFILTERIDX_VENDOR_ID: u16Value = pDevice->idVendor; break;
976 case USBFILTERIDX_PRODUCT_ID: u16Value = pDevice->idProduct; break;
977 case USBFILTERIDX_DEVICE: u16Value = pDevice->bcdDevice; break;
978 case USBFILTERIDX_DEVICE_CLASS: u16Value = pDevice->bDeviceClass; break;
979 case USBFILTERIDX_DEVICE_SUB_CLASS: u16Value = pDevice->bDeviceSubClass; break;
980 case USBFILTERIDX_DEVICE_PROTOCOL: u16Value = pDevice->bDeviceProtocol; break;
981 case USBFILTERIDX_BUS: u16Value = pDevice->bBus; break;
982 case USBFILTERIDX_PORT: u16Value = pDevice->bPort; break;
983 default: u16Value = UINT16_MAX; break;
984
985 }
986 switch (pFilter->aFields[i].enmMatch)
987 {
988 case USBFILTERMATCH_NUM_EXACT:
989 case USBFILTERMATCH_NUM_EXACT_NP:
990 if (pFilter->aFields[i].u16Value != u16Value)
991 return false;
992 break;
993 case USBFILTERMATCH_NUM_EXPRESSION:
994 case USBFILTERMATCH_NUM_EXPRESSION_NP:
995 if (!usbfilterMatchNumExpression(usbfilterGetString(pFilter, (USBFILTERIDX)i), u16Value))
996 return false;
997 break;
998 }
999 break;
1000 }
1001
1002 case USBFILTERMATCH_STR_EXACT:
1003 case USBFILTERMATCH_STR_EXACT_NP:
1004 case USBFILTERMATCH_STR_PATTERN:
1005 case USBFILTERMATCH_STR_PATTERN_NP:
1006 {
1007 const char *psz;
1008 switch (i)
1009 {
1010 case USBFILTERIDX_MANUFACTURER_STR: psz = pDevice->pszManufacturer; break;
1011 case USBFILTERIDX_PRODUCT_STR: psz = pDevice->pszProduct; break;
1012 case USBFILTERIDX_SERIAL_NUMBER_STR: psz = pDevice->pszSerialNumber; break;
1013 default: psz = NULL; break;
1014 }
1015 switch (pFilter->aFields[i].enmMatch)
1016 {
1017 case USBFILTERMATCH_STR_EXACT:
1018 if ( !psz
1019 || strcmp(usbfilterGetString(pFilter, (USBFILTERIDX)i), psz))
1020 return false;
1021 break;
1022
1023 case USBFILTERMATCH_STR_EXACT_NP:
1024 if ( psz
1025 && strcmp(usbfilterGetString(pFilter, (USBFILTERIDX)i), psz))
1026 return false;
1027 break;
1028
1029 case USBFILTERMATCH_STR_PATTERN:
1030 if ( !psz
1031 || !usbfilterMatchStringPattern(usbfilterGetString(pFilter, (USBFILTERIDX)i), psz))
1032 return false;
1033 break;
1034
1035 case USBFILTERMATCH_STR_PATTERN_NP:
1036 if ( psz
1037 && !usbfilterMatchStringPattern(usbfilterGetString(pFilter, (USBFILTERIDX)i), psz))
1038 return false;
1039 break;
1040 }
1041 break;
1042 }
1043
1044 default:
1045 AssertMsgFailed(("#%d: %d\n", i, pFilter->aFields[i].enmMatch));
1046 return false;
1047 }
1048 }
1049
1050 return true;
1051}
1052
1053
1054/**
1055 * Checks if the two filters are identical.
1056 *
1057 * @returns true if the are identical, false if they aren't.
1058 * @param pFilter The first filter.
1059 * @param pFilter2 The second filter.
1060 */
1061USBLIB_DECL(bool) USBFilterIsIdentical(PCUSBFILTER pFilter, PCUSBFILTER pFilter2)
1062{
1063 /* Lazy works here because we're darn strict with zero padding and such elsewhere. */
1064 return memcmp(pFilter, pFilter2, sizeof(*pFilter)) == 0;
1065}
1066
1067
1068
1069/**
1070 * Sets the filter type.
1071 *
1072 * @returns VBox status code.
1073 * @retval VERR_INVALID_PARAMETER if the filter type is invalid.
1074 * @retval VERR_INVALID_MAGIC if pFilter is invalid.
1075 *
1076 * @param pFilter The filter.
1077 * @param enmType The new filter type.
1078 */
1079USBLIB_DECL(int) USBFilterSetFilterType(PUSBFILTER pFilter, USBFILTERTYPE enmType)
1080{
1081 AssertReturn(pFilter->u32Magic == USBFILTER_MAGIC, VERR_INVALID_MAGIC);
1082 AssertReturn(enmType > USBFILTERTYPE_INVALID && enmType < USBFILTERTYPE_END, VERR_INVALID_PARAMETER);
1083
1084 pFilter->enmType = enmType;
1085 return VINF_SUCCESS;
1086}
1087
1088
1089/**
1090 * Replaces the string value of a field.
1091 *
1092 * This will remove any existing string value current held by the field from the
1093 * string table and then attempt to add the new value. This function can be used
1094 * to delete any assigned string before changing the type to numeric by passing
1095 * in an empty string. This works because the first byte in the string table is
1096 * reserved for the empty (NULL) string.
1097 *
1098 * @returns VBox status code.
1099 * @retval VINF_SUCCESS on success.
1100 * @retval VERR_BUFFER_OVERFLOW if the string table is full.
1101 * @retval VERR_INVALID_PARAMETER if the enmFieldIdx isn't valid.
1102 * @retval VERR_INVALID_POINTER if pszString isn't valid.
1103 * @retval VERR_INVALID_MAGIC if pFilter is invalid.
1104 *
1105 * @param pFilter The filter.
1106 * @param enmFieldIdx The field index.
1107 * @param pszString The string to add.
1108 * @param fPurge Purge invalid UTF-8 encoding and control characters
1109 * before setting it.
1110 */
1111static int usbfilterSetString(PUSBFILTER pFilter, USBFILTERIDX enmFieldIdx, const char *pszString, bool fPurge)
1112{
1113 /*
1114 * Validate input.
1115 */
1116 AssertReturn(pFilter->u32Magic == USBFILTER_MAGIC, VERR_INVALID_MAGIC);
1117 AssertReturn((unsigned)enmFieldIdx < (unsigned)USBFILTERIDX_END, VERR_INVALID_PARAMETER);
1118 AssertPtrReturn(pszString, VERR_INVALID_POINTER);
1119
1120 Assert(pFilter->offCurEnd < sizeof(pFilter->achStrTab));
1121 Assert(pFilter->achStrTab[pFilter->offCurEnd] == '\0');
1122
1123 /*
1124 * Remove old string value if any.
1125 */
1126 if ( USBFilterIsMethodUsingStringValue((USBFILTERMATCH)pFilter->aFields[enmFieldIdx].enmMatch)
1127 && pFilter->aFields[enmFieldIdx].u16Value != 0)
1128 {
1129 uint32_t off = pFilter->aFields[enmFieldIdx].u16Value;
1130 pFilter->aFields[enmFieldIdx].u16Value = 0; /* Assign it to the NULL string. */
1131
1132 unsigned cchShift = (unsigned)strlen(&pFilter->achStrTab[off]) + 1;
1133 ssize_t cchToMove = (pFilter->offCurEnd + 1) - (off + cchShift);
1134 Assert(cchToMove >= 0);
1135 if (cchToMove > 0)
1136 {
1137 /* We're not last - must shift the strings. */
1138 memmove(&pFilter->achStrTab[off], &pFilter->achStrTab[off + cchShift], cchToMove);
1139 for (unsigned i = 0; i < RT_ELEMENTS(pFilter->aFields); i++)
1140 if ( pFilter->aFields[i].u16Value >= off
1141 && USBFilterIsMethodUsingStringValue((USBFILTERMATCH)pFilter->aFields[i].enmMatch))
1142 pFilter->aFields[i].u16Value -= cchShift;
1143 }
1144 pFilter->offCurEnd -= cchShift;
1145 Assert(pFilter->offCurEnd < sizeof(pFilter->achStrTab));
1146 Assert(pFilter->offCurEnd + cchShift <= sizeof(pFilter->achStrTab));
1147
1148 /* zero the unused string table (to allow lazyness/strictness elsewhere). */
1149 memset(&pFilter->achStrTab[pFilter->offCurEnd], '\0', cchShift);
1150 }
1151
1152 /*
1153 * Make a special case for the empty string.
1154 * (This also makes the delete logical above work correctly for the last string.)
1155 */
1156 if (!*pszString)
1157 pFilter->aFields[enmFieldIdx].u16Value = 0;
1158 else
1159 {
1160 size_t cch = strlen(pszString);
1161 if (pFilter->offCurEnd + cch + 2 > sizeof(pFilter->achStrTab))
1162 return VERR_BUFFER_OVERFLOW;
1163
1164 pFilter->aFields[enmFieldIdx].u16Value = pFilter->offCurEnd + 1;
1165 memcpy(&pFilter->achStrTab[pFilter->offCurEnd + 1], pszString, cch + 1);
1166 if (fPurge)
1167 cch = USBLibPurgeEncoding(&pFilter->achStrTab[pFilter->offCurEnd + 1]);
1168 pFilter->offCurEnd += (uint32_t)cch + 1;
1169 }
1170
1171 return VINF_SUCCESS;
1172}
1173
1174/**
1175 * Wrapper around usbfilterSetString() that deletes any string value
1176 * currently assigned to a field.
1177 *
1178 * Upon successful return the field contains a null string, nothing or a number.
1179 *
1180 * This function will validate the field index if there isn't any string
1181 * value to delete, thus preventing any extra validating of the index.
1182 *
1183 * @returns VBox status code. See usbfilterSetString.
1184 * @param pFilter The filter.
1185 * @param enmFieldIdx The index of the field which string value should be deleted.
1186 */
1187static int usbfilterDeleteAnyStringValue(PUSBFILTER pFilter, USBFILTERIDX enmFieldIdx)
1188{
1189 int rc = VINF_SUCCESS;
1190 if ( USBFilterIsMethodUsingStringValue((USBFILTERMATCH)pFilter->aFields[enmFieldIdx].enmMatch)
1191 && pFilter->aFields[enmFieldIdx].u16Value != 0)
1192 rc = usbfilterSetString(pFilter, enmFieldIdx, "", false /*fPurge*/);
1193 else if ((unsigned)enmFieldIdx >= (unsigned)USBFILTERIDX_END)
1194 rc = VERR_INVALID_PARAMETER;
1195 return rc;
1196}
1197
1198
1199/**
1200 * Sets a field to always match (ignore whatever is thrown at it).
1201 *
1202 * @returns VBox status code.
1203 * @retval VINF_SUCCESS on success.
1204 * @retval VERR_INVALID_PARAMETER if the enmFieldIdx isn't valid.
1205 * @retval VERR_INVALID_MAGIC if pFilter is invalid.
1206 *
1207 * @param pFilter The filter.
1208 * @param enmFieldIdx The field index. This must be a string field.
1209 */
1210USBLIB_DECL(int) USBFilterSetIgnore(PUSBFILTER pFilter, USBFILTERIDX enmFieldIdx)
1211{
1212 int rc = usbfilterDeleteAnyStringValue(pFilter, enmFieldIdx);
1213 if (RT_SUCCESS(rc))
1214 {
1215 pFilter->aFields[enmFieldIdx].enmMatch = USBFILTERMATCH_IGNORE;
1216 pFilter->aFields[enmFieldIdx].u16Value = 0;
1217 }
1218 return rc;
1219}
1220
1221
1222/**
1223 * Sets a field to match on device field present only.
1224 *
1225 * @returns VBox status code.
1226 * @retval VINF_SUCCESS on success.
1227 * @retval VERR_INVALID_PARAMETER if the enmFieldIdx isn't valid.
1228 * @retval VERR_INVALID_MAGIC if pFilter is invalid.
1229 *
1230 * @param pFilter The filter.
1231 * @param enmFieldIdx The field index. This must be a string field.
1232 */
1233USBLIB_DECL(int) USBFilterSetPresentOnly(PUSBFILTER pFilter, USBFILTERIDX enmFieldIdx)
1234{
1235 int rc = usbfilterDeleteAnyStringValue(pFilter, enmFieldIdx);
1236 if (RT_SUCCESS(rc))
1237 {
1238 pFilter->aFields[enmFieldIdx].enmMatch = USBFILTERMATCH_PRESENT;
1239 pFilter->aFields[enmFieldIdx].u16Value = 0;
1240 }
1241 return rc;
1242}
1243
1244
1245/**
1246 * Sets a field to exactly match a number.
1247 *
1248 * @returns VBox status code.
1249 * @retval VINF_SUCCESS on success.
1250 * @retval VERR_INVALID_PARAMETER if the enmFieldIdx isn't valid.
1251 * @retval VERR_INVALID_MAGIC if pFilter is invalid.
1252 *
1253 * @param pFilter The filter.
1254 * @param enmFieldIdx The field index. This must be a string field.
1255 * @param u16Value The string pattern.
1256 * @param fMustBePresent If set, a non-present field on the device will result in a mismatch.
1257 * If clear, a non-present field on the device will match.
1258 */
1259USBLIB_DECL(int) USBFilterSetNumExact(PUSBFILTER pFilter, USBFILTERIDX enmFieldIdx, uint16_t u16Value, bool fMustBePresent)
1260{
1261 int rc = USBFilterIsNumericField(enmFieldIdx) ? VINF_SUCCESS : VERR_INVALID_PARAMETER;
1262 if (RT_SUCCESS(rc))
1263 {
1264 rc = usbfilterDeleteAnyStringValue(pFilter, enmFieldIdx);
1265 if (RT_SUCCESS(rc))
1266 {
1267 pFilter->aFields[enmFieldIdx].u16Value = u16Value;
1268 pFilter->aFields[enmFieldIdx].enmMatch = fMustBePresent ? USBFILTERMATCH_NUM_EXACT : USBFILTERMATCH_NUM_EXACT_NP;
1269 }
1270 }
1271
1272 return rc;
1273}
1274
1275
1276/**
1277 * Sets a field to match a numeric expression.
1278 *
1279 * @returns VBox status code.
1280 * @retval VINF_SUCCESS on success.
1281 * @retval VERR_BUFFER_OVERFLOW if the string table is full.
1282 * @retval VERR_INVALID_PARAMETER if the enmFieldIdx or the numeric expression aren't valid.
1283 * @retval VERR_INVALID_POINTER if pszExpression isn't a valid pointer.
1284 * @retval VERR_INVALID_MAGIC if pFilter is invalid.
1285 *
1286 * @param pFilter The filter.
1287 * @param enmFieldIdx The field index. This must be a string field.
1288 * @param pszExpression The numeric expression.
1289 * @param fMustBePresent If set, a non-present field on the device will result in a mismatch.
1290 * If clear, a non-present field on the device will match.
1291 */
1292USBLIB_DECL(int) USBFilterSetNumExpression(PUSBFILTER pFilter, USBFILTERIDX enmFieldIdx, const char *pszExpression, bool fMustBePresent)
1293{
1294 int rc = USBFilterIsNumericField(enmFieldIdx) ? VINF_SUCCESS : VERR_INVALID_PARAMETER;
1295 if (RT_SUCCESS(rc))
1296 {
1297 /* Strip leading spaces and empty sub expressions (||). */
1298 while (*pszExpression && (RT_C_IS_BLANK(*pszExpression) || *pszExpression == '|'))
1299 pszExpression++;
1300
1301 rc = usbfilterValidateNumExpression(pszExpression);
1302 if (RT_SUCCESS(rc))
1303 {
1304 /* We could optimize the expression further (stripping spaces, convert numbers),
1305 but it's more work than what it's worth and it could upset some users. */
1306 rc = usbfilterSetString(pFilter, enmFieldIdx, pszExpression, false /*fPurge*/);
1307 if (RT_SUCCESS(rc))
1308 pFilter->aFields[enmFieldIdx].enmMatch = fMustBePresent ? USBFILTERMATCH_NUM_EXPRESSION : USBFILTERMATCH_NUM_EXPRESSION_NP;
1309 else if (rc == VERR_NO_DIGITS)
1310 rc = VERR_INVALID_PARAMETER;
1311 }
1312 }
1313 return rc;
1314}
1315
1316
1317/**
1318 * Sets a field to exactly match a string.
1319 *
1320 * @returns VBox status code.
1321 * @retval VINF_SUCCESS on success.
1322 * @retval VERR_BUFFER_OVERFLOW if the string table is full.
1323 * @retval VERR_INVALID_PARAMETER if the enmFieldIdx isn't valid.
1324 * @retval VERR_INVALID_POINTER if pszPattern isn't a valid pointer.
1325 * @retval VERR_INVALID_MAGIC if pFilter is invalid.
1326 *
1327 * @param pFilter The filter.
1328 * @param enmFieldIdx The field index. This must be a string field.
1329 * @param pszValue The string value.
1330 * @param fMustBePresent If set, a non-present field on the device will result in a mismatch.
1331 * If clear, a non-present field on the device will match.
1332 * @param fPurge Purge invalid UTF-8 encoding and control
1333 * characters before setting it.
1334 */
1335USBLIB_DECL(int) USBFilterSetStringExact(PUSBFILTER pFilter, USBFILTERIDX enmFieldIdx, const char *pszValue,
1336 bool fMustBePresent, bool fPurge)
1337{
1338 int rc = USBFilterIsStringField(enmFieldIdx) ? VINF_SUCCESS : VERR_INVALID_PARAMETER;
1339 if (RT_SUCCESS(rc))
1340 {
1341 rc = usbfilterSetString(pFilter, enmFieldIdx, pszValue, fPurge);
1342 if (RT_SUCCESS(rc))
1343 pFilter->aFields[enmFieldIdx].enmMatch = fMustBePresent ? USBFILTERMATCH_STR_EXACT : USBFILTERMATCH_STR_EXACT_NP;
1344 }
1345 return rc;
1346}
1347
1348
1349/**
1350 * Sets a field to match a string pattern.
1351 *
1352 * @returns VBox status code.
1353 * @retval VINF_SUCCESS on success.
1354 * @retval VERR_BUFFER_OVERFLOW if the string table is full.
1355 * @retval VERR_INVALID_PARAMETER if the enmFieldIdx or pattern aren't valid.
1356 * @retval VERR_INVALID_POINTER if pszPattern isn't a valid pointer.
1357 * @retval VERR_INVALID_MAGIC if pFilter is invalid.
1358 *
1359 * @param pFilter The filter.
1360 * @param enmFieldIdx The field index. This must be a string field.
1361 * @param pszPattern The string pattern.
1362 * @param fMustBePresent If set, a non-present field on the device will result in a mismatch.
1363 * If clear, a non-present field on the device will match.
1364 */
1365USBLIB_DECL(int) USBFilterSetStringPattern(PUSBFILTER pFilter, USBFILTERIDX enmFieldIdx, const char *pszPattern, bool fMustBePresent)
1366{
1367 int rc = USBFilterIsStringField(enmFieldIdx) ? VINF_SUCCESS : VERR_INVALID_PARAMETER;
1368 if (RT_SUCCESS(rc))
1369 {
1370 rc = usbfilterValidateStringPattern(pszPattern);
1371 if (RT_SUCCESS(rc))
1372 {
1373 rc = usbfilterSetString(pFilter, enmFieldIdx, pszPattern, false /*fPurge*/);
1374 if (RT_SUCCESS(rc))
1375 pFilter->aFields[enmFieldIdx].enmMatch = fMustBePresent ? USBFILTERMATCH_STR_PATTERN : USBFILTERMATCH_STR_PATTERN_NP;
1376 }
1377 }
1378 return rc;
1379}
1380
1381
1382/**
1383 * Sets the must-be-present part of a field.
1384 *
1385 * This only works on field which already has matching criteria. This means
1386 * that field marked 'ignore' will not be processed and will result in a
1387 * warning status code.
1388 *
1389 * @returns VBox status code.
1390 * @retval VINF_SUCCESS on success.
1391 * @retval VWRN_INVALID_PARAMETER if the field is marked 'ignore'. No assertions.
1392 * @retval VERR_INVALID_PARAMETER if the enmFieldIdx or pattern aren't valid.
1393 * @retval VERR_INVALID_POINTER if pszPattern isn't a valid pointer.
1394 * @retval VERR_INVALID_MAGIC if pFilter is invalid.
1395 *
1396 * @param pFilter The filter.
1397 * @param enmFieldIdx The field index.
1398 * @param fMustBePresent If set, a non-present field on the device will result in a mismatch.
1399 * If clear, a non-present field on the device will match.
1400 */
1401USBLIB_DECL(int) USBFilterSetMustBePresent(PUSBFILTER pFilter, USBFILTERIDX enmFieldIdx, bool fMustBePresent)
1402{
1403 AssertPtrReturn(pFilter, VERR_INVALID_POINTER);
1404 AssertReturn(pFilter->u32Magic == USBFILTER_MAGIC, VERR_INVALID_MAGIC);
1405 AssertReturn((unsigned)enmFieldIdx < (unsigned)USBFILTERIDX_END, VERR_INVALID_PARAMETER);
1406
1407 USBFILTERMATCH enmMatch = (USBFILTERMATCH)pFilter->aFields[enmFieldIdx].enmMatch;
1408 if (fMustBePresent)
1409 {
1410 switch (enmMatch)
1411 {
1412 case USBFILTERMATCH_IGNORE:
1413 return VWRN_INVALID_PARAMETER;
1414
1415 case USBFILTERMATCH_PRESENT:
1416 case USBFILTERMATCH_NUM_EXACT:
1417 case USBFILTERMATCH_NUM_EXPRESSION:
1418 case USBFILTERMATCH_STR_EXACT:
1419 case USBFILTERMATCH_STR_PATTERN:
1420 break;
1421
1422 case USBFILTERMATCH_NUM_EXACT_NP:
1423 enmMatch = USBFILTERMATCH_NUM_EXACT;
1424 break;
1425 case USBFILTERMATCH_NUM_EXPRESSION_NP:
1426 enmMatch = USBFILTERMATCH_NUM_EXPRESSION;
1427 break;
1428 case USBFILTERMATCH_STR_EXACT_NP:
1429 enmMatch = USBFILTERMATCH_STR_EXACT;
1430 break;
1431 case USBFILTERMATCH_STR_PATTERN_NP:
1432 enmMatch = USBFILTERMATCH_STR_PATTERN;
1433 break;
1434 default:
1435 AssertMsgFailedReturn(("%p: enmFieldIdx=%d enmMatch=%d\n", pFilter, enmFieldIdx, enmMatch), VERR_INVALID_MAGIC);
1436 }
1437 }
1438 else
1439 {
1440 switch (enmMatch)
1441 {
1442 case USBFILTERMATCH_IGNORE:
1443 return VWRN_INVALID_PARAMETER;
1444
1445 case USBFILTERMATCH_NUM_EXACT_NP:
1446 case USBFILTERMATCH_STR_PATTERN_NP:
1447 case USBFILTERMATCH_STR_EXACT_NP:
1448 case USBFILTERMATCH_NUM_EXPRESSION_NP:
1449 break;
1450
1451 case USBFILTERMATCH_PRESENT:
1452 enmMatch = USBFILTERMATCH_IGNORE;
1453 break;
1454 case USBFILTERMATCH_NUM_EXACT:
1455 enmMatch = USBFILTERMATCH_NUM_EXACT_NP;
1456 break;
1457 case USBFILTERMATCH_NUM_EXPRESSION:
1458 enmMatch = USBFILTERMATCH_NUM_EXPRESSION_NP;
1459 break;
1460 case USBFILTERMATCH_STR_EXACT:
1461 enmMatch = USBFILTERMATCH_STR_EXACT_NP;
1462 break;
1463 case USBFILTERMATCH_STR_PATTERN:
1464 enmMatch = USBFILTERMATCH_STR_PATTERN_NP;
1465 break;
1466
1467 default:
1468 AssertMsgFailedReturn(("%p: enmFieldIdx=%d enmMatch=%d\n", pFilter, enmFieldIdx, enmMatch), VERR_INVALID_MAGIC);
1469 }
1470 }
1471
1472 pFilter->aFields[enmFieldIdx].enmMatch = enmMatch;
1473 return VINF_SUCCESS;
1474}
1475
1476
1477/**
1478 * Gets the filter type.
1479 *
1480 * @returns The filter type.
1481 * USBFILTERTYPE_INVALID if the filter is invalid.
1482 * @param pFilter The filter.
1483 */
1484USBLIB_DECL(USBFILTERTYPE) USBFilterGetFilterType(PCUSBFILTER pFilter)
1485{
1486 AssertReturn(pFilter->u32Magic == USBFILTER_MAGIC, USBFILTERTYPE_INVALID);
1487 return pFilter->enmType;
1488}
1489
1490
1491/**
1492 * Gets the matching method for a field.
1493 *
1494 * @returns The matching method on success, UBFILTERMATCH_INVALID on invalid field index.
1495 * @param pFilter The filter.
1496 * @param enmFieldIdx The field index.
1497 */
1498USBLIB_DECL(USBFILTERMATCH) USBFilterGetMatchingMethod(PCUSBFILTER pFilter, USBFILTERIDX enmFieldIdx)
1499{
1500 if ( pFilter->u32Magic == USBFILTER_MAGIC
1501 && (unsigned)enmFieldIdx < (unsigned)USBFILTERIDX_END)
1502 return (USBFILTERMATCH)pFilter->aFields[enmFieldIdx].enmMatch;
1503 return USBFILTERMATCH_INVALID;
1504}
1505
1506
1507/**
1508 * Gets the numeric value of a field.
1509 *
1510 * The field must contain a number, we're not doing any conversions for you.
1511 *
1512 * @returns VBox status code.
1513 * @retval VINF_SUCCESS on success.
1514 * @retval VERR_INVALID_PARAMETER if the enmFieldIdx isn't valid or if the field doesn't contain a number.
1515 * @retval VERR_INVALID_MAGIC if pFilter is invalid.
1516 *
1517 * @param pFilter The filter.
1518 * @param enmFieldIdx The field index.
1519 * @param pu16Value Where to store the value.
1520 */
1521USBLIB_DECL(int) USBFilterQueryNum(PUSBFILTER pFilter, USBFILTERIDX enmFieldIdx, uint16_t *pu16Value)
1522{
1523 AssertReturn(pFilter->u32Magic == USBFILTER_MAGIC, VERR_INVALID_MAGIC);
1524 int iValue = usbfilterGetNum(pFilter, enmFieldIdx);
1525 if (iValue == -1)
1526 return VERR_INVALID_PARAMETER;
1527 *pu16Value = (uint16_t)iValue;
1528 return VINF_SUCCESS;
1529}
1530
1531
1532/**
1533 * Gets the numeric value of a field.
1534 *
1535 * The field must contain a number, we're not doing any conversions for you.
1536 *
1537 * @returns The field value on success, -1 on failure (invalid input / not numeric).
1538 *
1539 * @param pFilter The filter.
1540 * @param enmFieldIdx The field index.
1541 */
1542USBLIB_DECL(int) USBFilterGetNum(PCUSBFILTER pFilter, USBFILTERIDX enmFieldIdx)
1543{
1544 AssertReturn(pFilter->u32Magic == USBFILTER_MAGIC, -1);
1545 return usbfilterGetNum(pFilter, enmFieldIdx);
1546}
1547
1548
1549/**
1550 * Gets the string value of a field.
1551 *
1552 * The field must contain a string, we're not doing any conversions for you.
1553 *
1554 * @returns VBox status code.
1555 * @retval VINF_SUCCESS on success.
1556 * @retval VERR_BUFFER_OVERFLOW if the buffer isn't sufficient to hold the string. The buffer
1557 * will be filled with as much of the string that'll fit.
1558 * @retval VERR_INVALID_PARAMETER if the enmFieldIdx isn't valid or if the field doesn't contain a string.
1559 * @retval VERR_INVALID_MAGIC if pFilter is invalid.
1560 *
1561 * @param pFilter The filter.
1562 * @param enmFieldIdx The field index.
1563 * @param pszBuf Where to store the string.
1564 * @param cchBuf The size of the buffer.
1565 */
1566USBLIB_DECL(int) USBFilterQueryString(PUSBFILTER pFilter, USBFILTERIDX enmFieldIdx, char *pszBuf, size_t cchBuf)
1567{
1568 AssertReturn(pFilter->u32Magic == USBFILTER_MAGIC, VERR_INVALID_MAGIC);
1569
1570 const char *psz = usbfilterGetString(pFilter, enmFieldIdx);
1571 if (RT_UNLIKELY(!psz))
1572 return VERR_INVALID_PARAMETER;
1573
1574 int rc = VINF_SUCCESS;
1575 size_t cch = strlen(psz);
1576 if (cch < cchBuf)
1577 memcpy(pszBuf, psz, cch + 1);
1578 else
1579 {
1580 rc = VERR_BUFFER_OVERFLOW;
1581 if (cchBuf)
1582 {
1583 memcpy(pszBuf, psz, cchBuf - 1);
1584 pszBuf[cchBuf - 1] = '\0';
1585 }
1586 }
1587
1588 return rc;
1589}
1590
1591
1592/**
1593 * Gets the string table entry for a field.
1594 *
1595 * @returns Pointer to the string. (readonly!)
1596 *
1597 * @param pFilter The filter.
1598 * @param enmFieldIdx The field index.
1599 */
1600USBLIB_DECL(const char *) USBFilterGetString(PCUSBFILTER pFilter, USBFILTERIDX enmFieldIdx)
1601{
1602 AssertReturn(pFilter->u32Magic == USBFILTER_MAGIC, NULL);
1603
1604 const char *psz = usbfilterGetString(pFilter, enmFieldIdx);
1605 if (RT_UNLIKELY(!psz))
1606 return NULL;
1607 return psz;
1608}
1609
1610
1611/**
1612 * Gets the string length of a field containing a string.
1613 *
1614 * @returns String length on success, -1 on failure (not a string, bad filter).
1615 * @param pFilter The filter.
1616 * @param enmFieldIdx The field index.
1617 */
1618USBLIB_DECL(ssize_t) USBFilterGetStringLen(PCUSBFILTER pFilter, USBFILTERIDX enmFieldIdx)
1619{
1620 if (RT_LIKELY(pFilter->u32Magic == USBFILTER_MAGIC))
1621 {
1622 const char *psz = usbfilterGetString(pFilter, enmFieldIdx);
1623 if (RT_LIKELY(psz))
1624 return strlen(psz);
1625 }
1626 return -1;
1627}
1628
1629
1630/**
1631 * Check if any of the fields are set to something substatial.
1632 *
1633 * Consider the fileter a wildcard if this returns false.
1634 *
1635 * @returns true / false.
1636 * @param pFilter The filter.
1637 */
1638USBLIB_DECL(bool) USBFilterHasAnySubstatialCriteria(PCUSBFILTER pFilter)
1639{
1640 AssertReturn(pFilter->u32Magic == USBFILTER_MAGIC, false);
1641
1642 for (unsigned i = 0; i < RT_ELEMENTS(pFilter->aFields); i++)
1643 {
1644 switch (pFilter->aFields[i].enmMatch)
1645 {
1646 case USBFILTERMATCH_IGNORE:
1647 case USBFILTERMATCH_PRESENT:
1648 break;
1649
1650 case USBFILTERMATCH_NUM_EXACT:
1651 case USBFILTERMATCH_NUM_EXACT_NP:
1652 case USBFILTERMATCH_STR_EXACT:
1653 case USBFILTERMATCH_STR_EXACT_NP:
1654 return true;
1655
1656 case USBFILTERMATCH_NUM_EXPRESSION:
1657 case USBFILTERMATCH_NUM_EXPRESSION_NP:
1658 {
1659 const char *psz = usbfilterGetString(pFilter, (USBFILTERIDX)i);
1660 if (psz)
1661 {
1662 while (*psz && (*psz == '|' || RT_C_IS_BLANK(*psz)))
1663 psz++;
1664 if (*psz)
1665 return true;
1666 }
1667 break;
1668 }
1669
1670 case USBFILTERMATCH_STR_PATTERN:
1671 case USBFILTERMATCH_STR_PATTERN_NP:
1672 {
1673 const char *psz = usbfilterGetString(pFilter, (USBFILTERIDX)i);
1674 if (psz)
1675 {
1676 while (*psz && (*psz == '*' || *psz == '?'))
1677 psz++;
1678 if (*psz)
1679 return true;
1680 }
1681 break;
1682 }
1683 }
1684 }
1685
1686 return false;
1687}
1688
1689
1690
1691/**
1692 * Checks whether the specified field is a numeric field or not.
1693 *
1694 * @returns true / false.
1695 * @param enmFieldIdx The field index.
1696 */
1697USBLIB_DECL(bool) USBFilterIsNumericField(USBFILTERIDX enmFieldIdx)
1698{
1699 switch (enmFieldIdx)
1700 {
1701 case USBFILTERIDX_VENDOR_ID:
1702 case USBFILTERIDX_PRODUCT_ID:
1703 case USBFILTERIDX_DEVICE:
1704 case USBFILTERIDX_DEVICE_CLASS:
1705 case USBFILTERIDX_DEVICE_SUB_CLASS:
1706 case USBFILTERIDX_DEVICE_PROTOCOL:
1707 case USBFILTERIDX_BUS:
1708 case USBFILTERIDX_PORT:
1709 return true;
1710
1711 default:
1712 AssertMsgFailed(("%d\n", enmFieldIdx));
1713 RT_FALL_THRU();
1714 case USBFILTERIDX_MANUFACTURER_STR:
1715 case USBFILTERIDX_PRODUCT_STR:
1716 case USBFILTERIDX_SERIAL_NUMBER_STR:
1717 return false;
1718 }
1719}
1720
1721
1722/**
1723 * Checks whether the specified field is a string field or not.
1724 *
1725 * @returns true / false.
1726 * @param enmFieldIdx The field index.
1727 */
1728USBLIB_DECL(bool) USBFilterIsStringField(USBFILTERIDX enmFieldIdx)
1729{
1730 switch (enmFieldIdx)
1731 {
1732 default:
1733 AssertMsgFailed(("%d\n", enmFieldIdx));
1734 RT_FALL_THRU();
1735 case USBFILTERIDX_VENDOR_ID:
1736 case USBFILTERIDX_PRODUCT_ID:
1737 case USBFILTERIDX_DEVICE:
1738 case USBFILTERIDX_DEVICE_CLASS:
1739 case USBFILTERIDX_DEVICE_SUB_CLASS:
1740 case USBFILTERIDX_DEVICE_PROTOCOL:
1741 case USBFILTERIDX_BUS:
1742 case USBFILTERIDX_PORT:
1743 return false;
1744
1745 case USBFILTERIDX_MANUFACTURER_STR:
1746 case USBFILTERIDX_PRODUCT_STR:
1747 case USBFILTERIDX_SERIAL_NUMBER_STR:
1748 return true;
1749 }
1750}
1751
1752
1753/**
1754 * Checks whether the specified matching method uses a numeric value or not.
1755 *
1756 * @returns true / false.
1757 * @param enmMatchingMethod The matching method.
1758 */
1759USBLIB_DECL(bool) USBFilterIsMethodUsingNumericValue(USBFILTERMATCH enmMatchingMethod)
1760{
1761 switch (enmMatchingMethod)
1762 {
1763 default:
1764 AssertMsgFailed(("%d\n", enmMatchingMethod));
1765 RT_FALL_THRU();
1766 case USBFILTERMATCH_IGNORE:
1767 case USBFILTERMATCH_PRESENT:
1768 case USBFILTERMATCH_NUM_EXPRESSION:
1769 case USBFILTERMATCH_NUM_EXPRESSION_NP:
1770 case USBFILTERMATCH_STR_EXACT:
1771 case USBFILTERMATCH_STR_EXACT_NP:
1772 case USBFILTERMATCH_STR_PATTERN:
1773 case USBFILTERMATCH_STR_PATTERN_NP:
1774 return false;
1775
1776 case USBFILTERMATCH_NUM_EXACT:
1777 case USBFILTERMATCH_NUM_EXACT_NP:
1778 return true;
1779 }
1780}
1781
1782
1783/**
1784 * Checks whether the specified matching method uses a string value or not.
1785 *
1786 * @returns true / false.
1787 * @param enmMatchingMethod The matching method.
1788 */
1789USBLIB_DECL(bool) USBFilterIsMethodUsingStringValue(USBFILTERMATCH enmMatchingMethod)
1790{
1791 switch (enmMatchingMethod)
1792 {
1793 default:
1794 AssertMsgFailed(("%d\n", enmMatchingMethod));
1795 RT_FALL_THRU();
1796 case USBFILTERMATCH_IGNORE:
1797 case USBFILTERMATCH_PRESENT:
1798 case USBFILTERMATCH_NUM_EXACT:
1799 case USBFILTERMATCH_NUM_EXACT_NP:
1800 return false;
1801
1802 case USBFILTERMATCH_NUM_EXPRESSION:
1803 case USBFILTERMATCH_NUM_EXPRESSION_NP:
1804 case USBFILTERMATCH_STR_EXACT:
1805 case USBFILTERMATCH_STR_EXACT_NP:
1806 case USBFILTERMATCH_STR_PATTERN:
1807 case USBFILTERMATCH_STR_PATTERN_NP:
1808 return true;
1809 }
1810}
1811
1812
1813/**
1814 * Checks if a matching method is for numeric fields or not.
1815 *
1816 * @returns true / false.
1817 * @param enmMatchingMethod The matching method.
1818 */
1819USBLIB_DECL(bool) USBFilterIsMethodNumeric(USBFILTERMATCH enmMatchingMethod)
1820{
1821 return enmMatchingMethod >= USBFILTERMATCH_NUM_FIRST
1822 && enmMatchingMethod <= USBFILTERMATCH_NUM_LAST;
1823}
1824
1825/**
1826 * Checks if a matching method is for string fields or not.
1827 *
1828 * @returns true / false.
1829 * @param enmMatchingMethod The matching method.
1830 */
1831USBLIB_DECL(bool) USBFilterIsMethodString(USBFILTERMATCH enmMatchingMethod)
1832{
1833 return enmMatchingMethod >= USBFILTERMATCH_STR_FIRST
1834 && enmMatchingMethod <= USBFILTERMATCH_STR_LAST;
1835}
1836
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette