vscclient.c 23.1 KB
Newer Older
Robert Relyea's avatar
Robert Relyea committed
1 2 3 4 5 6 7 8 9 10 11 12
/*
 * 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.
 */

13
#ifndef _WIN32
Robert Relyea's avatar
Robert Relyea committed
14
#include <netdb.h>
15 16
#endif
#include <glib.h>
Robert Relyea's avatar
Robert Relyea committed
17 18

#include "qemu-common.h"
19 20
#include "qemu/thread.h"
#include "qemu/sockets.h"
Robert Relyea's avatar
Robert Relyea committed
21 22 23 24 25 26 27

#include "vscard_common.h"

#include "vreader.h"
#include "vcard_emul.h"
#include "vevent.h"

28
static int verbose;
Robert Relyea's avatar
Robert Relyea committed
29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54

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();
}

55 56 57 58 59 60
static GIOChannel *channel_socket;
static GByteArray *socket_to_send;
static QemuMutex socket_to_send_lock;
static guint socket_tag;

static void
61
update_socket_watch(void);
62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82

static gboolean
do_socket_send(GIOChannel *source,
               GIOCondition condition,
               gpointer data)
{
    gsize bw;
    GError *err = NULL;

    g_return_val_if_fail(socket_to_send->len != 0, FALSE);
    g_return_val_if_fail(condition & G_IO_OUT, FALSE);

    g_io_channel_write_chars(channel_socket,
        (gchar *)socket_to_send->data, socket_to_send->len, &bw, &err);
    if (err != NULL) {
        g_error("Error while sending socket %s", err->message);
        return FALSE;
    }
    g_byte_array_remove_range(socket_to_send, 0, bw);

    if (socket_to_send->len == 0) {
83
        update_socket_watch();
84 85 86 87 88 89 90 91
        return FALSE;
    }
    return TRUE;
}

static gboolean
socket_prepare_sending(gpointer user_data)
{
92
    update_socket_watch();
93 94 95

    return FALSE;
}
Robert Relyea's avatar
Robert Relyea committed
96 97 98 99 100 101 102 103 104 105

static int
send_msg(
    VSCMsgType type,
    uint32_t reader_id,
    const void *msg,
    unsigned int length
) {
    VSCMsgHeader mhHeader;

106
    qemu_mutex_lock(&socket_to_send_lock);
Robert Relyea's avatar
Robert Relyea committed
107 108

    if (verbose > 10) {
109
        printf("sending type=%d id=%u, len =%u (0x%x)\n",
Robert Relyea's avatar
Robert Relyea committed
110 111 112 113 114 115
               type, reader_id, length, length);
    }

    mhHeader.type = htonl(type);
    mhHeader.reader_id = 0;
    mhHeader.length = htonl(length);
116 117 118 119 120
    g_byte_array_append(socket_to_send, (guint8 *)&mhHeader, sizeof(mhHeader));
    g_byte_array_append(socket_to_send, (guint8 *)msg, length);
    g_idle_add(socket_prepare_sending, NULL);

    qemu_mutex_unlock(&socket_to_send_lock);
Robert Relyea's avatar
Robert Relyea committed
121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159

    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;
            }
160
            /* this reader hasn't been told its status from qemu yet, wait for
Robert Relyea's avatar
Robert Relyea committed
161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197
             * 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) {
198
                printf(" READER REMOVE: %u\n", reader_id);
Robert Relyea's avatar
Robert Relyea committed
199 200 201 202 203 204 205 206 207 208
            }
            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) {
209
                printf(" CARD INSERT %u: ", reader_id);
Robert Relyea's avatar
Robert Relyea committed
210 211 212 213 214 215 216
                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) {
217
                printf(" CARD REMOVE %u:\n", reader_id);
Robert Relyea's avatar
Robert Relyea committed
218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241
            }
            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;
}

242 243 244 245 246 247 248
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;
249
    QemuThread thread_id;
250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271

    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 */
272
    qemu_thread_create(&thread_id, event_thread, NULL, 0);
273 274 275
    return 0;
}

276 277 278 279 280 281

enum {
    STATE_HEADER,
    STATE_MESSAGE,
};

282 283
#define APDUBufSize 270

284 285 286 287
static gboolean
do_socket_read(GIOChannel *source,
               GIOCondition condition,
               gpointer data)
