diff --git a/README.md b/README.md index 8808338..22f7fea 100644 --- a/README.md +++ b/README.md @@ -72,6 +72,7 @@ worker threads have the following states: * ACCEPTING (they're blocking in the accept() call on the server socket fd) * CONNECTED_WAIT_ID (TCP established, but peer not yet identified itself) * CONNECTED_CLIENT (TCP established, client has identified itself, no mapping) +* CONNECTED_CLIENT_WAIT_MAP(TCP established, client has identified, waiting for mapping) * CONNECTED_CLIENT_MAPPED (TCP established, client has identified itself, mapping exists) * CONNECTED_CLIENT_MAPPED_CARD (TCP established, client identified, mapping exists, card opened) * CONNECTED_SERVER (TCP established, server has identified itself) diff --git a/src/bankd.h b/src/bankd.h index 21d0ccb..bd7130a 100644 --- a/src/bankd.h +++ b/src/bankd.h @@ -46,6 +46,15 @@ static inline bool client_slot_equals(const struct client_slot *a, const struct return false; } +static inline ClientSlot_t client_slot2asn(const struct client_slot *in) +{ + ClientSlot_t out = { + .clientId = in->client_id, + .slotNr = in->slot_nr, + }; + return out; +} + /* slot mappings are created / removed by the server */ struct bankd_slot_mapping { /* global lits of bankd slot mappings */ @@ -100,6 +109,9 @@ struct bankd_worker { /* worker thread state */ enum bankd_worker_state state; + uint8_t atr[32]; + unsigned int atr_len; + /* slot number we are representing */ struct bank_slot slot; @@ -116,12 +128,15 @@ struct bankd_worker { struct { const char *name; + struct osmo_fsm_inst *fi; union { struct { /* PC/SC context / application handle */ SCARDCONTEXT hContext; /* PC/SC card handle */ SCARDHANDLE hCard; + /* PC/SC slot status (SCARD_ABSENT, ...) bit-mask */ + DWORD dwState; } pcsc; }; } reader; @@ -152,3 +167,14 @@ struct bankd { int bankd_pcsc_read_slotnames(struct bankd *bankd, const char *csv_file); const char *bankd_pcsc_get_slot_name(struct bankd *bankd, const struct bank_slot *slot); + +enum sc_fsm_events { + SC_E_CONNECT_CMD, + SC_E_DISCONNECT_CMD, + SC_E_TPDU_CMD, +}; + +struct osmo_fsm_inst *sc_fsm_alloc(struct bankd_worker *worker); + +int worker_handle_tpduModemToCard(struct bankd_worker *worker, const RsproPDU_t *pdu); +int worker_send_rspro(struct bankd_worker *worker, RsproPDU_t *pdu); diff --git a/src/bankd_main.c b/src/bankd_main.c index 3545d68..5f1844a 100644 --- a/src/bankd_main.c +++ b/src/bankd_main.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -206,10 +207,7 @@ static int worker_open_card(struct bankd_worker *worker) rc = SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL, &worker->reader.pcsc.hContext); PCSC_ERROR(worker, rc, "SCardEstablishContext") - DWORD dwActiveProtocol; - rc = SCardConnect(worker->reader.pcsc.hContext, worker->reader.name, SCARD_SHARE_SHARED, - SCARD_PROTOCOL_T0, &worker->reader.pcsc.hCard, &dwActiveProtocol); - PCSC_ERROR(worker, rc, "SCardConnect") + worker->reader.fi = sc_fsm_alloc(worker); worker_set_state(worker, BW_ST_CONN_CLIENT_MAPPED_CARD); @@ -246,7 +244,7 @@ static int blocking_ipa_read(int fd, uint8_t *buf, unsigned int buf_size) return len; } -static int worker_send_rspro(struct bankd_worker *worker, RsproPDU_t *pdu) +int worker_send_rspro(struct bankd_worker *worker, RsproPDU_t *pdu) { struct msgb *msg = rspro_enc_msg(pdu); int rc; @@ -261,6 +259,7 @@ static int worker_send_rspro(struct bankd_worker *worker, RsproPDU_t *pdu) ipa_prepend_header_ext(msg, IPAC_PROTO_EXT_RSPRO); ipa_prepend_header(msg, IPAC_PROTO_OSMO); + printf("tx: %s\n", msgb_hexdump(msg)); /* actually send it through the socket */ rc = write(worker->client.fd, msgb_data(msg), msgb_length(msg)); if (rc == msgb_length(msg)) @@ -327,7 +326,7 @@ static int worker_handle_connectClientReq(struct bankd_worker *worker, const Rsp return worker_send_rspro(worker, resp); } -static int worker_handle_tpduModemToCard(struct bankd_worker *worker, const RsproPDU_t *pdu) +int worker_handle_tpduModemToCard(struct bankd_worker *worker, const RsproPDU_t *pdu) { const struct TpduModemToCard *mdm2sim = &pdu->msg.choice.tpduModemToCard; const SCARD_IO_REQUEST *pioSendPci = SCARD_PCI_T0; @@ -339,11 +338,6 @@ static int worker_handle_tpduModemToCard(struct bankd_worker *worker, const Rspr LOGW(worker, "tpduModemToCard(%s)\n", osmo_hexdump_nospc(mdm2sim->data.buf, mdm2sim->data.size)); - if (worker->state != BW_ST_CONN_CLIENT_MAPPED_CARD) { - LOGW(worker, "Unexpected tpduModemToCaard\n"); - return -104; - } - /* FIXME: Validate that toBankSlot / fromClientSlot match our expectations */ rc = SCardTransmit(worker->reader.pcsc.hCard, @@ -371,7 +365,8 @@ static int worker_handle_rspro(struct bankd_worker *worker, const RsproPDU_t *pd rc = worker_handle_connectClientReq(worker, pdu); break; case RsproPDUchoice_PR_tpduModemToCard: - rc = worker_handle_tpduModemToCard(worker, pdu); + osmo_fsm_inst_dispatch(worker->reader.fi, SC_E_TPDU_CMD, (void *)pdu); + rc = 0; break; case RsproPDUchoice_PR_clientSlotStatusInd: /* FIXME */ diff --git a/src/bankd_pcsc.c b/src/bankd_pcsc.c index 2ab768c..01fe877 100644 --- a/src/bankd_pcsc.c +++ b/src/bankd_pcsc.c @@ -2,10 +2,17 @@ #include #include #include +#include +#include #include #include "bankd.h" +#include "rspro_util.h" + +/*********************************************************************** + * RSPRO bank/slot-id <-> PCSC Reader name mapping + ***********************************************************************/ struct pcsc_slot_name { struct llist_head list; @@ -116,3 +123,184 @@ const char *bankd_pcsc_get_slot_name(struct bankd *bankd, const struct bank_slot } return NULL; } + + +/*********************************************************************** + * SCard related FSM + ***********************************************************************/ + +#define S(x) (1 << (x)) + +#define T2_TIMEOUT_SECS 10 +#define T1_TIMEOUT_SECS 10 + +enum sc_fsm_states { + SC_ST_CARD_ABSENT, + SC_ST_CARD_PRESENT, +}; + +static const struct value_string sc_fsm_event_names[] = { + { SC_E_CONNECT_CMD, "CONNECT_CMD" }, + { SC_E_DISCONNECT_CMD, "DISCONNECT_CMD" }, + { SC_E_TPDU_CMD, "TPDU_CMD" }, + { 0, NULL } +}; + +/* an attempt at SCardConnect */ +static void attempt_sc_connect(struct osmo_fsm_inst *fi) +{ + struct bankd_worker *worker = fi->priv; + LONG rc; + DWORD protocol; + + /* another attempt at SCardConnect */ + rc = SCardConnect(worker->reader.pcsc.hContext, worker->reader.name, + SCARD_SHARE_EXCLUSIVE, SCARD_PROTOCOL_T0, + &worker->reader.pcsc.hCard, &protocol); + if (rc == SCARD_S_SUCCESS) { + osmo_fsm_inst_state_chg(fi, SC_ST_CARD_PRESENT, T2_TIMEOUT_SECS, 2); + /* FIXME: inform client */ + } else { + /* schedule the next SCardConnect request */ + osmo_timer_schedule(&fi->timer, T1_TIMEOUT_SECS, 1); + } +} + +/* no card currently present; attempt to re-connect via timer if asked to */ +static void sc_st_card_absent(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + struct bankd_worker *worker = fi->priv; + const struct TpduModemToCard *mdm2sim; + const RsproPDU_t *pdu, *pdu_resp; + + switch (event) { + case SC_E_CONNECT_CMD: + attempt_sc_connect(fi); + break; + case SC_E_TPDU_CMD: + pdu = data; + mdm2sim = &pdu->msg.choice.tpduModemToCard; + /* reject transceiving the PDU; we're not connected */ +#if 0 + pdu_resp = rspro_gen_TpduCard2Modem(&mdm2sim->toBankSlot, &mdm2sim->fromClientSlot, + rx_buf, rx_buf_len); + worker_send_rspro(worker, pdu_resp); +#endif + break; + default: + OSMO_ASSERT(0); + } +} + +static void sc_st_card_present(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + struct bankd_worker *worker = fi->priv; + const RsproPDU_t *pdu; + LONG rc; + + switch (event) { + case SC_E_TPDU_CMD: + /* transceive an APDU */ + pdu = data; + worker_handle_tpduModemToCard(worker, pdu); + break; + case SC_E_DISCONNECT_CMD: + rc = SCardDisconnect(worker->reader.pcsc.hCard, SCARD_UNPOWER_CARD); + /* FIXME: evaluate rc */ + osmo_fsm_inst_state_chg(fi, SC_ST_CARD_ABSENT, 0, 0); + break; + default: + OSMO_ASSERT(0); + } +} + +static int sc_timer_cb(struct osmo_fsm_inst *fi) +{ + struct bankd_worker *worker = fi->priv; + char reader_name[32]; + uint8_t atr[32]; + DWORD reader_state, protocol; + DWORD atr_len = sizeof(atr); + DWORD reader_name_len = sizeof(atr); + LONG rc; + + switch (fi->T) { + case 1: + attempt_sc_connect(fi); + break; + case 2: + /* another iteration of SCardStatus */ + rc = SCardStatus(worker->reader.pcsc.hCard, reader_name, &reader_name_len, + &reader_state, &protocol, atr, &atr_len); + if (rc == SCARD_S_SUCCESS) { + RsproPDU_t *pdu = NULL; + /* Determine any changes in state, and if so, report to client */ + if (reader_state != worker->reader.pcsc.dwState) { + worker->reader.pcsc.dwState = reader_state; + /* FIXME: inform client */ + //pdu = rspro_gen_SetAtrReq(foo, bar, worker->atr, worker->atr_len); + //worker_send_rspro(worker, pdu); + } + if (atr_len != worker->atr_len || memcmp(atr, worker->atr, atr_len)) { + ClientSlot_t clslot = client_slot2asn(&worker->client.clslot); + OSMO_ASSERT(atr_len < sizeof(worker->atr)); + memcpy(worker->atr, atr, atr_len); + worker->atr_len = atr_len; + /* inform client */ + pdu = rspro_gen_SetAtrReq(&clslot, worker->atr, worker->atr_len); + worker_send_rspro(worker, pdu); + } + /* schedule the next SCardStatus request */ + osmo_timer_schedule(&fi->timer, T2_TIMEOUT_SECS, 0); + } else + osmo_fsm_inst_state_chg(fi, SC_ST_CARD_ABSENT, T1_TIMEOUT_SECS, 1); + break; + default: + OSMO_ASSERT(0); + } + return 0; +} + +static const struct osmo_fsm_state sc_fsm_states[] = { + [SC_ST_CARD_ABSENT] = { + .in_event_mask = S(SC_E_CONNECT_CMD) | S(SC_E_DISCONNECT_CMD) | S(SC_E_TPDU_CMD), + .out_state_mask = S(SC_ST_CARD_PRESENT) | S(SC_ST_CARD_ABSENT), + .name = "CARD_ABSENT", + .action = sc_st_card_absent, + }, + [SC_ST_CARD_PRESENT] = { + .in_event_mask = S(SC_E_DISCONNECT_CMD) | S(SC_E_TPDU_CMD), + .out_state_mask = S(SC_ST_CARD_PRESENT) | S(SC_ST_CARD_ABSENT), + .name = "CART_PRESENT", + .action = sc_st_card_present, + }, +}; + +static struct osmo_fsm sc_fsm = { + .name = "SC", + .states = sc_fsm_states, + .num_states = ARRAY_SIZE(sc_fsm_states), + .timer_cb = sc_timer_cb, + .event_names = sc_fsm_event_names, +}; + +static bool fsm_initialized = false; + +struct osmo_fsm_inst *sc_fsm_alloc(struct bankd_worker *worker) +{ + struct osmo_fsm_inst *fi; + char num[8]; + + if (!fsm_initialized) { + osmo_fsm_register(&sc_fsm); + fsm_initialized = true; + } + + snprintf(num, 8, "%d", worker->num); + + fi = osmo_fsm_inst_alloc(&sc_fsm, worker, worker, LOGL_DEBUG, num); + + osmo_fsm_inst_dispatch(fi, SC_E_CONNECT_CMD, NULL); + + return fi; +} diff --git a/src/remsim_client.c b/src/remsim_client.c index 850ded6..73a1798 100644 --- a/src/remsim_client.c +++ b/src/remsim_client.c @@ -33,6 +33,7 @@ void ipa_client_conn_send_rspro(struct ipa_client_conn *ipa, RsproPDU_t *rspro) static int bankd_handle_msg(struct bankd_client *bc, struct msgb *msg) { + printf("Decoding RSPRO %s\n", msgb_hexdump(msg)); RsproPDU_t *pdu = rspro_dec_msg(msg); if (!pdu) { fprintf(stderr, "Error decoding PDU\n"); diff --git a/src/remsim_client_fsm.c b/src/remsim_client_fsm.c index 2459a14..e116ca3 100644 --- a/src/remsim_client_fsm.c +++ b/src/remsim_client_fsm.c @@ -91,7 +91,7 @@ static void bdc_st_established_onenter(struct osmo_fsm_inst *fi, uint32_t prev_s struct bankd_client *bc = (struct bankd_client *) fi->priv; RsproPDU_t *pdu; - /* FIXME: Send ClientConnReq */ + /* FIXME: make configurable */ const ClientSlot_t clslot = { .clientId = 23, .slotNr = 1 }; pdu = rspro_gen_ConnectClientReq(&bc->own_comp_id, &clslot); ipa_client_conn_send_rspro(bc->bankd_conn, pdu); @@ -307,12 +307,14 @@ static void srvc_st_init_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state) if (!srvc->conn) { fprintf(stderr, "Unable to create socket: %s\n", strerror(errno)); /* FIXME */ + OSMO_ASSERT(0); } /* Attempt to connect TCP socket */ rc = ipa_client_conn_open(srvc->conn); if (rc < 0) { fprintf(stderr, "Unable to connect: %s\n", strerror(errno)); /* FIXME */ + OSMO_ASSERT(0); } } diff --git a/src/rspro_util.c b/src/rspro_util.c index 002bd81..67d4ac0 100644 --- a/src/rspro_util.c +++ b/src/rspro_util.c @@ -43,6 +43,7 @@ struct msgb *rspro_enc_msg(RsproPDU_t *pdu) return NULL; } msgb_put(msg, rval.encoded); + printf("encoded %s\n", msgb_hexdump(msg)); ASN_STRUCT_FREE(asn_DEF_RsproPDU, pdu); @@ -128,8 +129,8 @@ RsproPDU_t *rspro_gen_ConnectClientRes(const struct app_comp_id *a_cid, e_Result RsproPDU_t *pdu = CALLOC(1, sizeof(*pdu)); if (!pdu) return NULL; - pdu->version = 2; - pdu->tag = 2342; + //pdu->version = 2; + //pdu->tag = 2342; pdu->msg.present = RsproPDUchoice_PR_connectClientRes; fill_comp_id(&pdu->msg.choice.connectClientRes.identity, a_cid); pdu->msg.choice.connectClientRes.result = res; @@ -161,15 +162,13 @@ RsproPDU_t *rspro_gen_ConfigClientReq(const ClientSlot_t *client, uint32_t ip, u return pdu; } -RsproPDU_t *rspro_gen_SetAtrReq(uint16_t client_id, uint16_t slot_nr, const uint8_t *atr, - unsigned int atr_len) +RsproPDU_t *rspro_gen_SetAtrReq(const ClientSlot_t *client, const uint8_t *atr, unsigned int atr_len) { RsproPDU_t *pdu = CALLOC(1, sizeof(*pdu)); if (!pdu) return NULL; pdu->msg.present = RsproPDUchoice_PR_setAtrReq; - pdu->msg.choice.setAtrReq.slot.clientId = client_id; - pdu->msg.choice.setAtrReq.slot.slotNr = slot_nr; + pdu->msg.choice.setAtrReq.slot = *client; OCTET_STRING_fromBuf(&pdu->msg.choice.setAtrReq.atr, (const char *)atr, atr_len); return pdu; diff --git a/src/rspro_util.h b/src/rspro_util.h index 5411a48..feef77d 100644 --- a/src/rspro_util.h +++ b/src/rspro_util.h @@ -26,8 +26,7 @@ RsproPDU_t *rspro_gen_ConnectClientReq(const struct app_comp_id *a_cid, const Cl RsproPDU_t *rspro_gen_ConnectClientRes(const struct app_comp_id *a_cid, e_ResultCode res); RsproPDU_t *rspro_gen_CreateMappingReq(const ClientSlot_t *client, const BankSlot_t *bank); RsproPDU_t *rspro_gen_ConfigClientReq(const ClientSlot_t *client, uint32_t ip, uint16_t port); -RsproPDU_t *rspro_gen_SetAtrReq(uint16_t client_id, uint16_t slot_nr, const uint8_t *atr, - unsigned int atr_len); +RsproPDU_t *rspro_gen_SetAtrReq(const ClientSlot_t *client, const uint8_t *atr, unsigned int atr_len); RsproPDU_t *rspro_gen_TpduModem2Card(const ClientSlot_t *client, const BankSlot_t *bank, const uint8_t *tpdu, unsigned int tpdu_len); RsproPDU_t *rspro_gen_TpduCard2Modem(const BankSlot_t *bank, const ClientSlot_t *client,