]>
Commit | Line | Data |
---|---|---|
2ac85b93 RR |
1 | /* |
2 | * Tester for VSCARD protocol, client side. | |
3 | * | |
4 | * Can be used with ccid-card-passthru. | |
5 | * | |
6 | * Copyright (c) 2011 Red Hat. | |
7 | * Written by Alon Levy. | |
8 | * | |
9 | * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. | |
10 | * See the COPYING.LIB file in the top-level directory. | |
11 | */ | |
12 | ||
c9495ee9 | 13 | #ifndef _WIN32 |
18b1afa8 EM |
14 | #include <sys/socket.h> |
15 | #include <netinet/in.h> | |
2ac85b93 | 16 | #include <netdb.h> |
2a0c46da | 17 | #define closesocket(x) close(x) |
c9495ee9 | 18 | #endif |
2ac85b93 RR |
19 | |
20 | #include "qemu-common.h" | |
2ac85b93 RR |
21 | |
22 | #include "vscard_common.h" | |
23 | ||
24 | #include "vreader.h" | |
25 | #include "vcard_emul.h" | |
26 | #include "vevent.h" | |
27 | ||
c9495ee9 | 28 | static int verbose; |
2ac85b93 RR |
29 | |
30 | static void | |
31 | print_byte_array( | |
32 | uint8_t *arrBytes, | |
33 | unsigned int nSize | |
34 | ) { | |
35 | int i; | |
36 | for (i = 0; i < nSize; i++) { | |
37 | printf("%02X ", arrBytes[i]); | |
38 | } | |
39 | printf("\n"); | |
40 | } | |
41 | ||
42 | static void | |
43 | print_usage(void) { | |
44 | printf("vscclient [-c <certname> .. -e <emul_args> -d <level>%s] " | |
45 | "<host> <port>\n", | |
46 | #ifdef USE_PASSTHRU | |
47 | " -p"); | |
48 | printf(" -p use passthrough mode\n"); | |
49 | #else | |
50 | ""); | |
51 | #endif | |
52 | vcard_emul_usage(); | |
53 | } | |
54 | ||
c9495ee9 MAL |
55 | static GIOChannel *channel_socket; |
56 | static GByteArray *socket_to_send; | |
2a0c46da | 57 | static CompatGMutex socket_to_send_lock; |
c9495ee9 MAL |
58 | static guint socket_tag; |
59 | ||
60 | static void | |
fa88afa5 | 61 | update_socket_watch(void); |
c9495ee9 MAL |
62 | |
63 | static gboolean | |
64 | do_socket_send(GIOChannel *source, | |
65 | GIOCondition condition, | |
66 | gpointer data) | |
67 | { | |
68 | gsize bw; | |
69 | GError *err = NULL; | |
70 | ||
71 | g_return_val_if_fail(socket_to_send->len != 0, FALSE); | |
72 | g_return_val_if_fail(condition & G_IO_OUT, FALSE); | |
73 | ||
74 | g_io_channel_write_chars(channel_socket, | |
75 | (gchar *)socket_to_send->data, socket_to_send->len, &bw, &err); | |
76 | if (err != NULL) { | |
77 | g_error("Error while sending socket %s", err->message); | |
78 | return FALSE; | |
79 | } | |
80 | g_byte_array_remove_range(socket_to_send, 0, bw); | |
81 | ||
82 | if (socket_to_send->len == 0) { | |
fa88afa5 | 83 | update_socket_watch(); |
c9495ee9 MAL |
84 | return FALSE; |
85 | } | |
86 | return TRUE; | |
87 | } | |
88 | ||
89 | static gboolean | |
90 | socket_prepare_sending(gpointer user_data) | |
91 | { | |
fa88afa5 | 92 | update_socket_watch(); |
c9495ee9 MAL |
93 | |
94 | return FALSE; | |
95 | } | |
2ac85b93 RR |
96 | |
97 | static int | |
98 | send_msg( | |
99 | VSCMsgType type, | |
100 | uint32_t reader_id, | |
101 | const void *msg, | |
102 | unsigned int length | |
103 | ) { | |
2ac85b93 RR |
104 | VSCMsgHeader mhHeader; |
105 | ||
2a0c46da | 106 | g_mutex_lock(&socket_to_send_lock); |
2ac85b93 RR |
107 | |
108 | if (verbose > 10) { | |
ba79c886 | 109 | printf("sending type=%d id=%u, len =%u (0x%x)\n", |
2ac85b93 RR |
110 | type, reader_id, length, length); |
111 | } | |
112 | ||
113 | mhHeader.type = htonl(type); | |
114 | mhHeader.reader_id = 0; | |
115 | mhHeader.length = htonl(length); | |
c9495ee9 MAL |
116 | g_byte_array_append(socket_to_send, (guint8 *)&mhHeader, sizeof(mhHeader)); |
117 | g_byte_array_append(socket_to_send, (guint8 *)msg, length); | |
118 | g_idle_add(socket_prepare_sending, NULL); | |
119 | ||
2a0c46da | 120 | g_mutex_unlock(&socket_to_send_lock); |
2ac85b93 RR |
121 | |
122 | return 0; | |
123 | } | |
124 | ||
125 | static VReader *pending_reader; | |
2a0c46da MT |
126 | static CompatGMutex pending_reader_lock; |
127 | static CompatGCond pending_reader_condition; | |
2ac85b93 RR |
128 | |
129 | #define MAX_ATR_LEN 40 | |
2a0c46da MT |
130 | static gpointer |
131 | event_thread(gpointer arg) | |
2ac85b93 RR |
132 | { |
133 | unsigned char atr[MAX_ATR_LEN]; | |
1687a089 MT |
134 | int atr_len; |
135 | VEvent *event; | |
2ac85b93 RR |
136 | unsigned int reader_id; |
137 | ||
138 | ||
139 | while (1) { | |
140 | const char *reader_name; | |
141 | ||
142 | event = vevent_wait_next_vevent(); | |
143 | if (event == NULL) { | |
144 | break; | |
145 | } | |
146 | reader_id = vreader_get_id(event->reader); | |
147 | if (reader_id == VSCARD_UNDEFINED_READER_ID && | |
148 | event->type != VEVENT_READER_INSERT) { | |
149 | /* ignore events from readers qemu has rejected */ | |
150 | /* if qemu is still deciding on this reader, wait to see if need to | |
151 | * forward this event */ | |
2a0c46da | 152 | g_mutex_lock(&pending_reader_lock); |
2ac85b93 RR |
153 | if (!pending_reader || (pending_reader != event->reader)) { |
154 | /* wasn't for a pending reader, this reader has already been | |
155 | * rejected by qemu */ | |
2a0c46da | 156 | g_mutex_unlock(&pending_reader_lock); |
2ac85b93 RR |
157 | vevent_delete(event); |
158 | continue; | |
159 | } | |
93148aa5 | 160 | /* this reader hasn't been told its status from qemu yet, wait for |
2ac85b93 RR |
161 | * that status */ |
162 | while (pending_reader != NULL) { | |
2a0c46da | 163 | g_cond_wait(&pending_reader_condition, &pending_reader_lock); |
2ac85b93 | 164 | } |
2a0c46da | 165 | g_mutex_unlock(&pending_reader_lock); |
2ac85b93 RR |
166 | /* now recheck the id */ |
167 | reader_id = vreader_get_id(event->reader); | |
168 | if (reader_id == VSCARD_UNDEFINED_READER_ID) { | |
169 | /* this reader was rejected */ | |
170 | vevent_delete(event); | |
171 | continue; | |
172 | } | |
173 | /* reader was accepted, now forward the event */ | |
174 | } | |
175 | switch (event->type) { | |
176 | case VEVENT_READER_INSERT: | |
177 | /* tell qemu to insert a new CCID reader */ | |
178 | /* wait until qemu has responded to our first reader insert | |
179 | * before we send a second. That way we won't confuse the responses | |
180 | * */ | |
2a0c46da | 181 | g_mutex_lock(&pending_reader_lock); |
2ac85b93 | 182 | while (pending_reader != NULL) { |
2a0c46da | 183 | g_cond_wait(&pending_reader_condition, &pending_reader_lock); |
2ac85b93 RR |
184 | } |
185 | pending_reader = vreader_reference(event->reader); | |
2a0c46da | 186 | g_mutex_unlock(&pending_reader_lock); |
2ac85b93 RR |
187 | reader_name = vreader_get_name(event->reader); |
188 | if (verbose > 10) { | |
189 | printf(" READER INSERT: %s\n", reader_name); | |
190 | } | |
191 | send_msg(VSC_ReaderAdd, | |
192 | reader_id, /* currerntly VSCARD_UNDEFINED_READER_ID */ | |
193 | NULL, 0 /* TODO reader_name, strlen(reader_name) */); | |
194 | break; | |
195 | case VEVENT_READER_REMOVE: | |
196 | /* future, tell qemu that an old CCID reader has been removed */ | |
197 | if (verbose > 10) { | |
ba79c886 | 198 | printf(" READER REMOVE: %u\n", reader_id); |
2ac85b93 RR |
199 | } |
200 | send_msg(VSC_ReaderRemove, reader_id, NULL, 0); | |
201 | break; | |
202 | case VEVENT_CARD_INSERT: | |
203 | /* get the ATR (intended as a response to a power on from the | |
204 | * reader */ | |
205 | atr_len = MAX_ATR_LEN; | |
206 | vreader_power_on(event->reader, atr, &atr_len); | |
207 | /* ATR call functions as a Card Insert event */ | |
208 | if (verbose > 10) { | |
ba79c886 | 209 | printf(" CARD INSERT %u: ", reader_id); |
2ac85b93 RR |
210 | print_byte_array(atr, atr_len); |
211 | } | |
212 | send_msg(VSC_ATR, reader_id, atr, atr_len); | |
213 | break; | |
214 | case VEVENT_CARD_REMOVE: | |
215 | /* Card removed */ | |
216 | if (verbose > 10) { | |
ba79c886 | 217 | printf(" CARD REMOVE %u:\n", reader_id); |
2ac85b93 RR |
218 | } |
219 | send_msg(VSC_CardRemove, reader_id, NULL, 0); | |
220 | break; | |
221 | default: | |
222 | break; | |
223 | } | |
224 | vevent_delete(event); | |
225 | } | |
226 | return NULL; | |
227 | } | |
228 | ||
229 | ||
230 | static unsigned int | |
231 | get_id_from_string(char *string, unsigned int default_id) | |
232 | { | |
233 | unsigned int id = atoi(string); | |
234 | ||
235 | /* don't accidentally swith to zero because no numbers have been supplied */ | |
236 | if ((id == 0) && *string != '0') { | |
237 | return default_id; | |
238 | } | |
239 | return id; | |
240 | } | |
241 | ||
a50b831a MAL |
242 | static int |
243 | on_host_init(VSCMsgHeader *mhHeader, VSCMsgInit *incoming) | |
244 | { | |
245 | uint32_t *capabilities = (incoming->capabilities); | |
246 | int num_capabilities = | |
247 | 1 + ((mhHeader->length - sizeof(VSCMsgInit)) / sizeof(uint32_t)); | |
248 | int i; | |
a50b831a MAL |
249 | |
250 | incoming->version = ntohl(incoming->version); | |
251 | if (incoming->version != VSCARD_VERSION) { | |
252 | if (verbose > 0) { | |
253 | printf("warning: host has version %d, we have %d\n", | |
254 | verbose, VSCARD_VERSION); | |
255 | } | |
256 | } | |
257 | if (incoming->magic != VSCARD_MAGIC) { | |
258 | printf("unexpected magic: got %d, expected %d\n", | |
259 | incoming->magic, VSCARD_MAGIC); | |
260 | return -1; | |
261 | } | |
262 | for (i = 0 ; i < num_capabilities; ++i) { | |
263 | capabilities[i] = ntohl(capabilities[i]); | |
264 | } | |
265 | /* Future: check capabilities */ | |
266 | /* remove whatever reader might be left in qemu, | |
267 | * in case of an unclean previous exit. */ | |
268 | send_msg(VSC_ReaderRemove, VSCARD_MINIMAL_READER_ID, NULL, 0); | |
269 | /* launch the event_thread. This will trigger reader adds for all the | |
270 | * existing readers */ | |
2a0c46da | 271 | g_thread_new("vsc/event", event_thread, NULL); |
a50b831a MAL |
272 | return 0; |
273 | } | |
274 | ||
c9495ee9 MAL |
275 | |
276 | enum { | |
277 | STATE_HEADER, | |
278 | STATE_MESSAGE, | |
279 | }; | |
280 | ||
a50b831a MAL |
281 | #define APDUBufSize 270 |
282 | ||
c9495ee9 MAL |
283 | static gboolean |
284 | do_socket_read(GIOChannel *source, | |
285 | GIOCondition condition, | |
286 | gpointer data) | |
a50b831a MAL |
287 | { |
288 | int rv; | |
289 | int dwSendLength; | |
290 | int dwRecvLength; | |
291 | uint8_t pbRecvBuffer[APDUBufSize]; | |
c9495ee9 | 292 | static uint8_t pbSendBuffer[APDUBufSize]; |
a50b831a MAL |
293 | VReaderStatus reader_status; |
294 | VReader *reader = NULL; | |
c9495ee9 | 295 | static VSCMsgHeader mhHeader; |
a50b831a | 296 | VSCMsgError *error_msg; |
c9495ee9 | 297 | GError *err = NULL; |
a50b831a | 298 | |
c9495ee9 MAL |
299 | static gchar *buf; |
300 | static gsize br, to_read; | |
301 | static int state = STATE_HEADER; | |
302 | ||
303 | if (state == STATE_HEADER && to_read == 0) { | |
304 | buf = (gchar *)&mhHeader; | |
305 | to_read = sizeof(mhHeader); | |
a50b831a | 306 | } |
c9495ee9 MAL |
307 | |
308 | if (to_read > 0) { | |
309 | g_io_channel_read_chars(source, (gchar *)buf, to_read, &br, &err); | |
310 | if (err != NULL) { | |
311 | g_error("error while reading: %s", err->message); | |
a50b831a | 312 | } |
c9495ee9 MAL |
313 | buf += br; |
314 | to_read -= br; | |
315 | if (to_read != 0) { | |
316 | return TRUE; | |
317 | } | |
318 | } | |
319 | ||
320 | if (state == STATE_HEADER) { | |
321 | mhHeader.type = ntohl(mhHeader.type); | |
322 | mhHeader.reader_id = ntohl(mhHeader.reader_id); | |
323 | mhHeader.length = ntohl(mhHeader.length); | |
a50b831a | 324 | if (verbose) { |
c9495ee9 MAL |
325 | printf("Header: type=%d, reader_id=%u length=%d (0x%x)\n", |
326 | mhHeader.type, mhHeader.reader_id, mhHeader.length, | |
327 | mhHeader.length); | |
a50b831a | 328 | } |
c9495ee9 MAL |
329 | switch (mhHeader.type) { |
330 | case VSC_APDU: | |
331 | case VSC_Flush: | |
332 | case VSC_Error: | |
333 | case VSC_Init: | |
334 | buf = (gchar *)pbSendBuffer; | |
335 | to_read = mhHeader.length; | |
336 | state = STATE_MESSAGE; | |
337 | return TRUE; | |
338 | default: | |
339 | fprintf(stderr, "Unexpected message of type 0x%X\n", mhHeader.type); | |
340 | return FALSE; | |
341 | } | |
342 | } | |
343 | ||
344 | if (state == STATE_MESSAGE) { | |
345 | switch (mhHeader.type) { | |
346 | case VSC_APDU: | |
a50b831a | 347 | if (verbose) { |
c9495ee9 MAL |
348 | printf(" recv APDU: "); |
349 | print_byte_array(pbSendBuffer, mhHeader.length); | |
a50b831a | 350 | } |
c9495ee9 MAL |
351 | /* Transmit received APDU */ |
352 | dwSendLength = mhHeader.length; | |
353 | dwRecvLength = sizeof(pbRecvBuffer); | |
354 | reader = vreader_get_reader_by_id(mhHeader.reader_id); | |
355 | reader_status = vreader_xfr_bytes(reader, | |
356 | pbSendBuffer, dwSendLength, | |
357 | pbRecvBuffer, &dwRecvLength); | |
358 | if (reader_status == VREADER_OK) { | |
359 | mhHeader.length = dwRecvLength; | |
360 | if (verbose) { | |
361 | printf(" send response: "); | |
362 | print_byte_array(pbRecvBuffer, mhHeader.length); | |
363 | } | |
364 | send_msg(VSC_APDU, mhHeader.reader_id, | |
365 | pbRecvBuffer, dwRecvLength); | |
366 | } else { | |
367 | rv = reader_status; /* warning: not meaningful */ | |
368 | send_msg(VSC_Error, mhHeader.reader_id, &rv, sizeof(uint32_t)); | |
a50b831a | 369 | } |
c9495ee9 MAL |
370 | vreader_free(reader); |
371 | reader = NULL; /* we've freed it, don't use it by accident | |
372 | again */ | |
a50b831a | 373 | break; |
c9495ee9 MAL |
374 | case VSC_Flush: |
375 | /* TODO: actually flush */ | |
376 | send_msg(VSC_FlushComplete, mhHeader.reader_id, NULL, 0); | |
377 | break; | |
378 | case VSC_Error: | |
379 | error_msg = (VSCMsgError *) pbSendBuffer; | |
380 | if (error_msg->code == VSC_SUCCESS) { | |
2a0c46da | 381 | g_mutex_lock(&pending_reader_lock); |
c9495ee9 MAL |
382 | if (pending_reader) { |
383 | vreader_set_id(pending_reader, mhHeader.reader_id); | |
384 | vreader_free(pending_reader); | |
385 | pending_reader = NULL; | |
2a0c46da | 386 | g_cond_signal(&pending_reader_condition); |
c9495ee9 | 387 | } |
2a0c46da | 388 | g_mutex_unlock(&pending_reader_lock); |
c9495ee9 | 389 | break; |
a50b831a | 390 | } |
c9495ee9 MAL |
391 | printf("warning: qemu refused to add reader\n"); |
392 | if (error_msg->code == VSC_CANNOT_ADD_MORE_READERS) { | |
393 | /* clear pending reader, qemu can't handle any more */ | |
2a0c46da | 394 | g_mutex_lock(&pending_reader_lock); |
c9495ee9 MAL |
395 | if (pending_reader) { |
396 | pending_reader = NULL; | |
397 | /* make sure the event loop doesn't hang */ | |
2a0c46da | 398 | g_cond_signal(&pending_reader_condition); |
c9495ee9 | 399 | } |
2a0c46da | 400 | g_mutex_unlock(&pending_reader_lock); |
c9495ee9 MAL |
401 | } |
402 | break; | |
403 | case VSC_Init: | |
404 | if (on_host_init(&mhHeader, (VSCMsgInit *)pbSendBuffer) < 0) { | |
405 | return FALSE; | |
406 | } | |
407 | break; | |
408 | default: | |
5ad04fb6 | 409 | g_assert_not_reached(); |
c9495ee9 | 410 | return FALSE; |
a50b831a | 411 | } |
c9495ee9 MAL |
412 | |
413 | state = STATE_HEADER; | |
414 | } | |
415 | ||
416 | ||
417 | return TRUE; | |
418 | } | |
419 | ||
420 | static gboolean | |
421 | do_socket(GIOChannel *source, | |
422 | GIOCondition condition, | |
423 | gpointer data) | |
424 | { | |
425 | /* not sure if two watches work well with a single win32 sources */ | |
426 | if (condition & G_IO_OUT) { | |
427 | if (!do_socket_send(source, condition, data)) { | |
428 | return FALSE; | |
a50b831a | 429 | } |
a50b831a MAL |
430 | } |
431 | ||
c9495ee9 MAL |
432 | if (condition & G_IO_IN) { |
433 | if (!do_socket_read(source, condition, data)) { | |
434 | return FALSE; | |
435 | } | |
436 | } | |
437 | ||
438 | return TRUE; | |
a50b831a MAL |
439 | } |
440 | ||
2ac85b93 | 441 | static void |
fa88afa5 | 442 | update_socket_watch(void) |
c9495ee9 | 443 | { |
fa88afa5 MAL |
444 | gboolean out = socket_to_send->len > 0; |
445 | ||
c9495ee9 MAL |
446 | if (socket_tag != 0) { |
447 | g_source_remove(socket_tag); | |
448 | } | |
449 | ||
450 | socket_tag = g_io_add_watch(channel_socket, | |
451 | G_IO_IN | (out ? G_IO_OUT : 0), do_socket, NULL); | |
452 | } | |
453 | ||
454 | static gboolean | |
455 | do_command(GIOChannel *source, | |
456 | GIOCondition condition, | |
457 | gpointer data) | |
2ac85b93 | 458 | { |
2ac85b93 RR |
459 | char *string; |
460 | VCardEmulError error; | |
461 | static unsigned int default_reader_id; | |
462 | unsigned int reader_id; | |
463 | VReader *reader = NULL; | |
c9495ee9 MAL |
464 | GError *err = NULL; |
465 | ||
466 | g_assert(condition & G_IO_IN); | |
2ac85b93 RR |
467 | |
468 | reader_id = default_reader_id; | |
c9495ee9 MAL |
469 | g_io_channel_read_line(source, &string, NULL, NULL, &err); |
470 | if (err != NULL) { | |
471 | g_error("Error while reading command: %s", err->message); | |
472 | } | |
473 | ||
2ac85b93 RR |
474 | if (string != NULL) { |
475 | if (strncmp(string, "exit", 4) == 0) { | |
476 | /* remove all the readers */ | |
477 | VReaderList *list = vreader_get_reader_list(); | |
478 | VReaderListEntry *reader_entry; | |
479 | printf("Active Readers:\n"); | |
480 | for (reader_entry = vreader_list_get_first(list); reader_entry; | |
481 | reader_entry = vreader_list_get_next(reader_entry)) { | |
482 | VReader *reader = vreader_list_get_reader(reader_entry); | |
483 | vreader_id_t reader_id; | |
484 | reader_id = vreader_get_id(reader); | |
485 | if (reader_id == -1) { | |
486 | continue; | |
487 | } | |
488 | /* be nice and signal card removal first (qemu probably should | |
489 | * do this itself) */ | |
490 | if (vreader_card_is_present(reader) == VREADER_OK) { | |
491 | send_msg(VSC_CardRemove, reader_id, NULL, 0); | |
492 | } | |
493 | send_msg(VSC_ReaderRemove, reader_id, NULL, 0); | |
494 | } | |
495 | exit(0); | |
496 | } else if (strncmp(string, "insert", 6) == 0) { | |
497 | if (string[6] == ' ') { | |
498 | reader_id = get_id_from_string(&string[7], reader_id); | |
499 | } | |
500 | reader = vreader_get_reader_by_id(reader_id); | |
501 | if (reader != NULL) { | |
502 | error = vcard_emul_force_card_insert(reader); | |
503 | printf("insert %s, returned %d\n", | |
d357e3d9 | 504 | vreader_get_name(reader), error); |
2ac85b93 | 505 | } else { |
ba79c886 | 506 | printf("no reader by id %u found\n", reader_id); |
2ac85b93 RR |
507 | } |
508 | } else if (strncmp(string, "remove", 6) == 0) { | |
509 | if (string[6] == ' ') { | |
510 | reader_id = get_id_from_string(&string[7], reader_id); | |
511 | } | |
512 | reader = vreader_get_reader_by_id(reader_id); | |
513 | if (reader != NULL) { | |
514 | error = vcard_emul_force_card_remove(reader); | |
515 | printf("remove %s, returned %d\n", | |
d357e3d9 | 516 | vreader_get_name(reader), error); |
2ac85b93 | 517 | } else { |
ba79c886 | 518 | printf("no reader by id %u found\n", reader_id); |
2ac85b93 RR |
519 | } |
520 | } else if (strncmp(string, "select", 6) == 0) { | |
521 | if (string[6] == ' ') { | |
522 | reader_id = get_id_from_string(&string[7], | |
523 | VSCARD_UNDEFINED_READER_ID); | |
524 | } | |
525 | if (reader_id != VSCARD_UNDEFINED_READER_ID) { | |
526 | reader = vreader_get_reader_by_id(reader_id); | |
527 | } | |
528 | if (reader) { | |
ba79c886 | 529 | printf("Selecting reader %u, %s\n", reader_id, |
2ac85b93 RR |
530 | vreader_get_name(reader)); |
531 | default_reader_id = reader_id; | |
532 | } else { | |
ba79c886 | 533 | printf("Reader with id %u not found\n", reader_id); |
2ac85b93 RR |
534 | } |
535 | } else if (strncmp(string, "debug", 5) == 0) { | |
536 | if (string[5] == ' ') { | |
537 | verbose = get_id_from_string(&string[6], 0); | |
538 | } | |
539 | printf("debug level = %d\n", verbose); | |
540 | } else if (strncmp(string, "list", 4) == 0) { | |
541 | VReaderList *list = vreader_get_reader_list(); | |
542 | VReaderListEntry *reader_entry; | |
543 | printf("Active Readers:\n"); | |
544 | for (reader_entry = vreader_list_get_first(list); reader_entry; | |
545 | reader_entry = vreader_list_get_next(reader_entry)) { | |
546 | VReader *reader = vreader_list_get_reader(reader_entry); | |
547 | vreader_id_t reader_id; | |
548 | reader_id = vreader_get_id(reader); | |
549 | if (reader_id == -1) { | |
550 | continue; | |
551 | } | |
ba79c886 | 552 | printf("%3u %s %s\n", reader_id, |
2ac85b93 RR |
553 | vreader_card_is_present(reader) == VREADER_OK ? |
554 | "CARD_PRESENT" : " ", | |
555 | vreader_get_name(reader)); | |
556 | } | |
557 | printf("Inactive Readers:\n"); | |
558 | for (reader_entry = vreader_list_get_first(list); reader_entry; | |
559 | reader_entry = vreader_list_get_next(reader_entry)) { | |
560 | VReader *reader = vreader_list_get_reader(reader_entry); | |
561 | vreader_id_t reader_id; | |
562 | reader_id = vreader_get_id(reader); | |
563 | if (reader_id != -1) { | |
564 | continue; | |
565 | } | |
566 | ||
567 | printf("INA %s %s\n", | |
568 | vreader_card_is_present(reader) == VREADER_OK ? | |
569 | "CARD_PRESENT" : " ", | |
570 | vreader_get_name(reader)); | |
571 | } | |
124fe7fb | 572 | vreader_list_delete(list); |
2ac85b93 RR |
573 | } else if (*string != 0) { |
574 | printf("valid commands:\n"); | |
575 | printf("insert [reader_id]\n"); | |
576 | printf("remove [reader_id]\n"); | |
577 | printf("select reader_id\n"); | |
578 | printf("list\n"); | |
579 | printf("debug [level]\n"); | |
580 | printf("exit\n"); | |
581 | } | |
582 | } | |
583 | vreader_free(reader); | |
584 | printf("> "); | |
585 | fflush(stdout); | |
c9495ee9 MAL |
586 | |
587 | return TRUE; | |
2ac85b93 RR |
588 | } |
589 | ||
590 | ||
2ac85b93 RR |
591 | /* just for ease of parsing command line arguments. */ |
592 | #define MAX_CERTS 100 | |
593 | ||
594 | static int | |
595 | connect_to_qemu( | |
596 | const char *host, | |
597 | const char *port | |
598 | ) { | |
599 | struct addrinfo hints; | |
5bbebf62 | 600 | struct addrinfo *server = NULL; |
c9495ee9 | 601 | int ret, sock; |
2ac85b93 | 602 | |
2a0c46da | 603 | sock = socket(AF_INET, SOCK_STREAM, 0); |
2ac85b93 RR |
604 | if (sock < 0) { |
605 | /* Error */ | |
606 | fprintf(stderr, "Error opening socket!\n"); | |
e7c5e893 | 607 | return -1; |
2ac85b93 RR |
608 | } |
609 | ||
610 | memset(&hints, 0, sizeof(struct addrinfo)); | |
611 | hints.ai_family = AF_UNSPEC; | |
612 | hints.ai_socktype = SOCK_STREAM; | |
613 | hints.ai_flags = 0; | |
614 | hints.ai_protocol = 0; /* Any protocol */ | |
615 | ||
616 | ret = getaddrinfo(host, port, &hints, &server); | |
617 | ||
618 | if (ret != 0) { | |
619 | /* Error */ | |
620 | fprintf(stderr, "getaddrinfo failed\n"); | |
581fe784 | 621 | goto cleanup_socket; |
2ac85b93 RR |
622 | } |
623 | ||
624 | if (connect(sock, server->ai_addr, server->ai_addrlen) < 0) { | |
625 | /* Error */ | |
626 | fprintf(stderr, "Could not connect\n"); | |
581fe784 | 627 | goto cleanup_socket; |
2ac85b93 RR |
628 | } |
629 | if (verbose) { | |
630 | printf("Connected (sizeof Header=%zd)!\n", sizeof(VSCMsgHeader)); | |
631 | } | |
5bbebf62 HZ |
632 | |
633 | freeaddrinfo(server); | |
2ac85b93 | 634 | return sock; |
581fe784 AL |
635 | |
636 | cleanup_socket: | |
5bbebf62 HZ |
637 | if (server) { |
638 | freeaddrinfo(server); | |
639 | } | |
581fe784 AL |
640 | closesocket(sock); |
641 | return -1; | |
2ac85b93 RR |
642 | } |
643 | ||
2ac85b93 RR |
644 | int |
645 | main( | |
646 | int argc, | |
647 | char *argv[] | |
648 | ) { | |
c9495ee9 MAL |
649 | GMainLoop *loop; |
650 | GIOChannel *channel_stdin; | |
2ac85b93 RR |
651 | char *qemu_host; |
652 | char *qemu_port; | |
2ac85b93 | 653 | |
2ac85b93 RR |
654 | VCardEmulOptions *command_line_options = NULL; |
655 | ||
656 | char *cert_names[MAX_CERTS]; | |
657 | char *emul_args = NULL; | |
658 | int cert_count = 0; | |
c9495ee9 MAL |
659 | int c, sock; |
660 | ||
2a0c46da MT |
661 | #ifdef _WIN32 |
662 | WSADATA Data; | |
663 | ||
664 | if (WSAStartup(MAKEWORD(2, 2), &Data) != 0) { | |
665 | c = WSAGetLastError(); | |
666 | fprintf(stderr, "WSAStartup: %d\n", c); | |
c9495ee9 | 667 | return 1; |
2a0c46da MT |
668 | } |
669 | #endif | |
670 | #if !GLIB_CHECK_VERSION(2, 31, 0) | |
671 | if (!g_thread_supported()) { | |
672 | g_thread_init(NULL); | |
673 | } | |
674 | #endif | |
2ac85b93 RR |
675 | |
676 | while ((c = getopt(argc, argv, "c:e:pd:")) != -1) { | |
677 | switch (c) { | |
678 | case 'c': | |
679 | if (cert_count >= MAX_CERTS) { | |
680 | printf("too many certificates (max = %d)\n", MAX_CERTS); | |
681 | exit(5); | |
682 | } | |
683 | cert_names[cert_count++] = optarg; | |
684 | break; | |
685 | case 'e': | |
686 | emul_args = optarg; | |
687 | break; | |
688 | case 'p': | |
689 | print_usage(); | |
690 | exit(4); | |
691 | break; | |
692 | case 'd': | |
693 | verbose = get_id_from_string(optarg, 1); | |
694 | break; | |
695 | } | |
696 | } | |
697 | ||
698 | if (argc - optind != 2) { | |
699 | print_usage(); | |
700 | exit(4); | |
701 | } | |
702 | ||
703 | if (cert_count > 0) { | |
704 | char *new_args; | |
705 | int len, i; | |
706 | /* if we've given some -c options, we clearly we want do so some | |
707 | * software emulation. add that emulation now. this is NSS Emulator | |
708 | * specific */ | |
709 | if (emul_args == NULL) { | |
710 | emul_args = (char *)"db=\"/etc/pki/nssdb\""; | |
711 | } | |
712 | #define SOFT_STRING ",soft=(,Virtual Reader,CAC,," | |
713 | /* 2 == close paren & null */ | |
714 | len = strlen(emul_args) + strlen(SOFT_STRING) + 2; | |
715 | for (i = 0; i < cert_count; i++) { | |
716 | len += strlen(cert_names[i])+1; /* 1 == comma */ | |
717 | } | |
7267c094 | 718 | new_args = g_malloc(len); |
2ac85b93 RR |
719 | strcpy(new_args, emul_args); |
720 | strcat(new_args, SOFT_STRING); | |
721 | for (i = 0; i < cert_count; i++) { | |
722 | strcat(new_args, cert_names[i]); | |
723 | strcat(new_args, ","); | |
724 | } | |
725 | strcat(new_args, ")"); | |
726 | emul_args = new_args; | |
727 | } | |
728 | if (emul_args) { | |
729 | command_line_options = vcard_emul_options(emul_args); | |
730 | } | |
731 | ||
be168af8 MA |
732 | qemu_host = g_strdup(argv[argc - 2]); |
733 | qemu_port = g_strdup(argv[argc - 1]); | |
2ac85b93 | 734 | sock = connect_to_qemu(qemu_host, qemu_port); |
e7c5e893 AL |
735 | if (sock == -1) { |
736 | fprintf(stderr, "error opening socket, exiting.\n"); | |
737 | exit(5); | |
738 | } | |
2ac85b93 | 739 | |
c9495ee9 | 740 | socket_to_send = g_byte_array_new(); |
2ac85b93 | 741 | vcard_emul_init(command_line_options); |
2a0c46da | 742 | loop = g_main_loop_new(NULL, TRUE); |
c9495ee9 | 743 | |
2ac85b93 RR |
744 | printf("> "); |
745 | fflush(stdout); | |
746 | ||
c9495ee9 MAL |
747 | #ifdef _WIN32 |
748 | channel_stdin = g_io_channel_win32_new_fd(STDIN_FILENO); | |
749 | #else | |
750 | channel_stdin = g_io_channel_unix_new(STDIN_FILENO); | |
751 | #endif | |
752 | g_io_add_watch(channel_stdin, G_IO_IN, do_command, NULL); | |
753 | #ifdef _WIN32 | |
754 | channel_socket = g_io_channel_win32_new_socket(sock); | |
755 | #else | |
756 | channel_socket = g_io_channel_unix_new(sock); | |
757 | #endif | |
758 | g_io_channel_set_encoding(channel_socket, NULL, NULL); | |
759 | /* we buffer ourself for thread safety reasons */ | |
760 | g_io_channel_set_buffered(channel_socket, FALSE); | |
761 | ||
2ac85b93 RR |
762 | /* Send init message, Host responds (and then we send reader attachments) */ |
763 | VSCMsgInit init = { | |
764 | .version = htonl(VSCARD_VERSION), | |
765 | .magic = VSCARD_MAGIC, | |
766 | .capabilities = {0} | |
767 | }; | |
69fded48 | 768 | send_msg(VSC_Init, 0, &init, sizeof(init)); |
2ac85b93 | 769 | |
c9495ee9 MAL |
770 | g_main_loop_run(loop); |
771 | g_main_loop_unref(loop); | |
2ac85b93 | 772 | |
c9495ee9 MAL |
773 | g_io_channel_unref(channel_stdin); |
774 | g_io_channel_unref(channel_socket); | |
5ad04fb6 | 775 | g_byte_array_free(socket_to_send, TRUE); |
2ac85b93 | 776 | |
581fe784 | 777 | closesocket(sock); |
2ac85b93 RR |
778 | return 0; |
779 | } |