288 289 290 291 292
{
    int rv;
    int dwSendLength;
    int dwRecvLength;
    uint8_t pbRecvBuffer[APDUBufSize];
293
    static uint8_t pbSendBuffer[APDUBufSize];
294 295
    VReaderStatus reader_status;
    VReader *reader = NULL;
296
    static VSCMsgHeader mhHeader;
297
    VSCMsgError *error_msg;
298
    GError *err = NULL;
299

300 301 302 303 304 305 306
    static gchar *buf;
    static gsize br, to_read;
    static int state = STATE_HEADER;

    if (state == STATE_HEADER && to_read == 0) {
        buf = (gchar *)&mhHeader;
        to_read = sizeof(mhHeader);
307
    }
308 309 310 311 312

    if (to_read > 0) {
        g_io_channel_read_chars(source, (gchar *)buf, to_read, &br, &err);
        if (err != NULL) {
            g_error("error while reading: %s", err->message);
313
        }
314 315 316 317 318 319 320 321 322 323 324
        buf += br;
        to_read -= br;
        if (to_read != 0) {
            return TRUE;
        }
    }

    if (state == STATE_HEADER) {
        mhHeader.type = ntohl(mhHeader.type);
        mhHeader.reader_id = ntohl(mhHeader.reader_id);
        mhHeader.length = ntohl(mhHeader.length);
325
        if (verbose) {
326 327 328
            printf("Header: type=%d, reader_id=%u length=%d (0x%x)\n",
                   mhHeader.type, mhHeader.reader_id, mhHeader.length,
                   mhHeader.length);
329
        }
330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347
        switch (mhHeader.type) {
        case VSC_APDU:
        case VSC_Flush:
        case VSC_Error:
        case VSC_Init:
            buf = (gchar *)pbSendBuffer;
            to_read = mhHeader.length;
            state = STATE_MESSAGE;
            return TRUE;
        default:
            fprintf(stderr, "Unexpected message of type 0x%X\n", mhHeader.type);
            return FALSE;
        }
    }

    if (state == STATE_MESSAGE) {
        switch (mhHeader.type) {
        case VSC_APDU:
348
            if (verbose) {
349 350
                printf(" recv APDU: ");
                print_byte_array(pbSendBuffer, mhHeader.length);
351
            }
352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369
            /* 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));
370
            }
371 372 373
            vreader_free(reader);
            reader = NULL; /* we've freed it, don't use it by accident
                              again */
374
            break;
375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390
        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;
391
            }
392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409
            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 FALSE;
            }
            break;
        default:
410
            g_assert_not_reached();
411
            return FALSE;
412
        }
413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429

        state = STATE_HEADER;
    }


    return TRUE;
}

static gboolean
do_socket(GIOChannel *source,
          GIOCondition condition,
          gpointer data)
{
    /* not sure if two watches work well with a single win32 sources */
    if (condition & G_IO_OUT) {
        if (!do_socket_send(source, condition, data)) {
            return FALSE;
430 431 432
        }
    }

433 434 435 436 437 438 439
    if (condition & G_IO_IN) {
        if (!do_socket_read(source, condition, data)) {
            return FALSE;
        }
    }

    return TRUE;
440 441
}

Robert Relyea's avatar
Robert Relyea committed
442
static void
443
update_socket_watch(void)
444
{
445 446
    gboolean out = socket_to_send->len > 0;

447 448 449 450 451 452 453 454 455 456 457 458
    if (socket_tag != 0) {
        g_source_remove(socket_tag);
    }

    socket_tag = g_io_add_watch(channel_socket,
        G_IO_IN | (out ? G_IO_OUT : 0), do_socket, NULL);
}

static gboolean
do_command(GIOChannel *source,
           GIOCondition condition,
           gpointer data)
Robert Relyea's avatar
Robert Relyea committed
459 460 461 462 463 464
{
    char *string;
    VCardEmulError error;
    static unsigned int default_reader_id;
    unsigned int reader_id;
    VReader *reader = NULL;
465 466 467
    GError *err = NULL;

    g_assert(condition & G_IO_IN);
Robert Relyea's avatar
Robert Relyea committed
468 469

    reader_id = default_reader_id;
470 471 472 473 474
    g_io_channel_read_line(source, &string, NULL, NULL, &err);
    if (err != NULL) {
        g_error("Error while reading command: %s", err->message);
    }

Robert Relyea's avatar
Robert Relyea committed
475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507
    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 {
508
                printf("no reader by id %u found\n", reader_id);
Robert Relyea's avatar
Robert Relyea committed
509 510 511 512 513 514 515 516 517 518 519 520
            }
        } 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 {
521
                printf("no reader by id %u found\n", reader_id);
Robert Relyea's avatar
Robert Relyea committed
522 523 524 525 526 527 528 529 530 531
            }
        } 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) {
532
                printf("Selecting reader %u, %s\n", reader_id,
Robert Relyea's avatar
Robert Relyea committed
533 534 535
                        vreader_get_name(reader));
                default_reader_id = reader_id;
            } else {
536
                printf("Reader with id %u not found\n", reader_id);
Robert Relyea's avatar
Robert Relyea committed
537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554
            }
        } 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;
                }
