bluez/sap/server.c
Arek Lichwa de16dfe912 Fix memory leak in sap plugin
Without this patch, when dropping RFCOMM channel to sap server, the
component's channel cleanup process doesn't release assigned GIOChannel
resources since conn->io is set to NULL before calling sap_conn_remove
(which would normally do the g_io_channel_unref).
2011-07-28 10:48:44 +03:00

1426 lines
32 KiB
C

/*
* BlueZ - Bluetooth protocol stack for Linux
*
* Copyright (C) 2010 Instituto Nokia de Tecnologia - INdT
* Copyright (C) 2010 ST-Ericsson SA
* Copyright (C) 2011 Tieto Poland
*
* Author: Marek Skowron <marek.skowron@tieto.com> for ST-Ericsson.
* Author: Waldemar Rymarkiewicz <waldemar.rymarkiewicz@tieto.com>
* for ST-Ericsson.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdio.h>
#include <errno.h>
#include <glib.h>
#include <netinet/in.h>
#include <bluetooth/bluetooth.h>
#include <bluetooth/sdp.h>
#include <bluetooth/sdp_lib.h>
#include "adapter.h"
#include "btio.h"
#include "sdpd.h"
#include "log.h"
#include "error.h"
#include "dbus-common.h"
#include "sap.h"
#include "server.h"
#define SAP_SERVER_INTERFACE "org.bluez.SimAccess"
#define SAP_UUID "0000112D-0000-1000-8000-00805F9B34FB"
#define SAP_SERVER_CHANNEL 8
#define PADDING4(x) ((4 - (x & 0x03)) & 0x03)
#define PARAMETER_SIZE(x) (sizeof(struct sap_parameter) + x + PADDING4(x))
#define SAP_NO_REQ 0xFF
#define SAP_TIMER_GRACEFUL_DISCONNECT 30
#define SAP_TIMER_NO_ACTIVITY 30
enum {
SAP_STATE_DISCONNECTED,
SAP_STATE_CONNECT_IN_PROGRESS,
SAP_STATE_CONNECTED,
SAP_STATE_GRACEFUL_DISCONNECT,
SAP_STATE_IMMEDIATE_DISCONNECT,
SAP_STATE_CLIENT_DISCONNECT
};
struct sap_connection {
GIOChannel *io;
uint32_t state;
uint8_t processing_req;
guint timer_id;
};
struct sap_server {
bdaddr_t src;
char *path;
uint32_t record_id;
GIOChannel *listen_io;
struct sap_connection *conn;
};
static DBusConnection *connection;
static struct sap_server *server;
static void start_guard_timer(struct sap_connection *conn, guint interval);
static void stop_guard_timer(struct sap_connection *conn);
static gboolean guard_timeout(gpointer data);
static size_t add_result_parameter(uint8_t result,
struct sap_parameter *param)
{
param->id = SAP_PARAM_ID_RESULT_CODE;
param->len = htons(SAP_PARAM_ID_RESULT_CODE_LEN);
*param->val = result;
return PARAMETER_SIZE(SAP_PARAM_ID_RESULT_CODE_LEN);
}
static int is_power_sim_off_req_allowed(uint8_t processing_req)
{
switch (processing_req) {
case SAP_NO_REQ:
case SAP_TRANSFER_APDU_REQ:
case SAP_TRANSFER_ATR_REQ:
case SAP_POWER_SIM_ON_REQ:
case SAP_RESET_SIM_REQ:
case SAP_TRANSFER_CARD_READER_STATUS_REQ:
return 1;
default:
return 0;
}
}
static int is_reset_sim_req_allowed(uint8_t processing_req)
{
switch (processing_req) {
case SAP_NO_REQ:
case SAP_TRANSFER_APDU_REQ:
case SAP_TRANSFER_ATR_REQ:
case SAP_TRANSFER_CARD_READER_STATUS_REQ:
return 1;
default:
return 0;
}
}
static int check_msg(struct sap_message *msg)
{
if (!msg)
return -EINVAL;
switch (msg->id) {
case SAP_CONNECT_REQ:
if (msg->nparam != 0x01)
return -EBADMSG;
if (msg->param->id != SAP_PARAM_ID_MAX_MSG_SIZE)
return -EBADMSG;
if (ntohs(msg->param->len) != SAP_PARAM_ID_MAX_MSG_SIZE_LEN)
return -EBADMSG;
break;
case SAP_TRANSFER_APDU_REQ:
if (msg->nparam != 0x01)
return -EBADMSG;
if (msg->param->id != SAP_PARAM_ID_COMMAND_APDU)
if ( msg->param->id != SAP_PARAM_ID_COMMAND_APDU7816)
return -EBADMSG;
if (msg->param->len == 0x00)
return -EBADMSG;
break;
case SAP_SET_TRANSPORT_PROTOCOL_REQ:
if (msg->nparam != 0x01)
return -EBADMSG;
if (msg->param->id != SAP_PARAM_ID_TRANSPORT_PROTOCOL)
return -EBADMSG;
if (ntohs(msg->param->len) != SAP_PARAM_ID_TRANSPORT_PROTO_LEN)
return -EBADMSG;
if (*msg->param->val != SAP_TRANSPORT_PROTOCOL_T0)
if (*msg->param->val != SAP_TRANSPORT_PROTOCOL_T1)
return -EBADMSG;
break;
case SAP_DISCONNECT_REQ:
case SAP_TRANSFER_ATR_REQ:
case SAP_POWER_SIM_OFF_REQ:
case SAP_POWER_SIM_ON_REQ:
case SAP_RESET_SIM_REQ:
case SAP_TRANSFER_CARD_READER_STATUS_REQ:
if (msg->nparam != 0x00)
return -EBADMSG;
break;
}
return 0;
}
static sdp_record_t *create_sap_record(uint8_t channel)
{
sdp_list_t *apseq, *aproto, *profiles, *proto[2], *root, *svclass_id;
uuid_t sap_uuid, gt_uuid, root_uuid, l2cap, rfcomm;
sdp_profile_desc_t profile;
sdp_record_t *record;
sdp_data_t *ch;
record = sdp_record_alloc();
if (!record)
return NULL;
sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
root = sdp_list_append(NULL, &root_uuid);
sdp_set_browse_groups(record, root);
sdp_list_free(root, NULL);
sdp_uuid16_create(&sap_uuid, SAP_SVCLASS_ID);
svclass_id = sdp_list_append(NULL, &sap_uuid);
sdp_uuid16_create(&gt_uuid, GENERIC_TELEPHONY_SVCLASS_ID);
svclass_id = sdp_list_append(svclass_id, &gt_uuid);
sdp_set_service_classes(record, svclass_id);
sdp_list_free(svclass_id, NULL);
sdp_uuid16_create(&profile.uuid, SAP_PROFILE_ID);
profile.version = SAP_VERSION;
profiles = sdp_list_append(NULL, &profile);
sdp_set_profile_descs(record, profiles);
sdp_list_free(profiles, NULL);
sdp_uuid16_create(&l2cap, L2CAP_UUID);
proto[0] = sdp_list_append(NULL, &l2cap);
apseq = sdp_list_append(NULL, proto[0]);
sdp_uuid16_create(&rfcomm, RFCOMM_UUID);
proto[1] = sdp_list_append(NULL, &rfcomm);
ch = sdp_data_alloc(SDP_UINT8, &channel);
proto[1] = sdp_list_append(proto[1], ch);
apseq = sdp_list_append(apseq, proto[1]);
aproto = sdp_list_append(NULL, apseq);
sdp_set_access_protos(record, aproto);
sdp_set_info_attr(record, "SIM Access Server",
NULL, NULL);
sdp_data_free(ch);
sdp_list_free(proto[0], NULL);
sdp_list_free(proto[1], NULL);
sdp_list_free(apseq, NULL);
sdp_list_free(aproto, NULL);
return record;
}
static int send_message(struct sap_connection *conn, void *buf, size_t size)
{
size_t written = 0;
GError *gerr = NULL;
GIOStatus gstatus;
if (!conn || !buf)
return -EINVAL;
DBG("conn %p, size %zu", conn, size);
gstatus = g_io_channel_write_chars(conn->io, buf, size, &written,
&gerr);
if (gstatus != G_IO_STATUS_NORMAL) {
if (gerr)
g_error_free(gerr);
error("write error (0x%02x).", gstatus);
return -EIO;
}
if (written != size) {
error("written %zu bytes out of %zu", written, size);
return -EIO;
}
return written;
}
static int disconnect_ind(void *sap_device, uint8_t disc_type)
{
struct sap_connection *conn = sap_device;
char buf[SAP_BUF_SIZE];
struct sap_message *msg = (struct sap_message *) buf;
struct sap_parameter *param = (struct sap_parameter *) msg->param;
size_t size = sizeof(struct sap_message);
if (!conn)
return -EINVAL;
DBG("data %p state %d disc_type 0x%02x", conn, conn->state, disc_type);
if (conn->state != SAP_STATE_GRACEFUL_DISCONNECT &&
conn->state != SAP_STATE_IMMEDIATE_DISCONNECT) {
error("Processing error (state %d pr 0x%02x)", conn->state,
conn->processing_req);
return -EPERM;
}
memset(buf, 0, sizeof(buf));
msg->id = SAP_DISCONNECT_IND;
msg->nparam = 0x01;
/* Add disconnection type param. */
param->id = SAP_PARAM_ID_DISCONNECT_IND;
param->len = htons(SAP_PARAM_ID_DISCONNECT_IND_LEN);
*param->val = disc_type;
size += PARAMETER_SIZE(SAP_PARAM_ID_DISCONNECT_IND_LEN);
return send_message(sap_device, buf, size);
}
static void connect_req(struct sap_connection *conn,
struct sap_parameter *param)
{
uint16_t maxmsgsize, *val;
DBG("conn %p state %d", conn, conn->state);
if (!param)
goto error_rsp;
if (conn->state != SAP_STATE_DISCONNECTED)
goto error_rsp;
stop_guard_timer(conn);
val = (uint16_t *) &param->val;
maxmsgsize = ntohs(*val);
DBG("Connect MaxMsgSize: 0x%04x", maxmsgsize);
conn->state = SAP_STATE_CONNECT_IN_PROGRESS;
if (maxmsgsize <= SAP_BUF_SIZE) {
conn->processing_req = SAP_CONNECT_REQ;
sap_connect_req(conn, maxmsgsize);
} else {
sap_connect_rsp(conn, SAP_STATUS_MAX_MSG_SIZE_NOT_SUPPORTED,
SAP_BUF_SIZE);
}
return;
error_rsp:
sap_error_rsp(conn);
}
static int disconnect_req(struct sap_connection *conn, uint8_t disc_type)
{
DBG("conn %p state %d disc_type 0x%02x", conn, conn->state, disc_type);
switch (disc_type) {
case SAP_DISCONNECTION_TYPE_GRACEFUL:
if (conn->state == SAP_STATE_DISCONNECTED ||
conn->state == SAP_STATE_CONNECT_IN_PROGRESS)
return -EPERM;
if (conn->state == SAP_STATE_CONNECTED) {
conn->state = SAP_STATE_GRACEFUL_DISCONNECT;
conn->processing_req = SAP_NO_REQ;
disconnect_ind(conn, disc_type);
/* Timer will disconnect if client won't do.*/
start_guard_timer(conn, SAP_TIMER_GRACEFUL_DISCONNECT);
}
return 0;
case SAP_DISCONNECTION_TYPE_IMMEDIATE:
if (conn->state == SAP_STATE_DISCONNECTED ||
conn->state == SAP_STATE_CONNECT_IN_PROGRESS)
return -EPERM;
if (conn->state == SAP_STATE_CONNECTED ||
conn->state == SAP_STATE_GRACEFUL_DISCONNECT) {
conn->state = SAP_STATE_IMMEDIATE_DISCONNECT;
conn->processing_req = SAP_NO_REQ;
stop_guard_timer(conn);
disconnect_ind(conn, disc_type);
sap_disconnect_req(conn, 0);
}
return 0;
case SAP_DISCONNECTION_TYPE_CLIENT:
if (conn->state != SAP_STATE_CONNECTED &&
conn->state != SAP_STATE_GRACEFUL_DISCONNECT) {
sap_error_rsp(conn);
return -EPERM;
}
conn->state = SAP_STATE_CLIENT_DISCONNECT;
conn->processing_req = SAP_NO_REQ;
stop_guard_timer(conn);
sap_disconnect_req(conn, 0);
return 0;
default:
error("Unknown disconnection type (0x%02x).", disc_type);
return -EINVAL;
}
}
static void transfer_apdu_req(struct sap_connection *conn,
struct sap_parameter *param)
{
DBG("conn %p state %d", conn, conn->state);
if (!param)
goto error_rsp;
param->len = ntohs(param->len);
if (conn->state != SAP_STATE_CONNECTED &&
conn->state != SAP_STATE_GRACEFUL_DISCONNECT)
goto error_rsp;
if (conn->processing_req != SAP_NO_REQ)
goto error_rsp;
conn->processing_req = SAP_TRANSFER_APDU_REQ;
sap_transfer_apdu_req(conn, param);
return;
error_rsp:
sap_error_rsp(conn);
}
static void transfer_atr_req(struct sap_connection *conn)
{
DBG("conn %p state %d", conn, conn->state);
if (conn->state != SAP_STATE_CONNECTED)
goto error_rsp;
if (conn->processing_req != SAP_NO_REQ)
goto error_rsp;
conn->processing_req = SAP_TRANSFER_ATR_REQ;
sap_transfer_atr_req(conn);
return;
error_rsp:
sap_error_rsp(conn);
}
static void power_sim_off_req(struct sap_connection *conn)
{
DBG("conn %p state %d", conn, conn->state);
if (conn->state != SAP_STATE_CONNECTED)
goto error_rsp;
if (!is_power_sim_off_req_allowed(conn->processing_req))
goto error_rsp;
conn->processing_req = SAP_POWER_SIM_OFF_REQ;
sap_power_sim_off_req(conn);
return;
error_rsp:
sap_error_rsp(conn);
}
static void power_sim_on_req(struct sap_connection *conn)
{
DBG("conn %p state %d", conn, conn->state);
if (conn->state != SAP_STATE_CONNECTED)
goto error_rsp;
if (conn->processing_req != SAP_NO_REQ)
goto error_rsp;
conn->processing_req = SAP_POWER_SIM_ON_REQ;
sap_power_sim_on_req(conn);
return;
error_rsp:
sap_error_rsp(conn);
}
static void reset_sim_req(struct sap_connection *conn)
{
DBG("conn %p state %d", conn, conn->state);
if (conn->state != SAP_STATE_CONNECTED)
goto error_rsp;
if (!is_reset_sim_req_allowed(conn->processing_req))
goto error_rsp;
conn->processing_req = SAP_RESET_SIM_REQ;
sap_reset_sim_req(conn);
return;
error_rsp:
sap_error_rsp(conn);
}
static void transfer_card_reader_status_req(struct sap_connection *conn)
{
DBG("conn %p state %d", conn, conn->state);
if (conn->state != SAP_STATE_CONNECTED)
goto error_rsp;
if (conn->processing_req != SAP_NO_REQ)
goto error_rsp;
conn->processing_req = SAP_TRANSFER_CARD_READER_STATUS_REQ;
sap_transfer_card_reader_status_req(conn);
return;
error_rsp:
sap_error_rsp(conn);
}
static void set_transport_protocol_req(struct sap_connection *conn,
struct sap_parameter *param)
{
if (!param)
goto error_rsp;
DBG("conn %p state %d param %p", conn, conn->state, param);
if (conn->state != SAP_STATE_CONNECTED)
goto error_rsp;
if (conn->processing_req != SAP_NO_REQ)
goto error_rsp;
conn->processing_req = SAP_SET_TRANSPORT_PROTOCOL_REQ;
sap_set_transport_protocol_req(conn, param);
return;
error_rsp:
sap_error_rsp(conn);
}
static void start_guard_timer(struct sap_connection *conn, guint interval)
{
if (!conn)
return;
if (!conn->timer_id)
conn->timer_id = g_timeout_add_seconds(interval, guard_timeout,
conn);
else
error("Timer is already active.");
}
static void stop_guard_timer(struct sap_connection *conn)
{
if (conn && conn->timer_id) {
g_source_remove(conn->timer_id);
conn->timer_id = 0;
}
}
static gboolean guard_timeout(gpointer data)
{
struct sap_connection *conn = data;
if (!conn)
return FALSE;
DBG("conn %p state %d pr 0x%02x", conn, conn->state,
conn->processing_req);
conn->timer_id = 0;
switch (conn->state) {
case SAP_STATE_DISCONNECTED:
/* Client opened RFCOMM channel but didn't send CONNECT_REQ,
* in fixed time or client disconnected SAP connection but
* didn't closed RFCOMM channel in fixed time.*/
if (conn->io) {
g_io_channel_shutdown(conn->io, TRUE, NULL);
g_io_channel_unref(conn->io);
}
break;
case SAP_STATE_GRACEFUL_DISCONNECT:
/* Client didn't disconnect SAP connection in fixed time,
* so close SAP connection immediately. */
disconnect_req(conn, SAP_DISCONNECTION_TYPE_IMMEDIATE);
break;
default:
error("Unexpected state (%d).", conn->state);
break;
}
return FALSE;
}
int sap_connect_rsp(void *sap_device, uint8_t status, uint16_t maxmsgsize)
{
struct sap_connection *conn = sap_device;
char buf[SAP_BUF_SIZE];
struct sap_message *msg = (struct sap_message *) buf;
struct sap_parameter *param = (struct sap_parameter *) msg->param;
size_t size = sizeof(struct sap_message);
if (!conn)
return -EINVAL;
DBG("state %d pr 0x%02x status 0x%02x", conn->state,
conn->processing_req, status);
if (conn->state != SAP_STATE_CONNECT_IN_PROGRESS)
return -EPERM;
memset(buf, 0, sizeof(buf));
msg->id = SAP_CONNECT_RESP;
msg->nparam = 0x01;
/* Add connection status */
param->id = SAP_PARAM_ID_CONN_STATUS;
param->len = htons(SAP_PARAM_ID_CONN_STATUS_LEN);
*param->val = status;
size += PARAMETER_SIZE(SAP_PARAM_ID_CONN_STATUS_LEN);
/* Add MaxMsgSize */
if (maxmsgsize && (status == SAP_STATUS_MAX_MSG_SIZE_NOT_SUPPORTED ||
status == SAP_STATUS_MAX_MSG_SIZE_TOO_SMALL)) {
uint16_t *len;
msg->nparam++;
param = (struct sap_parameter *) &buf[size];
param->id = SAP_PARAM_ID_MAX_MSG_SIZE;
param->len = htons(SAP_PARAM_ID_MAX_MSG_SIZE_LEN);
len = (uint16_t *) &param->val;
*len = htons(maxmsgsize);
size += PARAMETER_SIZE(SAP_PARAM_ID_MAX_MSG_SIZE_LEN);
}
if (status == SAP_STATUS_OK) {
gboolean connected = TRUE;
emit_property_changed(connection, server->path,
SAP_SERVER_INTERFACE,
"Connected", DBUS_TYPE_BOOLEAN, &connected);
conn->state = SAP_STATE_CONNECTED;
} else {
conn->state = SAP_STATE_DISCONNECTED;
/* Timer will shutdown channel if client doesn't send
* CONNECT_REQ or doesn't shutdown channel itself.*/
start_guard_timer(conn, SAP_TIMER_NO_ACTIVITY);
}
conn->processing_req = SAP_NO_REQ;
return send_message(sap_device, buf, size);
}
int sap_disconnect_rsp(void *sap_device)
{
struct sap_connection *conn = sap_device;
struct sap_message msg;
if (!conn)
return -EINVAL;
DBG("state %d pr 0x%02x", conn->state, conn->processing_req);
switch (conn->state) {
case SAP_STATE_CLIENT_DISCONNECT:
memset(&msg, 0, sizeof(msg));
msg.id = SAP_DISCONNECT_RESP;
conn->state = SAP_STATE_DISCONNECTED;
conn->processing_req = SAP_NO_REQ;
/* Timer will close channel if client doesn't do it.*/
start_guard_timer(conn, SAP_TIMER_NO_ACTIVITY);
return send_message(sap_device, &msg, sizeof(msg));
case SAP_STATE_IMMEDIATE_DISCONNECT:
conn->state = SAP_STATE_DISCONNECTED;
conn->processing_req = SAP_NO_REQ;
if (conn->io) {
g_io_channel_shutdown(conn->io, TRUE, NULL);
g_io_channel_unref(conn->io);
}
return 0;
default:
break;
}
return 0;
}
int sap_transfer_apdu_rsp(void *sap_device, uint8_t result, uint8_t *apdu,
uint16_t length)
{
struct sap_connection *conn = sap_device;
char buf[SAP_BUF_SIZE];
struct sap_message *msg = (struct sap_message *) buf;
struct sap_parameter *param = (struct sap_parameter *) msg->param;
size_t size = sizeof(struct sap_message);
if (!conn)
return -EINVAL;
DBG("state %d pr 0x%02x", conn->state, conn->processing_req);
if (conn->processing_req != SAP_TRANSFER_APDU_REQ)
return 0;
if (result == SAP_RESULT_OK && (!apdu || (apdu && length == 0x00)))
return -EINVAL;
memset(buf, 0, sizeof(buf));
msg->id = SAP_TRANSFER_APDU_RESP;
msg->nparam = 0x01;
size += add_result_parameter(result, param);
/* Add APDU response. */
if (result == SAP_RESULT_OK) {
msg->nparam++;
param = (struct sap_parameter *) &buf[size];
param->id = SAP_PARAM_ID_RESPONSE_APDU;
param->len = htons(length);
size += PARAMETER_SIZE(length);
if (size > SAP_BUF_SIZE)
return -EOVERFLOW;
memcpy(param->val, apdu, length);
}
conn->processing_req = SAP_NO_REQ;
return send_message(sap_device, buf, size);
}
int sap_transfer_atr_rsp(void *sap_device, uint8_t result, uint8_t *atr,
uint16_t length)
{
struct sap_connection *conn = sap_device;
char buf[SAP_BUF_SIZE];
struct sap_message *msg = (struct sap_message *) buf;
struct sap_parameter *param = (struct sap_parameter *) msg->param;
size_t size = sizeof(struct sap_message);
if (!conn)
return -EINVAL;
DBG("result 0x%02x state %d pr 0x%02x len %d", result, conn->state,
conn->processing_req, length);
if (conn->processing_req != SAP_TRANSFER_ATR_REQ)
return 0;
if (result == SAP_RESULT_OK && (!atr || (atr && length == 0x00)))
return -EINVAL;
memset(buf, 0, sizeof(buf));
msg->id = SAP_TRANSFER_ATR_RESP;
msg->nparam = 0x01;
size += add_result_parameter(result, param);
/* Add ATR response */
if (result == SAP_RESULT_OK) {
msg->nparam++;
param = (struct sap_parameter *) &buf[size];
param->id = SAP_PARAM_ID_ATR;
param->len = htons(length);
size += PARAMETER_SIZE(length);
if (size > SAP_BUF_SIZE)
return -EOVERFLOW;
memcpy(param->val, atr, length);
}
conn->processing_req = SAP_NO_REQ;
return send_message(sap_device, buf, size);
}
int sap_power_sim_off_rsp(void *sap_device, uint8_t result)
{
struct sap_connection *conn = sap_device;
char buf[SAP_BUF_SIZE];
struct sap_message *msg = (struct sap_message *) buf;
size_t size = sizeof(struct sap_message);
if (!conn)
return -EINVAL;
DBG("state %d pr 0x%02x", conn->state, conn->processing_req);
if (conn->processing_req != SAP_POWER_SIM_OFF_REQ)
return 0;
memset(buf, 0, sizeof(buf));
msg->id = SAP_POWER_SIM_OFF_RESP;
msg->nparam = 0x01;
size += add_result_parameter(result, msg->param);
conn->processing_req = SAP_NO_REQ;
return send_message(sap_device, buf, size);
}
int sap_power_sim_on_rsp(void *sap_device, uint8_t result)
{
struct sap_connection *conn = sap_device;
char buf[SAP_BUF_SIZE];
struct sap_message *msg = (struct sap_message *) buf;
size_t size = sizeof(struct sap_message);
if (!conn)
return -EINVAL;
DBG("state %d pr 0x%02x", conn->state, conn->processing_req);
if (conn->processing_req != SAP_POWER_SIM_ON_REQ)
return 0;
memset(buf, 0, sizeof(buf));
msg->id = SAP_POWER_SIM_ON_RESP;
msg->nparam = 0x01;
size += add_result_parameter(result, msg->param);
conn->processing_req = SAP_NO_REQ;
return send_message(sap_device, buf, size);
}
int sap_reset_sim_rsp(void *sap_device, uint8_t result)
{
struct sap_connection *conn = sap_device;
char buf[SAP_BUF_SIZE];
struct sap_message *msg = (struct sap_message *) buf;
size_t size = sizeof(struct sap_message);
if (!conn)
return -EINVAL;
DBG("state %d pr 0x%02x result 0x%02x", conn->state,
conn->processing_req, result);
if (conn->processing_req != SAP_RESET_SIM_REQ)
return 0;
memset(buf, 0, sizeof(buf));
msg->id = SAP_RESET_SIM_RESP;
msg->nparam = 0x01;
size += add_result_parameter(result, msg->param);
conn->processing_req = SAP_NO_REQ;
return send_message(sap_device, buf, size);
}
int sap_transfer_card_reader_status_rsp(void *sap_device, uint8_t result,
uint8_t status)
{
struct sap_connection *conn = sap_device;
char buf[SAP_BUF_SIZE];
struct sap_message *msg = (struct sap_message *) buf;
struct sap_parameter *param = (struct sap_parameter *) msg->param;
size_t size = sizeof(struct sap_message);
if (!conn)
return -EINVAL;
DBG("state %d pr 0x%02x result 0x%02x", conn->state,
conn->processing_req, result);
if (conn->processing_req != SAP_TRANSFER_CARD_READER_STATUS_REQ)
return 0;
memset(buf, 0, sizeof(buf));
msg->id = SAP_TRANSFER_CARD_READER_STATUS_RESP;
msg->nparam = 0x01;
size += add_result_parameter(result, param);
/* Add card reader status. */
if (result == SAP_RESULT_OK) {
msg->nparam++;
param = (struct sap_parameter *) &buf[size];
param->id = SAP_PARAM_ID_CARD_READER_STATUS;
param->len = htons(SAP_PARAM_ID_CARD_READER_STATUS_LEN);
*param->val = status;
size += PARAMETER_SIZE(SAP_PARAM_ID_CARD_READER_STATUS_LEN);
}
conn->processing_req = SAP_NO_REQ;
return send_message(sap_device, buf, size);
}
int sap_transport_protocol_rsp(void *sap_device, uint8_t result)
{
struct sap_connection *conn = sap_device;
char buf[SAP_BUF_SIZE];
struct sap_message *msg = (struct sap_message *) buf;
size_t size = sizeof(struct sap_message);
if (!conn)
return -EINVAL;
DBG("state %d pr 0x%02x result 0x%02x", conn->state,
conn->processing_req, result);
if (conn->processing_req != SAP_SET_TRANSPORT_PROTOCOL_REQ)
return 0;
memset(buf, 0, sizeof(buf));
msg->id = SAP_SET_TRANSPORT_PROTOCOL_RESP;
msg->nparam = 0x01;
size += add_result_parameter(result, msg->param);
conn->processing_req = SAP_NO_REQ;
return send_message(sap_device, buf, size);
}
int sap_error_rsp(void *sap_device)
{
struct sap_message msg;
struct sap_connection *conn = sap_device;
memset(&msg, 0, sizeof(msg));
msg.id = SAP_ERROR_RESP;
error("SAP error (state %d pr 0x%02x).", conn->state,
conn->processing_req);
return send_message(conn, &msg, sizeof(msg));
}
int sap_status_ind(void *sap_device, uint8_t status_change)
{
struct sap_connection *conn = sap_device;
char buf[SAP_BUF_SIZE];
struct sap_message *msg = (struct sap_message *) buf;
struct sap_parameter *param = (struct sap_parameter *) msg->param;
size_t size = sizeof(struct sap_message);
if (!conn)
return -EINVAL;
DBG("state %d pr 0x%02x sc 0x%02x", conn->state, conn->processing_req,
status_change);
if (conn->state != SAP_STATE_CONNECTED &&
conn->state != SAP_STATE_GRACEFUL_DISCONNECT)
return 0;
memset(buf, 0, sizeof(buf));
msg->id = SAP_STATUS_IND;
msg->nparam = 0x01;
/* Add status change. */
param->id = SAP_PARAM_ID_STATUS_CHANGE;
param->len = htons(SAP_PARAM_ID_STATUS_CHANGE_LEN);
*param->val = status_change;
size += PARAMETER_SIZE(SAP_PARAM_ID_STATUS_CHANGE_LEN);
return send_message(sap_device, buf, size);
}
int sap_disconnect_ind(void *sap_device, uint8_t disc_type)
{
struct sap_connection *conn = sap_device;
return disconnect_req(conn, SAP_DISCONNECTION_TYPE_IMMEDIATE);
}
static int handle_cmd(struct sap_connection *conn, void *buf, size_t size)
{
struct sap_message *msg = buf;
if (!conn)
return -EINVAL;
if (size < sizeof(struct sap_message))
goto error_rsp;
if (msg->nparam != 0 && size < (sizeof(struct sap_message) +
sizeof(struct sap_parameter) + 4))
goto error_rsp;
if (check_msg(msg) < 0)
goto error_rsp;
switch (msg->id) {
case SAP_CONNECT_REQ:
connect_req(conn, msg->param);
return 0;
case SAP_DISCONNECT_REQ:
disconnect_req(conn, SAP_DISCONNECTION_TYPE_CLIENT);
return 0;
case SAP_TRANSFER_APDU_REQ:
transfer_apdu_req(conn, msg->param);
return 0;
case SAP_TRANSFER_ATR_REQ:
transfer_atr_req(conn);
return 0;
case SAP_POWER_SIM_OFF_REQ:
power_sim_off_req(conn);
return 0;
case SAP_POWER_SIM_ON_REQ:
power_sim_on_req(conn);
return 0;
case SAP_RESET_SIM_REQ:
reset_sim_req(conn);
return 0;
case SAP_TRANSFER_CARD_READER_STATUS_REQ:
transfer_card_reader_status_req(conn);
return 0;
case SAP_SET_TRANSPORT_PROTOCOL_REQ:
set_transport_protocol_req(conn, msg->param);
return 0;
default:
DBG("Unknown SAP message id 0x%02x.", msg->id);
break;
}
error_rsp:
DBG("Invalid SAP message format.");
sap_error_rsp(conn);
return -EBADMSG;
}
static void sap_conn_remove(struct sap_connection *conn)
{
DBG("conn %p", conn);
if (!conn)
return;
if (conn->io) {
g_io_channel_shutdown(conn->io, TRUE, NULL);
g_io_channel_unref(conn->io);
}
conn->io = NULL;
g_free(conn);
server->conn = NULL;
}
static gboolean sap_io_cb(GIOChannel *io, GIOCondition cond, gpointer data)
{
struct sap_connection *conn = data;
char buf[SAP_BUF_SIZE];
size_t bytes_read = 0;
GError *gerr = NULL;
GIOStatus gstatus;
DBG("conn %p io %p", conn, io);
if (cond & G_IO_NVAL) {
DBG("ERR (G_IO_NVAL) on rfcomm socket.");
return FALSE;
}
if (cond & G_IO_ERR) {
DBG("ERR (G_IO_ERR) on rfcomm socket.");
return FALSE;
}
if (cond & G_IO_HUP) {
DBG("HUP on rfcomm socket.");
return FALSE;
}
gstatus = g_io_channel_read_chars(io, buf, sizeof(buf) - 1,
&bytes_read, &gerr);
if (gstatus != G_IO_STATUS_NORMAL) {
if (gerr)
g_error_free(gerr);
return TRUE;
}
if (handle_cmd(conn, buf, bytes_read) < 0)
error("SAP protocol processing failure.");
return TRUE;
}
static void sap_io_destroy(void *data)
{
struct sap_connection *conn = data;
DBG("conn %p", conn);
if (conn && conn->io) {
gboolean connected = FALSE;
stop_guard_timer(conn);
if (conn->state != SAP_STATE_CONNECT_IN_PROGRESS)
emit_property_changed(connection, server->path,
SAP_SERVER_INTERFACE, "Connected",
DBUS_TYPE_BOOLEAN, &connected);
if (conn->state == SAP_STATE_CONNECT_IN_PROGRESS ||
conn->state == SAP_STATE_CONNECTED ||
conn->state == SAP_STATE_GRACEFUL_DISCONNECT)
sap_disconnect_req(NULL, 1);
sap_conn_remove(conn);
}
}
static void sap_connect_cb(GIOChannel *io, GError *gerr, gpointer data)
{
struct sap_connection *conn = data;
DBG("conn %p, io %p", conn, io);
if (!conn)
return;
/* Timer will shutdown the channel in case of lack of client
activity */
start_guard_timer(conn, SAP_TIMER_NO_ACTIVITY);
g_io_add_watch_full(io, G_PRIORITY_DEFAULT,
G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
sap_io_cb, conn, sap_io_destroy);
}
static void connect_auth_cb(DBusError *derr, void *data)
{
struct sap_connection *conn = data;
GError *gerr = NULL;
DBG("conn %p", conn);
if (!conn)
return;
if (derr && dbus_error_is_set(derr)) {
error("Access has been denied (%s)", derr->message);
sap_conn_remove(conn);
return;
}
if (!bt_io_accept(conn->io, sap_connect_cb, conn, NULL, &gerr)) {
error("bt_io_accept: %s", gerr->message);
g_error_free(gerr);
sap_conn_remove(conn);
return;
}
DBG("Access has been granted.");
}
static void connect_confirm_cb(GIOChannel *io, gpointer data)
{
struct sap_connection *conn = server->conn;
GError *gerr = NULL;
bdaddr_t src, dst;
char dstaddr[18];
int err;
DBG("conn %p io %p", conn, io);
if (!io)
return;
if (conn) {
DBG("Another SAP connection already exists.");
g_io_channel_shutdown(io, TRUE, NULL);
return;
}
conn = g_try_new0(struct sap_connection, 1);
if (!conn) {
error("Can't allocate memory for incomming SAP connection.");
g_io_channel_shutdown(io, TRUE, NULL);
return;
}
g_io_channel_set_encoding(io, NULL, NULL);
g_io_channel_set_buffered(io, FALSE);
server->conn = conn;
conn->io = g_io_channel_ref(io);
conn->state = SAP_STATE_DISCONNECTED;
bt_io_get(io, BT_IO_RFCOMM, &gerr,
BT_IO_OPT_SOURCE_BDADDR, &src,
BT_IO_OPT_DEST_BDADDR, &dst,
BT_IO_OPT_INVALID);
if (gerr) {
error("%s", gerr->message);
g_error_free(gerr);
sap_conn_remove(conn);
return;
}
ba2str(&dst, dstaddr);
err = btd_request_authorization(&src, &dst, SAP_UUID, connect_auth_cb,
conn);
if (err < 0) {
error("Authorization failure (err %d)", err);
sap_conn_remove(conn);
return;
}
DBG("Authorizing incomming SAP connection from %s", dstaddr);
}
static inline DBusMessage *message_failed(DBusMessage *msg,
const char *description)
{
return g_dbus_create_error(msg, ERROR_INTERFACE ".Failed", "%s",
description);
}
static DBusMessage *disconnect(DBusConnection *conn, DBusMessage *msg,
void *data)
{
struct sap_server *server = data;
if (!server)
return message_failed(msg, "Server internal error.");
DBG("conn %p", server->conn);
if (!server->conn)
return message_failed(msg, "Client already disconnected");
if (disconnect_req(server->conn, SAP_DISCONNECTION_TYPE_GRACEFUL) < 0)
return g_dbus_create_error(msg, ERROR_INTERFACE ".Failed",
"There is no active connection");
return dbus_message_new_method_return(msg);
}
static DBusMessage *get_properties(DBusConnection *c,
DBusMessage *msg, void *data)
{
struct sap_connection *conn = data;
DBusMessage *reply;
DBusMessageIter iter;
DBusMessageIter dict;
dbus_bool_t connected;
if (!conn)
return message_failed(msg, "Server internal error.");
reply = dbus_message_new_method_return(msg);
if (!reply)
return NULL;
dbus_message_iter_init_append(reply, &iter);
dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
connected = (conn->state == SAP_STATE_CONNECTED ||
conn->state == SAP_STATE_GRACEFUL_DISCONNECT);
dict_append_entry(&dict, "Connected", DBUS_TYPE_BOOLEAN, &connected);
dbus_message_iter_close_container(&iter, &dict);
return reply;
}
static GDBusMethodTable server_methods[] = {
{"GetProperties", "", "a{sv}", get_properties},
{"Disconnect", "", "", disconnect},
{ }
};
static GDBusSignalTable server_signals[] = {
{ "PropertyChanged", "sv"},
{ }
};
static void server_free(struct sap_server *server)
{
if (!server)
return;
sap_conn_remove(server->conn);
g_free(server->path);
g_free(server);
server = NULL;
}
static void destroy_sap_interface(void *data)
{
struct sap_server *server = data;
DBG("Unregistered interface %s on path %s", SAP_SERVER_INTERFACE,
server->path);
server_free(server);
}
int sap_server_register(const char *path, bdaddr_t *src)
{
sdp_record_t *record = NULL;
GError *gerr = NULL;
GIOChannel *io;
if (sap_init() < 0) {
error("Sap driver initialization failed.");
return -1;
}
server = g_try_new0(struct sap_server, 1);
if (!server) {
sap_exit();
return -ENOMEM;
}
bacpy(&server->src, src);
server->path = g_strdup(path);
record = create_sap_record(SAP_SERVER_CHANNEL);
if (!record) {
error("Creating SAP SDP record failed.");
goto sdp_err;
}
if (add_record_to_server(&server->src, record) < 0) {
error("Adding SAP SDP record to the SDP server failed.");
sdp_record_free(record);
goto sdp_err;
}
server->record_id = record->handle;
io = bt_io_listen(BT_IO_RFCOMM, NULL, connect_confirm_cb, server,
NULL, &gerr,
BT_IO_OPT_SOURCE_BDADDR, &server->src,
BT_IO_OPT_CHANNEL, SAP_SERVER_CHANNEL,
BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_HIGH,
BT_IO_OPT_MASTER, TRUE,
BT_IO_OPT_INVALID);
if (!io) {
error("Can't listen at channel %d.", SAP_SERVER_CHANNEL);
g_error_free(gerr);
goto server_err;
}
DBG("Listen socket 0x%02x", g_io_channel_unix_get_fd(io));
server->listen_io = io;
server->conn = NULL;
if (!g_dbus_register_interface(connection, path, SAP_SERVER_INTERFACE,
server_methods, server_signals, NULL,
server, destroy_sap_interface)) {
error("D-Bus failed to register %s interface",
SAP_SERVER_INTERFACE);
goto server_err;
}
return 0;
server_err:
remove_record_from_server(server->record_id);
sdp_err:
server_free(server);
sap_exit();
return -1;
}
int sap_server_unregister(const char *path)
{
if (!server)
return -EINVAL;
remove_record_from_server(server->record_id);
if (server->conn)
sap_conn_remove(server->conn);
if (server->listen_io) {
g_io_channel_shutdown(server->listen_io, TRUE, NULL);
g_io_channel_unref(server->listen_io);
server->listen_io = NULL;
}
g_dbus_unregister_interface(connection, path, SAP_SERVER_INTERFACE);
sap_exit();
return 0;
}
int sap_server_init(DBusConnection *conn)
{
connection = dbus_connection_ref(conn);
return 0;
}
void sap_server_exit(void)
{
dbus_connection_unref(connection);
connection = NULL;
}