1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * names.c -- USB name database manipulation routines |
4 | * |
5 | * Copyright (C) 1999, 2000 Thomas Sailer (sailer@ife.ee.ethz.ch) |
6 | * |
7 | * Copyright (C) 2005 Takahiro Hirofuchi |
8 | * - names_deinit() is added. |
9 | */ |
10 | |
11 | #include <sys/types.h> |
12 | #include <sys/stat.h> |
13 | #include <fcntl.h> |
14 | #include <dirent.h> |
15 | #include <string.h> |
16 | #include <errno.h> |
17 | #include <stdlib.h> |
18 | #include <unistd.h> |
19 | #include <stdio.h> |
20 | #include <ctype.h> |
21 | |
22 | #include "names.h" |
23 | #include "usbip_common.h" |
24 | |
25 | struct vendor { |
26 | struct vendor *next; |
27 | u_int16_t vendorid; |
28 | char name[1]; |
29 | }; |
30 | |
31 | struct product { |
32 | struct product *next; |
33 | u_int16_t vendorid, productid; |
34 | char name[1]; |
35 | }; |
36 | |
37 | struct class { |
38 | struct class *next; |
39 | u_int8_t classid; |
40 | char name[1]; |
41 | }; |
42 | |
43 | struct subclass { |
44 | struct subclass *next; |
45 | u_int8_t classid, subclassid; |
46 | char name[1]; |
47 | }; |
48 | |
49 | struct protocol { |
50 | struct protocol *next; |
51 | u_int8_t classid, subclassid, protocolid; |
52 | char name[1]; |
53 | }; |
54 | |
55 | struct genericstrtable { |
56 | struct genericstrtable *next; |
57 | unsigned int num; |
58 | char name[1]; |
59 | }; |
60 | |
61 | |
62 | #define HASH1 0x10 |
63 | #define HASH2 0x02 |
64 | #define HASHSZ 16 |
65 | |
66 | static unsigned int hashnum(unsigned int num) |
67 | { |
68 | unsigned int mask1 = HASH1 << 27, mask2 = HASH2 << 27; |
69 | |
70 | for (; mask1 >= HASH1; mask1 >>= 1, mask2 >>= 1) |
71 | if (num & mask1) |
72 | num ^= mask2; |
73 | return num & (HASHSZ-1); |
74 | } |
75 | |
76 | |
77 | static struct vendor *vendors[HASHSZ] = { NULL, }; |
78 | static struct product *products[HASHSZ] = { NULL, }; |
79 | static struct class *classes[HASHSZ] = { NULL, }; |
80 | static struct subclass *subclasses[HASHSZ] = { NULL, }; |
81 | static struct protocol *protocols[HASHSZ] = { NULL, }; |
82 | |
83 | const char *names_vendor(u_int16_t vendorid) |
84 | { |
85 | struct vendor *v; |
86 | |
87 | v = vendors[hashnum(num: vendorid)]; |
88 | for (; v; v = v->next) |
89 | if (v->vendorid == vendorid) |
90 | return v->name; |
91 | return NULL; |
92 | } |
93 | |
94 | const char *names_product(u_int16_t vendorid, u_int16_t productid) |
95 | { |
96 | struct product *p; |
97 | |
98 | p = products[hashnum(num: (vendorid << 16) | productid)]; |
99 | for (; p; p = p->next) |
100 | if (p->vendorid == vendorid && p->productid == productid) |
101 | return p->name; |
102 | return NULL; |
103 | } |
104 | |
105 | const char *names_class(u_int8_t classid) |
106 | { |
107 | struct class *c; |
108 | |
109 | c = classes[hashnum(num: classid)]; |
110 | for (; c; c = c->next) |
111 | if (c->classid == classid) |
112 | return c->name; |
113 | return NULL; |
114 | } |
115 | |
116 | const char *names_subclass(u_int8_t classid, u_int8_t subclassid) |
117 | { |
118 | struct subclass *s; |
119 | |
120 | s = subclasses[hashnum(num: (classid << 8) | subclassid)]; |
121 | for (; s; s = s->next) |
122 | if (s->classid == classid && s->subclassid == subclassid) |
123 | return s->name; |
124 | return NULL; |
125 | } |
126 | |
127 | const char *names_protocol(u_int8_t classid, u_int8_t subclassid, |
128 | u_int8_t protocolid) |
129 | { |
130 | struct protocol *p; |
131 | |
132 | p = protocols[hashnum(num: (classid << 16) | (subclassid << 8) |
133 | | protocolid)]; |
134 | for (; p; p = p->next) |
135 | if (p->classid == classid && p->subclassid == subclassid && |
136 | p->protocolid == protocolid) |
137 | return p->name; |
138 | return NULL; |
139 | } |
140 | |
141 | /* add a cleanup function by takahiro */ |
142 | struct pool { |
143 | struct pool *next; |
144 | void *mem; |
145 | }; |
146 | |
147 | static struct pool *pool_head; |
148 | |
149 | static void *my_malloc(size_t size) |
150 | { |
151 | struct pool *p; |
152 | |
153 | p = calloc(1, sizeof(struct pool)); |
154 | if (!p) |
155 | return NULL; |
156 | |
157 | p->mem = calloc(1, size); |
158 | if (!p->mem) { |
159 | free(p); |
160 | return NULL; |
161 | } |
162 | |
163 | p->next = pool_head; |
164 | pool_head = p; |
165 | |
166 | return p->mem; |
167 | } |
168 | |
169 | void names_free(void) |
170 | { |
171 | struct pool *pool; |
172 | |
173 | if (!pool_head) |
174 | return; |
175 | |
176 | for (pool = pool_head; pool != NULL; ) { |
177 | struct pool *tmp; |
178 | |
179 | if (pool->mem) |
180 | free(pool->mem); |
181 | |
182 | tmp = pool; |
183 | pool = pool->next; |
184 | free(tmp); |
185 | } |
186 | } |
187 | |
188 | static int new_vendor(const char *name, u_int16_t vendorid) |
189 | { |
190 | struct vendor *v; |
191 | unsigned int h = hashnum(num: vendorid); |
192 | |
193 | v = vendors[h]; |
194 | for (; v; v = v->next) |
195 | if (v->vendorid == vendorid) |
196 | return -1; |
197 | v = my_malloc(size: sizeof(struct vendor) + strlen(name)); |
198 | if (!v) |
199 | return -1; |
200 | strcpy(v->name, name); |
201 | v->vendorid = vendorid; |
202 | v->next = vendors[h]; |
203 | vendors[h] = v; |
204 | return 0; |
205 | } |
206 | |
207 | static int new_product(const char *name, u_int16_t vendorid, |
208 | u_int16_t productid) |
209 | { |
210 | struct product *p; |
211 | unsigned int h = hashnum(num: (vendorid << 16) | productid); |
212 | |
213 | p = products[h]; |
214 | for (; p; p = p->next) |
215 | if (p->vendorid == vendorid && p->productid == productid) |
216 | return -1; |
217 | p = my_malloc(size: sizeof(struct product) + strlen(name)); |
218 | if (!p) |
219 | return -1; |
220 | strcpy(p->name, name); |
221 | p->vendorid = vendorid; |
222 | p->productid = productid; |
223 | p->next = products[h]; |
224 | products[h] = p; |
225 | return 0; |
226 | } |
227 | |
228 | static int new_class(const char *name, u_int8_t classid) |
229 | { |
230 | struct class *c; |
231 | unsigned int h = hashnum(num: classid); |
232 | |
233 | c = classes[h]; |
234 | for (; c; c = c->next) |
235 | if (c->classid == classid) |
236 | return -1; |
237 | c = my_malloc(size: sizeof(struct class) + strlen(name)); |
238 | if (!c) |
239 | return -1; |
240 | strcpy(c->name, name); |
241 | c->classid = classid; |
242 | c->next = classes[h]; |
243 | classes[h] = c; |
244 | return 0; |
245 | } |
246 | |
247 | static int new_subclass(const char *name, u_int8_t classid, u_int8_t subclassid) |
248 | { |
249 | struct subclass *s; |
250 | unsigned int h = hashnum(num: (classid << 8) | subclassid); |
251 | |
252 | s = subclasses[h]; |
253 | for (; s; s = s->next) |
254 | if (s->classid == classid && s->subclassid == subclassid) |
255 | return -1; |
256 | s = my_malloc(size: sizeof(struct subclass) + strlen(name)); |
257 | if (!s) |
258 | return -1; |
259 | strcpy(s->name, name); |
260 | s->classid = classid; |
261 | s->subclassid = subclassid; |
262 | s->next = subclasses[h]; |
263 | subclasses[h] = s; |
264 | return 0; |
265 | } |
266 | |
267 | static int new_protocol(const char *name, u_int8_t classid, u_int8_t subclassid, |
268 | u_int8_t protocolid) |
269 | { |
270 | struct protocol *p; |
271 | unsigned int h = hashnum(num: (classid << 16) | (subclassid << 8) |
272 | | protocolid); |
273 | |
274 | p = protocols[h]; |
275 | for (; p; p = p->next) |
276 | if (p->classid == classid && p->subclassid == subclassid |
277 | && p->protocolid == protocolid) |
278 | return -1; |
279 | p = my_malloc(size: sizeof(struct protocol) + strlen(name)); |
280 | if (!p) |
281 | return -1; |
282 | strcpy(p->name, name); |
283 | p->classid = classid; |
284 | p->subclassid = subclassid; |
285 | p->protocolid = protocolid; |
286 | p->next = protocols[h]; |
287 | protocols[h] = p; |
288 | return 0; |
289 | } |
290 | |
291 | static void parse(FILE *f) |
292 | { |
293 | char buf[512], *cp; |
294 | unsigned int linectr = 0; |
295 | int lastvendor = -1; |
296 | int lastclass = -1; |
297 | int lastsubclass = -1; |
298 | int lasthut = -1; |
299 | int lastlang = -1; |
300 | unsigned int u; |
301 | |
302 | while (fgets(buf, sizeof(buf), f)) { |
303 | linectr++; |
304 | /* remove line ends */ |
305 | cp = strchr(buf, '\r'); |
306 | if (cp) |
307 | *cp = 0; |
308 | cp = strchr(buf, '\n'); |
309 | if (cp) |
310 | *cp = 0; |
311 | if (buf[0] == '#' || !buf[0]) |
312 | continue; |
313 | cp = buf; |
314 | if (buf[0] == 'P' && buf[1] == 'H' && buf[2] == 'Y' && |
315 | buf[3] == 'S' && buf[4] == 'D' && |
316 | buf[5] == 'E' && buf[6] == 'S' && /*isspace(buf[7])*/ |
317 | buf[7] == ' ') { |
318 | continue; |
319 | } |
320 | if (buf[0] == 'P' && buf[1] == 'H' && |
321 | buf[2] == 'Y' && /*isspace(buf[3])*/ buf[3] == ' ') { |
322 | continue; |
323 | } |
324 | if (buf[0] == 'B' && buf[1] == 'I' && buf[2] == 'A' && |
325 | buf[3] == 'S' && /*isspace(buf[4])*/ buf[4] == ' ') { |
326 | continue; |
327 | } |
328 | if (buf[0] == 'L' && /*isspace(buf[1])*/ buf[1] == ' ') { |
329 | lasthut = lastclass = lastvendor = lastsubclass = -1; |
330 | /* |
331 | * set 1 as pseudo-id to indicate that the parser is |
332 | * in a `L' section. |
333 | */ |
334 | lastlang = 1; |
335 | continue; |
336 | } |
337 | if (buf[0] == 'C' && /*isspace(buf[1])*/ buf[1] == ' ') { |
338 | /* class spec */ |
339 | cp = buf+2; |
340 | while (isspace(*cp)) |
341 | cp++; |
342 | if (!isxdigit(*cp)) { |
343 | err("Invalid class spec at line %u" , linectr); |
344 | continue; |
345 | } |
346 | u = strtoul(cp, &cp, 16); |
347 | while (isspace(*cp)) |
348 | cp++; |
349 | if (!*cp) { |
350 | err("Invalid class spec at line %u" , linectr); |
351 | continue; |
352 | } |
353 | if (new_class(name: cp, classid: u)) |
354 | err("Duplicate class spec at line %u class %04x %s" , |
355 | linectr, u, cp); |
356 | dbg("line %5u class %02x %s" , linectr, u, cp); |
357 | lasthut = lastlang = lastvendor = lastsubclass = -1; |
358 | lastclass = u; |
359 | continue; |
360 | } |
361 | if (buf[0] == 'A' && buf[1] == 'T' && isspace(buf[2])) { |
362 | /* audio terminal type spec */ |
363 | continue; |
364 | } |
365 | if (buf[0] == 'H' && buf[1] == 'C' && buf[2] == 'C' |
366 | && isspace(buf[3])) { |
367 | /* HID Descriptor bCountryCode */ |
368 | continue; |
369 | } |
370 | if (isxdigit(*cp)) { |
371 | /* vendor */ |
372 | u = strtoul(cp, &cp, 16); |
373 | while (isspace(*cp)) |
374 | cp++; |
375 | if (!*cp) { |
376 | err("Invalid vendor spec at line %u" , linectr); |
377 | continue; |
378 | } |
379 | if (new_vendor(name: cp, vendorid: u)) |
380 | err("Duplicate vendor spec at line %u vendor %04x %s" , |
381 | linectr, u, cp); |
382 | dbg("line %5u vendor %04x %s" , linectr, u, cp); |
383 | lastvendor = u; |
384 | lasthut = lastlang = lastclass = lastsubclass = -1; |
385 | continue; |
386 | } |
387 | if (buf[0] == '\t' && isxdigit(buf[1])) { |
388 | /* product or subclass spec */ |
389 | u = strtoul(buf+1, &cp, 16); |
390 | while (isspace(*cp)) |
391 | cp++; |
392 | if (!*cp) { |
393 | err("Invalid product/subclass spec at line %u" , |
394 | linectr); |
395 | continue; |
396 | } |
397 | if (lastvendor != -1) { |
398 | if (new_product(name: cp, vendorid: lastvendor, productid: u)) |
399 | err("Duplicate product spec at line %u product %04x:%04x %s" , |
400 | linectr, lastvendor, u, cp); |
401 | dbg("line %5u product %04x:%04x %s" , linectr, |
402 | lastvendor, u, cp); |
403 | continue; |
404 | } |
405 | if (lastclass != -1) { |
406 | if (new_subclass(name: cp, classid: lastclass, subclassid: u)) |
407 | err("Duplicate subclass spec at line %u class %02x:%02x %s" , |
408 | linectr, lastclass, u, cp); |
409 | dbg("line %5u subclass %02x:%02x %s" , linectr, |
410 | lastclass, u, cp); |
411 | lastsubclass = u; |
412 | continue; |
413 | } |
414 | if (lasthut != -1) { |
415 | /* do not store hut */ |
416 | continue; |
417 | } |
418 | if (lastlang != -1) { |
419 | /* do not store langid */ |
420 | continue; |
421 | } |
422 | err("Product/Subclass spec without prior Vendor/Class spec at line %u" , |
423 | linectr); |
424 | continue; |
425 | } |
426 | if (buf[0] == '\t' && buf[1] == '\t' && isxdigit(buf[2])) { |
427 | /* protocol spec */ |
428 | u = strtoul(buf+2, &cp, 16); |
429 | while (isspace(*cp)) |
430 | cp++; |
431 | if (!*cp) { |
432 | err("Invalid protocol spec at line %u" , |
433 | linectr); |
434 | continue; |
435 | } |
436 | if (lastclass != -1 && lastsubclass != -1) { |
437 | if (new_protocol(name: cp, classid: lastclass, subclassid: lastsubclass, |
438 | protocolid: u)) |
439 | err("Duplicate protocol spec at line %u class %02x:%02x:%02x %s" , |
440 | linectr, lastclass, lastsubclass, |
441 | u, cp); |
442 | dbg("line %5u protocol %02x:%02x:%02x %s" , |
443 | linectr, lastclass, lastsubclass, u, cp); |
444 | continue; |
445 | } |
446 | err("Protocol spec without prior Class and Subclass spec at line %u" , |
447 | linectr); |
448 | continue; |
449 | } |
450 | if (buf[0] == 'H' && buf[1] == 'I' && |
451 | buf[2] == 'D' && /*isspace(buf[3])*/ buf[3] == ' ') { |
452 | continue; |
453 | } |
454 | if (buf[0] == 'H' && buf[1] == 'U' && |
455 | buf[2] == 'T' && /*isspace(buf[3])*/ buf[3] == ' ') { |
456 | lastlang = lastclass = lastvendor = lastsubclass = -1; |
457 | /* |
458 | * set 1 as pseudo-id to indicate that the parser is |
459 | * in a `HUT' section. |
460 | */ |
461 | lasthut = 1; |
462 | continue; |
463 | } |
464 | if (buf[0] == 'R' && buf[1] == ' ') |
465 | continue; |
466 | |
467 | if (buf[0] == 'V' && buf[1] == 'T') |
468 | continue; |
469 | |
470 | err("Unknown line at line %u" , linectr); |
471 | } |
472 | } |
473 | |
474 | |
475 | int names_init(char *n) |
476 | { |
477 | FILE *f; |
478 | |
479 | f = fopen(n, "r" ); |
480 | if (!f) |
481 | return errno; |
482 | |
483 | parse(f); |
484 | fclose(f); |
485 | return 0; |
486 | } |
487 | |