555
                printf("%3u %s %s\n", reader_id,
Robert Relyea's avatar
Robert Relyea committed
556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587
                       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);
588 589

    return TRUE;
Robert Relyea's avatar
Robert Relyea committed
590 591 592 593 594 595 596 597 598 599 600 601 602
}


/* 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;
603
    int ret, sock;
Robert Relyea's avatar
Robert Relyea committed
604 605 606 607 608

    sock = qemu_socket(AF_INET, SOCK_STREAM, 0);
    if (sock < 0) {
        /* Error */
        fprintf(stderr, "Error opening socket!\n");
609
        return -1;
Robert Relyea's avatar
Robert Relyea committed
610 611 612 613 614 615 616 617 618 619 620 621 622
    }

    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");
623
        goto cleanup_socket;
Robert Relyea's avatar
Robert Relyea committed
624 625 626 627 628
    }

    if (connect(sock, server->ai_addr, server->ai_addrlen) < 0) {
        /* Error */
        fprintf(stderr, "Could not connect\n");
629
        goto cleanup_socket;
Robert Relyea's avatar
Robert Relyea committed
630 631 632 633 634
    }
    if (verbose) {
        printf("Connected (sizeof Header=%zd)!\n", sizeof(VSCMsgHeader));
    }
    return sock;
635 636 637 638

cleanup_socket:
    closesocket(sock);
    return -1;
Robert Relyea's avatar
Robert Relyea committed
639 640 641 642 643 644 645
}

int
main(
    int argc,
    char *argv[]
) {
646 647
    GMainLoop *loop;
    GIOChannel *channel_stdin;
Robert Relyea's avatar
Robert Relyea committed
648 649 650 651 652 653 654 655
    char *qemu_host;
    char *qemu_port;

    VCardEmulOptions *command_line_options = NULL;

    char *cert_names[MAX_CERTS];
    char *emul_args = NULL;
    int cert_count = 0;
656 657 658 659
    int c, sock;

    if (socket_init() != 0)
        return 1;
Robert Relyea's avatar
Robert Relyea committed
660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702

    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 */
        }
703
        new_args = g_malloc(len);
Robert Relyea's avatar
Robert Relyea committed
704 705 706 707 708 709 710 711 712 713 714 715 716
        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);
    }

717 718
    qemu_host = g_strdup(argv[argc - 2]);
    qemu_port = g_strdup(argv[argc - 1]);
Robert Relyea's avatar
Robert Relyea committed
719
    sock = connect_to_qemu(qemu_host, qemu_port);
720 721 722 723
    if (sock == -1) {
        fprintf(stderr, "error opening socket, exiting.\n");
        exit(5);
    }
Robert Relyea's avatar
Robert Relyea committed
724

725 726
    socket_to_send = g_byte_array_new();
    qemu_mutex_init(&socket_to_send_lock);
Robert Relyea's avatar
Robert Relyea committed
727 728 729 730 731
    qemu_mutex_init(&pending_reader_lock);
    qemu_cond_init(&pending_reader_condition);

    vcard_emul_init(command_line_options);

732 733
    loop = g_main_loop_new(NULL, true);

Robert Relyea's avatar
Robert Relyea committed
734 735 736
    printf("> ");
    fflush(stdout);

737 738 739 740 741 742 743 744 745 746 747 748 749 750 751
#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_add_watch(channel_stdin, G_IO_IN, do_command, NULL);
#ifdef _WIN32
    channel_socket = g_io_channel_win32_new_socket(sock);
#else
    channel_socket = g_io_channel_unix_new(sock);
#endif
    g_io_channel_set_encoding(channel_socket, NULL, NULL);
    /* we buffer ourself for thread safety reasons */
    g_io_channel_set_buffered(channel_socket, FALSE);

Robert Relyea's avatar
Robert Relyea committed
752 753 754 755 756 757
    /* Send init message, Host responds (and then we send reader attachments) */
    VSCMsgInit init = {
        .version = htonl(VSCARD_VERSION),
        .magic = VSCARD_MAGIC,
        .capabilities = {0}
    };
758
    send_msg(VSC_Init, 0, &init, sizeof(init));
Robert Relyea's avatar
Robert Relyea committed
759

760 761
    g_main_loop_run(loop);
    g_main_loop_unref(loop);
Robert Relyea's avatar
Robert Relyea committed
762

763 764
    g_io_channel_unref(channel_stdin);
    g_io_channel_unref(channel_socket);
765
    g_byte_array_free(socket_to_send, TRUE);
Robert Relyea's avatar
Robert Relyea committed
766

767
    closesocket(sock);
Robert Relyea's avatar
Robert Relyea committed
768 769
    return 0;
}