|  | /* | 
|  | * Tester for VSCARD protocol, client side. | 
|  | * | 
|  | * Can be used with ccid-card-passthru. | 
|  | * | 
|  | * Copyright (c) 2011 Red Hat. | 
|  | * Written by Alon Levy. | 
|  | * | 
|  | * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. | 
|  | * See the COPYING.LIB file in the top-level directory. | 
|  | */ | 
|  |  | 
|  | #include <netdb.h> | 
|  |  | 
|  | #include "qemu-common.h" | 
|  | #include "qemu-thread.h" | 
|  | #include "qemu_socket.h" | 
|  |  | 
|  | #include "vscard_common.h" | 
|  |  | 
|  | #include "vreader.h" | 
|  | #include "vcard_emul.h" | 
|  | #include "vevent.h" | 
|  |  | 
|  | int verbose; | 
|  |  | 
|  | int sock; | 
|  |  | 
|  | static void | 
|  | print_byte_array( | 
|  | uint8_t *arrBytes, | 
|  | unsigned int nSize | 
|  | ) { | 
|  | int i; | 
|  | for (i = 0; i < nSize; i++) { | 
|  | printf("%02X ", arrBytes[i]); | 
|  | } | 
|  | printf("\n"); | 
|  | } | 
|  |  | 
|  | static void | 
|  | print_usage(void) { | 
|  | printf("vscclient [-c <certname> .. -e <emul_args> -d <level>%s] " | 
|  | "<host> <port>\n", | 
|  | #ifdef USE_PASSTHRU | 
|  | " -p"); | 
|  | printf(" -p use passthrough mode\n"); | 
|  | #else | 
|  | ""); | 
|  | #endif | 
|  | vcard_emul_usage(); | 
|  | } | 
|  |  | 
|  | static QemuMutex write_lock; | 
|  |  | 
|  | static int | 
|  | send_msg( | 
|  | VSCMsgType type, | 
|  | uint32_t reader_id, | 
|  | const void *msg, | 
|  | unsigned int length | 
|  | ) { | 
|  | int rv; | 
|  | VSCMsgHeader mhHeader; | 
|  |  | 
|  | qemu_mutex_lock(&write_lock); | 
|  |  | 
|  | if (verbose > 10) { | 
|  | printf("sending type=%d id=%u, len =%u (0x%x)\n", | 
|  | type, reader_id, length, length); | 
|  | } | 
|  |  | 
|  | mhHeader.type = htonl(type); | 
|  | mhHeader.reader_id = 0; | 
|  | mhHeader.length = htonl(length); | 
|  | rv = write(sock, &mhHeader, sizeof(mhHeader)); | 
|  | if (rv < 0) { | 
|  | /* Error */ | 
|  | fprintf(stderr, "write header error\n"); | 
|  | close(sock); | 
|  | qemu_mutex_unlock(&write_lock); | 
|  | return 16; | 
|  | } | 
|  | rv = write(sock, msg, length); | 
|  | if (rv < 0) { | 
|  | /* Error */ | 
|  | fprintf(stderr, "write error\n"); | 
|  | close(sock); | 
|  | qemu_mutex_unlock(&write_lock); | 
|  | return 16; | 
|  | } | 
|  | qemu_mutex_unlock(&write_lock); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static VReader *pending_reader; | 
|  | static QemuMutex pending_reader_lock; | 
|  | static QemuCond pending_reader_condition; | 
|  |  | 
|  | #define MAX_ATR_LEN 40 | 
|  | static void * | 
|  | event_thread(void *arg) | 
|  | { | 
|  | unsigned char atr[MAX_ATR_LEN]; | 
|  | int atr_len = MAX_ATR_LEN; | 
|  | VEvent *event = NULL; | 
|  | unsigned int reader_id; | 
|  |  | 
|  |  | 
|  | while (1) { | 
|  | const char *reader_name; | 
|  |  | 
|  | event = vevent_wait_next_vevent(); | 
|  | if (event == NULL) { | 
|  | break; | 
|  | } | 
|  | reader_id = vreader_get_id(event->reader); | 
|  | if (reader_id == VSCARD_UNDEFINED_READER_ID && | 
|  | event->type != VEVENT_READER_INSERT) { | 
|  | /* ignore events from readers qemu has rejected */ | 
|  | /* if qemu is still deciding on this reader, wait to see if need to | 
|  | * forward this event */ | 
|  | qemu_mutex_lock(&pending_reader_lock); | 
|  | if (!pending_reader || (pending_reader != event->reader)) { | 
|  | /* wasn't for a pending reader, this reader has already been | 
|  | * rejected by qemu */ | 
|  | qemu_mutex_unlock(&pending_reader_lock); | 
|  | vevent_delete(event); | 
|  | continue; | 
|  | } | 
|  | /* this reader hasn't been told its status from qemu yet, wait for | 
|  | * that status */ | 
|  | while (pending_reader != NULL) { | 
|  | qemu_cond_wait(&pending_reader_condition, &pending_reader_lock); | 
|  | } | 
|  | qemu_mutex_unlock(&pending_reader_lock); | 
|  | /* now recheck the id */ | 
|  | reader_id = vreader_get_id(event->reader); | 
|  | if (reader_id == VSCARD_UNDEFINED_READER_ID) { | 
|  | /* this reader was rejected */ | 
|  | vevent_delete(event); | 
|  | continue; | 
|  | } | 
|  | /* reader was accepted, now forward the event */ | 
|  | } | 
|  | switch (event->type) { | 
|  | case VEVENT_READER_INSERT: | 
|  | /* tell qemu to insert a new CCID reader */ | 
|  | /* wait until qemu has responded to our first reader insert | 
|  | * before we send a second. That way we won't confuse the responses | 
|  | * */ | 
|  | qemu_mutex_lock(&pending_reader_lock); | 
|  | while (pending_reader != NULL) { | 
|  | qemu_cond_wait(&pending_reader_condition, &pending_reader_lock); | 
|  | } | 
|  | pending_reader = vreader_reference(event->reader); | 
|  | qemu_mutex_unlock(&pending_reader_lock); | 
|  | reader_name = vreader_get_name(event->reader); | 
|  | if (verbose > 10) { | 
|  | printf(" READER INSERT: %s\n", reader_name); | 
|  | } | 
|  | send_msg(VSC_ReaderAdd, | 
|  | reader_id, /* currerntly VSCARD_UNDEFINED_READER_ID */ | 
|  | NULL, 0 /* TODO reader_name, strlen(reader_name) */); | 
|  | break; | 
|  | case VEVENT_READER_REMOVE: | 
|  | /* future, tell qemu that an old CCID reader has been removed */ | 
|  | if (verbose > 10) { | 
|  | printf(" READER REMOVE: %u\n", reader_id); | 
|  | } | 
|  | send_msg(VSC_ReaderRemove, reader_id, NULL, 0); | 
|  | break; | 
|  | case VEVENT_CARD_INSERT: | 
|  | /* get the ATR (intended as a response to a power on from the | 
|  | * reader */ | 
|  | atr_len = MAX_ATR_LEN; | 
|  | vreader_power_on(event->reader, atr, &atr_len); | 
|  | /* ATR call functions as a Card Insert event */ | 
|  | if (verbose > 10) { | 
|  | printf(" CARD INSERT %u: ", reader_id); | 
|  | print_byte_array(atr, atr_len); | 
|  | } | 
|  | send_msg(VSC_ATR, reader_id, atr, atr_len); | 
|  | break; | 
|  | case VEVENT_CARD_REMOVE: | 
|  | /* Card removed */ | 
|  | if (verbose > 10) { | 
|  | printf(" CARD REMOVE %u:\n", reader_id); | 
|  | } | 
|  | send_msg(VSC_CardRemove, reader_id, NULL, 0); | 
|  | break; | 
|  | default: | 
|  | break; | 
|  | } | 
|  | vevent_delete(event); | 
|  | } | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  |  | 
|  | static unsigned int | 
|  | get_id_from_string(char *string, unsigned int default_id) | 
|  | { | 
|  | unsigned int id = atoi(string); | 
|  |  | 
|  | /* don't accidentally swith to zero because no numbers have been supplied */ | 
|  | if ((id == 0) && *string != '0') { | 
|  | return default_id; | 
|  | } | 
|  | return id; | 
|  | } | 
|  |  | 
|  | static void | 
|  | do_command(void) | 
|  | { | 
|  | char inbuf[255]; | 
|  | char *string; | 
|  | VCardEmulError error; | 
|  | static unsigned int default_reader_id; | 
|  | unsigned int reader_id; | 
|  | VReader *reader = NULL; | 
|  |  | 
|  | reader_id = default_reader_id; | 
|  | string = fgets(inbuf, sizeof(inbuf), stdin); | 
|  | if (string != NULL) { | 
|  | if (strncmp(string, "exit", 4) == 0) { | 
|  | /* remove all the readers */ | 
|  | VReaderList *list = vreader_get_reader_list(); | 
|  | VReaderListEntry *reader_entry; | 
|  | printf("Active Readers:\n"); | 
|  | for (reader_entry = vreader_list_get_first(list); reader_entry; | 
|  | reader_entry = vreader_list_get_next(reader_entry)) { | 
|  | VReader *reader = vreader_list_get_reader(reader_entry); | 
|  | vreader_id_t reader_id; | 
|  | reader_id = vreader_get_id(reader); | 
|  | if (reader_id == -1) { | 
|  | continue; | 
|  | } | 
|  | /* be nice and signal card removal first (qemu probably should | 
|  | * do this itself) */ | 
|  | if (vreader_card_is_present(reader) == VREADER_OK) { | 
|  | send_msg(VSC_CardRemove, reader_id, NULL, 0); | 
|  | } | 
|  | send_msg(VSC_ReaderRemove, reader_id, NULL, 0); | 
|  | } | 
|  | exit(0); | 
|  | } else if (strncmp(string, "insert", 6) == 0) { | 
|  | if (string[6] == ' ') { | 
|  | reader_id = get_id_from_string(&string[7], reader_id); | 
|  | } | 
|  | reader = vreader_get_reader_by_id(reader_id); | 
|  | if (reader != NULL) { | 
|  | error = vcard_emul_force_card_insert(reader); | 
|  | printf("insert %s, returned %d\n", | 
|  | reader ? vreader_get_name(reader) | 
|  | : "invalid reader", error); | 
|  | } else { | 
|  | printf("no reader by id %u found\n", reader_id); | 
|  | } | 
|  | } else if (strncmp(string, "remove", 6) == 0) { | 
|  | if (string[6] == ' ') { | 
|  | reader_id = get_id_from_string(&string[7], reader_id); | 
|  | } | 
|  | reader = vreader_get_reader_by_id(reader_id); | 
|  | if (reader != NULL) { | 
|  | error = vcard_emul_force_card_remove(reader); | 
|  | printf("remove %s, returned %d\n", | 
|  | reader ? vreader_get_name(reader) | 
|  | : "invalid reader", error); | 
|  | } else { | 
|  | printf("no reader by id %u found\n", reader_id); | 
|  | } | 
|  | } else if (strncmp(string, "select", 6) == 0) { | 
|  | if (string[6] == ' ') { | 
|  | reader_id = get_id_from_string(&string[7], | 
|  | VSCARD_UNDEFINED_READER_ID); | 
|  | } | 
|  | if (reader_id != VSCARD_UNDEFINED_READER_ID) { | 
|  | reader = vreader_get_reader_by_id(reader_id); | 
|  | } | 
|  | if (reader) { | 
|  | printf("Selecting reader %u, %s\n", reader_id, | 
|  | vreader_get_name(reader)); | 
|  | default_reader_id = reader_id; | 
|  | } else { | 
|  | printf("Reader with id %u not found\n", reader_id); | 
|  | } | 
|  | } else if (strncmp(string, "debug", 5) == 0) { | 
|  | if (string[5] == ' ') { | 
|  | verbose = get_id_from_string(&string[6], 0); | 
|  | } | 
|  | printf("debug level = %d\n", verbose); | 
|  | } else if (strncmp(string, "list", 4) == 0) { | 
|  | VReaderList *list = vreader_get_reader_list(); | 
|  | VReaderListEntry *reader_entry; | 
|  | printf("Active Readers:\n"); | 
|  | for (reader_entry = vreader_list_get_first(list); reader_entry; | 
|  | reader_entry = vreader_list_get_next(reader_entry)) { | 
|  | VReader *reader = vreader_list_get_reader(reader_entry); | 
|  | vreader_id_t reader_id; | 
|  | reader_id = vreader_get_id(reader); | 
|  | if (reader_id == -1) { | 
|  | continue; | 
|  | } | 
|  | printf("%3u %s %s\n", reader_id, | 
|  | vreader_card_is_present(reader) == VREADER_OK ? | 
|  | "CARD_PRESENT" : "            ", | 
|  | vreader_get_name(reader)); | 
|  | } | 
|  | printf("Inactive Readers:\n"); | 
|  | for (reader_entry = vreader_list_get_first(list); reader_entry; | 
|  | reader_entry = vreader_list_get_next(reader_entry)) { | 
|  | VReader *reader = vreader_list_get_reader(reader_entry); | 
|  | vreader_id_t reader_id; | 
|  | reader_id = vreader_get_id(reader); | 
|  | if (reader_id != -1) { | 
|  | continue; | 
|  | } | 
|  |  | 
|  | printf("INA %s %s\n", | 
|  | vreader_card_is_present(reader) == VREADER_OK ? | 
|  | "CARD_PRESENT" : "            ", | 
|  | vreader_get_name(reader)); | 
|  | } | 
|  | } else if (*string != 0) { | 
|  | printf("valid commands:\n"); | 
|  | printf("insert [reader_id]\n"); | 
|  | printf("remove [reader_id]\n"); | 
|  | printf("select reader_id\n"); | 
|  | printf("list\n"); | 
|  | printf("debug [level]\n"); | 
|  | printf("exit\n"); | 
|  | } | 
|  | } | 
|  | vreader_free(reader); | 
|  | printf("> "); | 
|  | fflush(stdout); | 
|  | } | 
|  |  | 
|  |  | 
|  | #define APDUBufSize 270 | 
|  |  | 
|  | /* just for ease of parsing command line arguments. */ | 
|  | #define MAX_CERTS 100 | 
|  |  | 
|  | static int | 
|  | connect_to_qemu( | 
|  | const char *host, | 
|  | const char *port | 
|  | ) { | 
|  | struct addrinfo hints; | 
|  | struct addrinfo *server; | 
|  | int ret; | 
|  |  | 
|  | sock = qemu_socket(AF_INET, SOCK_STREAM, 0); | 
|  | if (sock < 0) { | 
|  | /* Error */ | 
|  | fprintf(stderr, "Error opening socket!\n"); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | memset(&hints, 0, sizeof(struct addrinfo)); | 
|  | hints.ai_family = AF_UNSPEC; | 
|  | hints.ai_socktype = SOCK_STREAM; | 
|  | hints.ai_flags = 0; | 
|  | hints.ai_protocol = 0;          /* Any protocol */ | 
|  |  | 
|  | ret = getaddrinfo(host, port, &hints, &server); | 
|  |  | 
|  | if (ret != 0) { | 
|  | /* Error */ | 
|  | fprintf(stderr, "getaddrinfo failed\n"); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | if (connect(sock, server->ai_addr, server->ai_addrlen) < 0) { | 
|  | /* Error */ | 
|  | fprintf(stderr, "Could not connect\n"); | 
|  | return -1; | 
|  | } | 
|  | if (verbose) { | 
|  | printf("Connected (sizeof Header=%zd)!\n", sizeof(VSCMsgHeader)); | 
|  | } | 
|  | return sock; | 
|  | } | 
|  |  | 
|  | static int on_host_init(VSCMsgHeader *mhHeader, VSCMsgInit *incoming) | 
|  | { | 
|  | uint32_t *capabilities = (incoming->capabilities); | 
|  | int num_capabilities = | 
|  | 1 + ((mhHeader->length - sizeof(VSCMsgInit)) / sizeof(uint32_t)); | 
|  | int i; | 
|  | int rv; | 
|  | pthread_t thread_id; | 
|  |  | 
|  | incoming->version = ntohl(incoming->version); | 
|  | if (incoming->version != VSCARD_VERSION) { | 
|  | if (verbose > 0) { | 
|  | printf("warning: host has version %d, we have %d\n", | 
|  | verbose, VSCARD_VERSION); | 
|  | } | 
|  | } | 
|  | if (incoming->magic != VSCARD_MAGIC) { | 
|  | printf("unexpected magic: got %d, expected %d\n", | 
|  | incoming->magic, VSCARD_MAGIC); | 
|  | return -1; | 
|  | } | 
|  | for (i = 0 ; i < num_capabilities; ++i) { | 
|  | capabilities[i] = ntohl(capabilities[i]); | 
|  | } | 
|  | /* Future: check capabilities */ | 
|  | /* remove whatever reader might be left in qemu, | 
|  | * in case of an unclean previous exit. */ | 
|  | send_msg(VSC_ReaderRemove, VSCARD_MINIMAL_READER_ID, NULL, 0); | 
|  | /* launch the event_thread. This will trigger reader adds for all the | 
|  | * existing readers */ | 
|  | rv = pthread_create(&thread_id, NULL, event_thread, NULL); | 
|  | if (rv < 0) { | 
|  | perror("pthread_create"); | 
|  | return rv; | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | int | 
|  | main( | 
|  | int argc, | 
|  | char *argv[] | 
|  | ) { | 
|  | char *qemu_host; | 
|  | char *qemu_port; | 
|  | VSCMsgHeader mhHeader; | 
|  | VSCMsgError *error_msg; | 
|  |  | 
|  | int rv; | 
|  | int dwSendLength; | 
|  | int dwRecvLength; | 
|  | uint8_t pbRecvBuffer[APDUBufSize]; | 
|  | uint8_t pbSendBuffer[APDUBufSize]; | 
|  | VReaderStatus reader_status; | 
|  | VReader *reader = NULL; | 
|  | VCardEmulOptions *command_line_options = NULL; | 
|  |  | 
|  | char *cert_names[MAX_CERTS]; | 
|  | char *emul_args = NULL; | 
|  | int cert_count = 0; | 
|  | int c; | 
|  |  | 
|  | while ((c = getopt(argc, argv, "c:e:pd:")) != -1) { | 
|  | switch (c) { | 
|  | case 'c': | 
|  | if (cert_count >= MAX_CERTS) { | 
|  | printf("too many certificates (max = %d)\n", MAX_CERTS); | 
|  | exit(5); | 
|  | } | 
|  | cert_names[cert_count++] = optarg; | 
|  | break; | 
|  | case 'e': | 
|  | emul_args = optarg; | 
|  | break; | 
|  | case 'p': | 
|  | print_usage(); | 
|  | exit(4); | 
|  | break; | 
|  | case 'd': | 
|  | verbose = get_id_from_string(optarg, 1); | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (argc - optind != 2) { | 
|  | print_usage(); | 
|  | exit(4); | 
|  | } | 
|  |  | 
|  | if (cert_count > 0) { | 
|  | char *new_args; | 
|  | int len, i; | 
|  | /* if we've given some -c options, we clearly we want do so some | 
|  | * software emulation.  add that emulation now. this is NSS Emulator | 
|  | * specific */ | 
|  | if (emul_args == NULL) { | 
|  | emul_args = (char *)"db=\"/etc/pki/nssdb\""; | 
|  | } | 
|  | #define SOFT_STRING ",soft=(,Virtual Reader,CAC,," | 
|  | /* 2 == close paren & null */ | 
|  | len = strlen(emul_args) + strlen(SOFT_STRING) + 2; | 
|  | for (i = 0; i < cert_count; i++) { | 
|  | len += strlen(cert_names[i])+1; /* 1 == comma */ | 
|  | } | 
|  | new_args = g_malloc(len); | 
|  | strcpy(new_args, emul_args); | 
|  | strcat(new_args, SOFT_STRING); | 
|  | for (i = 0; i < cert_count; i++) { | 
|  | strcat(new_args, cert_names[i]); | 
|  | strcat(new_args, ","); | 
|  | } | 
|  | strcat(new_args, ")"); | 
|  | emul_args = new_args; | 
|  | } | 
|  | if (emul_args) { | 
|  | command_line_options = vcard_emul_options(emul_args); | 
|  | } | 
|  |  | 
|  | qemu_host = strdup(argv[argc - 2]); | 
|  | qemu_port = strdup(argv[argc - 1]); | 
|  | sock = connect_to_qemu(qemu_host, qemu_port); | 
|  | if (sock == -1) { | 
|  | fprintf(stderr, "error opening socket, exiting.\n"); | 
|  | exit(5); | 
|  | } | 
|  |  | 
|  | qemu_mutex_init(&write_lock); | 
|  | qemu_mutex_init(&pending_reader_lock); | 
|  | qemu_cond_init(&pending_reader_condition); | 
|  |  | 
|  | vcard_emul_init(command_line_options); | 
|  |  | 
|  | printf("> "); | 
|  | fflush(stdout); | 
|  |  | 
|  | /* Send init message, Host responds (and then we send reader attachments) */ | 
|  | VSCMsgInit init = { | 
|  | .version = htonl(VSCARD_VERSION), | 
|  | .magic = VSCARD_MAGIC, | 
|  | .capabilities = {0} | 
|  | }; | 
|  | send_msg(VSC_Init, mhHeader.reader_id, &init, sizeof(init)); | 
|  |  | 
|  | do { | 
|  | fd_set fds; | 
|  |  | 
|  | FD_ZERO(&fds); | 
|  | FD_SET(1, &fds); | 
|  | FD_SET(sock, &fds); | 
|  |  | 
|  | /* waiting on input from the socket */ | 
|  | rv = select(sock+1, &fds, NULL, NULL, NULL); | 
|  | if (rv < 0) { | 
|  | /* handle error */ | 
|  | perror("select"); | 
|  | return 7; | 
|  | } | 
|  | if (FD_ISSET(1, &fds)) { | 
|  | do_command(); | 
|  | } | 
|  | if (!FD_ISSET(sock, &fds)) { | 
|  | continue; | 
|  | } | 
|  |  | 
|  | rv = read(sock, &mhHeader, sizeof(mhHeader)); | 
|  | if (rv < sizeof(mhHeader)) { | 
|  | /* Error */ | 
|  | if (rv < 0) { | 
|  | perror("header read error\n"); | 
|  | } else { | 
|  | fprintf(stderr, "header short read %d\n", rv); | 
|  | } | 
|  | return 8; | 
|  | } | 
|  | mhHeader.type = ntohl(mhHeader.type); | 
|  | mhHeader.reader_id = ntohl(mhHeader.reader_id); | 
|  | mhHeader.length = ntohl(mhHeader.length); | 
|  | if (verbose) { | 
|  | printf("Header: type=%d, reader_id=%u length=%d (0x%x)\n", | 
|  | mhHeader.type, mhHeader.reader_id, mhHeader.length, | 
|  | mhHeader.length); | 
|  | } | 
|  | switch (mhHeader.type) { | 
|  | case VSC_APDU: | 
|  | case VSC_Flush: | 
|  | case VSC_Error: | 
|  | case VSC_Init: | 
|  | rv = read(sock, pbSendBuffer, mhHeader.length); | 
|  | break; | 
|  | default: | 
|  | fprintf(stderr, "Unexpected message of type 0x%X\n", mhHeader.type); | 
|  | return 0; | 
|  | } | 
|  | switch (mhHeader.type) { | 
|  | case VSC_APDU: | 
|  | if (rv < 0) { | 
|  | /* Error */ | 
|  | fprintf(stderr, "read error\n"); | 
|  | close(sock); | 
|  | return 8; | 
|  | } | 
|  | if (verbose) { | 
|  | printf(" recv APDU: "); | 
|  | print_byte_array(pbSendBuffer, mhHeader.length); | 
|  | } | 
|  | /* Transmit received APDU */ | 
|  | dwSendLength = mhHeader.length; | 
|  | dwRecvLength = sizeof(pbRecvBuffer); | 
|  | reader = vreader_get_reader_by_id(mhHeader.reader_id); | 
|  | reader_status = vreader_xfr_bytes(reader, | 
|  | pbSendBuffer, dwSendLength, | 
|  | pbRecvBuffer, &dwRecvLength); | 
|  | if (reader_status == VREADER_OK) { | 
|  | mhHeader.length = dwRecvLength; | 
|  | if (verbose) { | 
|  | printf(" send response: "); | 
|  | print_byte_array(pbRecvBuffer, mhHeader.length); | 
|  | } | 
|  | send_msg(VSC_APDU, mhHeader.reader_id, | 
|  | pbRecvBuffer, dwRecvLength); | 
|  | } else { | 
|  | rv = reader_status; /* warning: not meaningful */ | 
|  | send_msg(VSC_Error, mhHeader.reader_id, &rv, sizeof(uint32_t)); | 
|  | } | 
|  | vreader_free(reader); | 
|  | reader = NULL; /* we've freed it, don't use it by accident | 
|  | again */ | 
|  | break; | 
|  | case VSC_Flush: | 
|  | /* TODO: actually flush */ | 
|  | send_msg(VSC_FlushComplete, mhHeader.reader_id, NULL, 0); | 
|  | break; | 
|  | case VSC_Error: | 
|  | error_msg = (VSCMsgError *) pbSendBuffer; | 
|  | if (error_msg->code == VSC_SUCCESS) { | 
|  | qemu_mutex_lock(&pending_reader_lock); | 
|  | if (pending_reader) { | 
|  | vreader_set_id(pending_reader, mhHeader.reader_id); | 
|  | vreader_free(pending_reader); | 
|  | pending_reader = NULL; | 
|  | qemu_cond_signal(&pending_reader_condition); | 
|  | } | 
|  | qemu_mutex_unlock(&pending_reader_lock); | 
|  | break; | 
|  | } | 
|  | printf("warning: qemu refused to add reader\n"); | 
|  | if (error_msg->code == VSC_CANNOT_ADD_MORE_READERS) { | 
|  | /* clear pending reader, qemu can't handle any more */ | 
|  | qemu_mutex_lock(&pending_reader_lock); | 
|  | if (pending_reader) { | 
|  | pending_reader = NULL; | 
|  | /* make sure the event loop doesn't hang */ | 
|  | qemu_cond_signal(&pending_reader_condition); | 
|  | } | 
|  | qemu_mutex_unlock(&pending_reader_lock); | 
|  | } | 
|  | break; | 
|  | case VSC_Init: | 
|  | if (on_host_init(&mhHeader, (VSCMsgInit *)pbSendBuffer) < 0) { | 
|  | return -1; | 
|  | } | 
|  | break; | 
|  | default: | 
|  | printf("Default\n"); | 
|  | return 0; | 
|  | } | 
|  | } while (rv >= 0); | 
|  |  | 
|  | return 0; | 
|  | } |