From: Marc-André Lureau <marcandre.lur...@gmail.com> Signed-off-by: Marc-André Lureau <marcandre.lur...@redhat.com> --- libcacard/vscclient.c | 374 ++++++++++++++++++++++++++------------------------ 1 file changed, 198 insertions(+), 176 deletions(-)
diff --git a/libcacard/vscclient.c b/libcacard/vscclient.c index 9b744f2..ad2ffd1 100644 --- a/libcacard/vscclient.c +++ b/libcacard/vscclient.c @@ -10,7 +10,10 @@ * See the COPYING.LIB file in the top-level directory. */ +#ifndef _WIN32 #include <netdb.h> +#endif +#include <glib.h> #include "qemu-common.h" #include "qemu/thread.h" @@ -73,7 +76,7 @@ send_msg( mhHeader.type = htonl(type); mhHeader.reader_id = 0; mhHeader.length = htonl(length); - rv = write(sock, &mhHeader, sizeof(mhHeader)); + rv = send(sock, (char *)&mhHeader, sizeof(mhHeader), 0); if (rv < 0) { /* Error */ fprintf(stderr, "write header error\n"); @@ -81,7 +84,7 @@ send_msg( qemu_mutex_unlock(&write_lock); return 16; } - rv = write(sock, msg, length); + rv = send(sock, (char *)msg, length, 0); if (rv < 0) { /* Error */ fprintf(stderr, "write error\n"); @@ -211,18 +214,179 @@ get_id_from_string(char *string, unsigned int default_id) return id; } -static void -do_command(void) +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; + QemuThread 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 */ + qemu_thread_create(&thread_id, event_thread, NULL, 0); + return 0; +} + +#define APDUBufSize 270 + +static gboolean +do_socket(GIOChannel *source, + GIOCondition condition, + gpointer data) +{ + gsize rv; + int dwSendLength; + int dwRecvLength; + uint8_t pbRecvBuffer[APDUBufSize]; + uint8_t pbSendBuffer[APDUBufSize]; + VReaderStatus reader_status; + VReader *reader = NULL; + VSCMsgHeader mhHeader; + VSCMsgError *error_msg; + + if (g_io_channel_read_chars(source, + (gchar *)&mhHeader, sizeof(mhHeader), &rv, NULL) + != G_IO_STATUS_NORMAL) { + g_error("error while reading from socket"); + } + + if (rv < sizeof(mhHeader)) { + fprintf(stderr, "header short read %" G_GSIZE_FORMAT "\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: + if (g_io_channel_read_chars(source, + (gchar *)pbSendBuffer, mhHeader.length, &rv, NULL) + != G_IO_STATUS_NORMAL) { + g_error("error while reading from socket"); + } + break; + default: + fprintf(stderr, "Unexpected message of type 0x%X\n", mhHeader.type); + return 0; + } + switch (mhHeader.type) { + case VSC_APDU: + 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; + } + + return TRUE; +} + +static gboolean +do_command(GIOChannel *source, + GIOCondition condition, + gpointer data) { - char inbuf[255]; char *string; VCardEmulError error; static unsigned int default_reader_id; unsigned int reader_id; VReader *reader = NULL; + g_assert(condition & G_IO_IN); + reader_id = default_reader_id; - string = fgets(inbuf, sizeof(inbuf), stdin); + if (g_io_channel_read_line(source, &string, + NULL, NULL, NULL) != G_IO_STATUS_NORMAL) + g_error("Error while reading command"); + if (string != NULL) { if (strncmp(string, "exit", 4) == 0) { /* remove all the readers */ @@ -336,10 +500,10 @@ do_command(void) vreader_free(reader); printf("> "); fflush(stdout); -} + return TRUE; +} -#define APDUBufSize 270 /* just for ease of parsing command line arguments. */ #define MAX_CERTS 100 @@ -385,61 +549,18 @@ connect_to_qemu( 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[] ) { + GMainLoop *loop; + GIOChannel *channel_stdin; + GIOChannel *channel_socket; 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]; @@ -447,6 +568,9 @@ main( int cert_count = 0; int c; + if (socket_init() != 0) + return 1; + while ((c = getopt(argc, argv, "c:e:pd:")) != -1) { switch (c) { case 'c': @@ -528,130 +652,28 @@ main( }; 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); + loop = g_main_loop_new(NULL, true); +#ifdef _WIN32 + channel_stdin = g_io_channel_win32_new_fd(STDIN_FILENO); +#else + channel_stdin = g_io_channel_unix_new(STDIN_FILENO); +#endif + g_io_channel_set_flags(channel_stdin, G_IO_FLAG_NONBLOCK, NULL); + g_io_add_watch(channel_stdin, G_IO_IN, do_command, NULL); +#ifdef _WIN32 + channel_socket = g_io_channel_win32_new_socket(sock); + /* g_io_channel_win32_set_debug(channel_socket, TRUE); */ +#else + channel_socket = g_io_channel_unix_new(sock); +#endif + g_io_channel_set_encoding(channel_socket, NULL, NULL); + g_io_add_watch(channel_socket, G_IO_IN, do_socket, NULL); - /* 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; - } + g_main_loop_run(loop); + g_main_loop_unref(loop); - 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); + g_io_channel_unref(channel_stdin); + g_io_channel_unref(channel_socket); return 0; } -- 1.8.1.1.439.g50a6b54