C++程序  |  214行  |  5.97 KB

/*
 * Copyright (C) 2017 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * Hack-y server to forward communication with an eSE during development.
 * See README.md for more information.
 */

#include <arpa/inet.h>
#include <linux/un.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>

#define LOG_TAG "ese-relay"
#include <ese/ese.h>
#include <ese/log.h>

extern const uint8_t *kAtr;
extern size_t kAtrLength;
extern void *kEseOpenData;
void ese_relay_init(struct EseInterface *ese);

/*
 * Aligned with vpcd.h in
 * https://frankmorgner.github.io/vsmartcard/virtualsmartcard
 */
#define CMD_POWER_OFF 0
#define CMD_POWER_ON 1
#define CMD_RESET 2
#define CMD_ATR 4

int setup_socket(const char *name) {
  int fd;
  struct sockaddr_un addr;

  memset(&addr, 0, sizeof(struct sockaddr_un));
  addr.sun_family = AF_UNIX;
  if (strlen(name) > UNIX_PATH_MAX - 1) {
    ALOGE("Abstract listener name too long.");
    return -1;
  }
  strncpy(&addr.sun_path[1], name, strlen(name));
  fd = socket(AF_UNIX, SOCK_STREAM, 0);
  if (fd == -1) {
    ALOGE("Could not open socket.");
    return fd;
  }
  if (bind(fd, (struct sockaddr *)&addr,
           sizeof(sa_family_t) + strlen(name) + 1) == -1) {
    ALOGE("Failed to bind to abstract socket name");
    close(fd);
    return -1;
  }
  return fd;
}

int main() {
  int server_fd = setup_socket(LOG_TAG);
  int client_fd = -1;
  struct EseInterface ese;
  ese_relay_init(&ese);

  if (listen(server_fd, 4)) {
    ALOGE("Failed to listen on socket.");
    close(server_fd);
    return -1;
  }

  while (server_fd) {
    struct sockaddr client_info;
    socklen_t client_info_len = (socklen_t)sizeof(&client_info);
    int client_fd;
    uint32_t tx_len, data_read;
    uint32_t rx_len;
    uint16_t network_tx_len;
    uint16_t network_rx_len;
    uint8_t tx_buf[4096];
    uint8_t rx_buf[4096];
    int connected = 0;

    if ((client_fd = accept(server_fd, &client_info, &client_info_len)) == -1) {
      ALOGE("Fatal error accept()ing a client connection.");
      return -1;
    }
    printf("Client connected.\n");
    connected = 1;
    if (ese_open(&ese, kEseOpenData)) {
      ALOGE("Cannot open hw");
      if (ese_error(&ese))
        ALOGE("eSE error (%d): %s", ese_error_code(&ese),
              ese_error_message(&ese));
      return 1;
    }
    printf("eSE is open\n");

    while (connected) {
      printf("Listening for data from client\n");
      if (read(client_fd, &network_tx_len, sizeof(network_tx_len)) !=
          sizeof(network_tx_len)) {
        ALOGE("Client disconnected.");
        break;
      }
      tx_len = (uint32_t)ntohs(network_tx_len);
      printf("tx_len: %u\n", tx_len);
      if (tx_len == 0) {
        ALOGE("Client had nothing to say. Goodbye.");
        break;
      }
      if (tx_len > sizeof(tx_buf)) {
        ALOGE("Client payload too large: %u", tx_len);
        break;
      }
      for (data_read = 0; data_read < tx_len;) {
        printf("Reading payload: %u of %u remaining\n", data_read, tx_len);
        ssize_t bytes = read(client_fd, tx_buf + data_read, tx_len - data_read);
        if (bytes < 0) {
          ALOGE("Client abandoned hope during transmission.");
          connected = 0;
          break;
        }
        data_read += bytes;
      }
      /* Finally, we can transcieve. */
      if (tx_len) {
        uint32_t i;
        printf("Sending %u bytes to card\n", tx_len);
        printf("TX: ");
        for (i = 0; i < tx_len; ++i)
          printf("%.2X ", tx_buf[i]);
        printf("\n");
      }

      if (tx_len == 1) { /* Control request */
        printf("Received a control request: %x\n", tx_buf[0]);
        rx_len = 0;
        switch (tx_buf[0]) {
        case CMD_POWER_OFF:
          ese.ops->hw_reset(&ese);
          break;
        case CMD_POWER_ON:
          break;
        case CMD_RESET:
          ese.ops->hw_reset(&ese);
          break;
        case CMD_ATR:
          /* Send a dummy ATR for another JCOP card */
          rx_len = kAtrLength;
          printf("Filling card RX buf with fake ATR (%u)\n", rx_len);
          memcpy(rx_buf, kAtr, rx_len);
          printf("Sending back ATR of length %u\n", rx_len);
          break;
        default:
          ALOGE("Unknown control byte seen: %x", tx_buf[0]);
        }
        if (!rx_len)
          continue;
      } else {
        rx_len = ese_transceive(&ese, tx_buf, tx_len, rx_buf, sizeof(rx_buf));
        if (ese_error(&ese)) {
          ALOGE("An error (%d) occurred: %s", ese_error_code(&ese),
                ese_error_message(&ese));
          return -1;
        }
      }
      if (rx_len > 0) {
        uint32_t i;
        printf("Read %d bytes from card\n", rx_len);
        printf("RX: ");
        for (i = 0; i < rx_len; ++i)
          printf("%.2X ", rx_buf[i]);
        printf("\n");
      }

      /* Send to client */
      network_rx_len = htons((uint16_t)rx_len);
      if (write(client_fd, &network_rx_len, sizeof(network_rx_len)) !=
          sizeof(network_rx_len)) {
        ALOGE("Client abandoned hope during response size.");
        break;
      }

      for (data_read = 0; data_read < rx_len;) {
        ssize_t bytes =
            write(client_fd, rx_buf + data_read, rx_len - data_read);
        if (bytes < 0) {
          ALOGE("Client abandoned hope during response.");
          connected = 0;
          break;
        }
        data_read += bytes;
      }
      usleep(1000);
    }
    close(client_fd);
    printf("Session ended\n\n");
    ese_close(&ese);
  }
  return 0;
}