mirror of
git://git.gnupg.org/gnupg.git
synced 2024-12-22 10:19:57 +01:00
Add a tool to analyze the CCID protocol on the USB bus.
This commit is contained in:
parent
6763dd76e0
commit
5206a2deb3
@ -1,3 +1,7 @@
|
||||
2009-04-17 Werner Koch <wk@g10code.com>
|
||||
|
||||
* ccidmon.c: New.
|
||||
|
||||
2009-03-03 Werner Koch <wk@g10code.com>
|
||||
|
||||
* gpgconf.c: New command --reload.
|
||||
|
@ -19,7 +19,8 @@
|
||||
EXTRA_DIST = \
|
||||
Manifest watchgnupg.c \
|
||||
addgnupghome applygnupgdefaults gpgsm-gencert.sh \
|
||||
lspgpot mail-signed-keys convert-from-106 sockprox.c
|
||||
lspgpot mail-signed-keys convert-from-106 sockprox.c \
|
||||
ccidmon.c
|
||||
|
||||
|
||||
AM_CPPFLAGS = -I$(top_srcdir)/gl -I$(top_srcdir)/intl -I$(top_srcdir)/common
|
||||
|
789
tools/ccidmon.c
Normal file
789
tools/ccidmon.c
Normal file
@ -0,0 +1,789 @@
|
||||
/* ccidmon.c - CCID monitor for use with the Linux usbmon facility.
|
||||
* Copyright (C) 2009 Free Software Foundation, Inc.
|
||||
*
|
||||
* This file is part of GnuPG.
|
||||
*
|
||||
* GnuPG 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* GnuPG 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
/* This utility takes the output of usbmon, filters out the bulk data
|
||||
and prints the CCID messages in a human friendly way.
|
||||
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <stdarg.h>
|
||||
#include <assert.h>
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
|
||||
|
||||
#ifndef PACKAGE_VERSION
|
||||
# define PACKAGE_VERSION "[build on " __DATE__ " " __TIME__ "]"
|
||||
#endif
|
||||
#ifndef PACKAGE_BUGREPORT
|
||||
# define PACKAGE_BUGREPORT "devnull@example.org"
|
||||
#endif
|
||||
#define PGM "ccidmon"
|
||||
|
||||
/* Option flags. */
|
||||
static int verbose;
|
||||
static int debug;
|
||||
static int skip_escape;
|
||||
static int usb_bus, usb_dev;
|
||||
|
||||
/* Error counter. */
|
||||
static int any_error;
|
||||
|
||||
/* Data storage. */
|
||||
struct
|
||||
{
|
||||
int is_bi;
|
||||
char address[50];
|
||||
int count;
|
||||
char data[2000];
|
||||
} databuffer;
|
||||
|
||||
|
||||
enum {
|
||||
RDR_to_PC_NotifySlotChange= 0x50,
|
||||
RDR_to_PC_HardwareError = 0x51,
|
||||
|
||||
PC_to_RDR_SetParameters = 0x61,
|
||||
PC_to_RDR_IccPowerOn = 0x62,
|
||||
PC_to_RDR_IccPowerOff = 0x63,
|
||||
PC_to_RDR_GetSlotStatus = 0x65,
|
||||
PC_to_RDR_Secure = 0x69,
|
||||
PC_to_RDR_T0APDU = 0x6a,
|
||||
PC_to_RDR_Escape = 0x6b,
|
||||
PC_to_RDR_GetParameters = 0x6c,
|
||||
PC_to_RDR_ResetParameters = 0x6d,
|
||||
PC_to_RDR_IccClock = 0x6e,
|
||||
PC_to_RDR_XfrBlock = 0x6f,
|
||||
PC_to_RDR_Mechanical = 0x71,
|
||||
PC_to_RDR_Abort = 0x72,
|
||||
PC_to_RDR_SetDataRate = 0x73,
|
||||
|
||||
RDR_to_PC_DataBlock = 0x80,
|
||||
RDR_to_PC_SlotStatus = 0x81,
|
||||
RDR_to_PC_Parameters = 0x82,
|
||||
RDR_to_PC_Escape = 0x83,
|
||||
RDR_to_PC_DataRate = 0x84
|
||||
};
|
||||
|
||||
|
||||
#define digitp(p) ((p) >= '0' && (p) <= '9')
|
||||
#define hexdigitp(a) (digitp (a) \
|
||||
|| ((a) >= 'A' && (a) <= 'F') \
|
||||
|| ((a) >= 'a' && (a) <= 'f'))
|
||||
#define ascii_isspace(a) ((a)==' ' || (a)=='\n' || (a)=='\r' || (a)=='\t')
|
||||
#define xtoi_1(p) ((p) <= '9'? ((p)- '0'): \
|
||||
(p) <= 'F'? ((p)-'A'+10):((p)-'a'+10))
|
||||
|
||||
|
||||
|
||||
/* Print diagnostic message and exit with failure. */
|
||||
static void
|
||||
die (const char *format, ...)
|
||||
{
|
||||
va_list arg_ptr;
|
||||
|
||||
fflush (stdout);
|
||||
fprintf (stderr, "%s: ", PGM);
|
||||
|
||||
va_start (arg_ptr, format);
|
||||
vfprintf (stderr, format, arg_ptr);
|
||||
va_end (arg_ptr);
|
||||
putc ('\n', stderr);
|
||||
|
||||
exit (1);
|
||||
}
|
||||
|
||||
|
||||
/* Print diagnostic message. */
|
||||
static void
|
||||
err (const char *format, ...)
|
||||
{
|
||||
va_list arg_ptr;
|
||||
|
||||
any_error = 1;
|
||||
|
||||
fflush (stdout);
|
||||
fprintf (stderr, "%s: ", PGM);
|
||||
|
||||
va_start (arg_ptr, format);
|
||||
vfprintf (stderr, format, arg_ptr);
|
||||
va_end (arg_ptr);
|
||||
putc ('\n', stderr);
|
||||
}
|
||||
|
||||
|
||||
/* Convert a little endian stored 4 byte value into an unsigned
|
||||
integer. */
|
||||
static unsigned int
|
||||
convert_le_u32 (const unsigned char *buf)
|
||||
{
|
||||
return buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24);
|
||||
}
|
||||
|
||||
|
||||
/* Convert a little endian stored 2 byte value into an unsigned
|
||||
integer. */
|
||||
static unsigned int
|
||||
convert_le_u16 (const unsigned char *buf)
|
||||
{
|
||||
return buf[0] | (buf[1] << 8);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
static void
|
||||
print_pr_data (const unsigned char *data, size_t datalen, size_t off)
|
||||
{
|
||||
int needlf = 0;
|
||||
int first = 1;
|
||||
|
||||
for (; off < datalen; off++)
|
||||
{
|
||||
if (!(off % 16) || first)
|
||||
{
|
||||
if (needlf)
|
||||
putchar ('\n');
|
||||
printf (" [%04d] ", off);
|
||||
}
|
||||
printf (" %02X", data[off]);
|
||||
needlf = 1;
|
||||
first = 0;
|
||||
}
|
||||
if (needlf)
|
||||
putchar ('\n');
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
print_p2r_header (const char *name, const unsigned char *msg, size_t msglen)
|
||||
{
|
||||
printf ("%s:\n", name);
|
||||
if (msglen < 7)
|
||||
return;
|
||||
printf (" dwLength ..........: %u\n", convert_le_u32 (msg+1));
|
||||
printf (" bSlot .............: %u\n", msg[5]);
|
||||
printf (" bSeq ..............: %u\n", msg[6]);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
print_p2r_iccpoweron (const unsigned char *msg, size_t msglen)
|
||||
{
|
||||
print_p2r_header ("PC_to_RDR_IccPowerOn", msg, msglen);
|
||||
if (msglen < 10)
|
||||
return;
|
||||
printf (" bPowerSelect ......: 0x%02x (%s)\n", msg[7],
|
||||
msg[7] == 0? "auto":
|
||||
msg[7] == 1? "5.0 V":
|
||||
msg[7] == 2? "3.0 V":
|
||||
msg[7] == 3? "1.8 V":"");
|
||||
print_pr_data (msg, msglen, 8);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
print_p2r_iccpoweroff (const unsigned char *msg, size_t msglen)
|
||||
{
|
||||
print_p2r_header ("PC_to_RDR_IccPowerOff", msg, msglen);
|
||||
print_pr_data (msg, msglen, 7);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
print_p2r_getslotstatus (const unsigned char *msg, size_t msglen)
|
||||
{
|
||||
print_p2r_header ("PC_to_RDR_GetSlotStatus", msg, msglen);
|
||||
print_pr_data (msg, msglen, 7);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
print_p2r_xfrblock (const unsigned char *msg, size_t msglen)
|
||||
{
|
||||
unsigned int val;
|
||||
|
||||
print_p2r_header ("PC_to_RDR_XfrBlock", msg, msglen);
|
||||
if (msglen < 10)
|
||||
return;
|
||||
printf (" bBWI ..............: 0x%02x\n", msg[7]);
|
||||
val = convert_le_u16 (msg+8);
|
||||
printf (" wLevelParameter ...: 0x%04x%s\n", val,
|
||||
val == 1? " (continued)":
|
||||
val == 2? " (continues+ends)":
|
||||
val == 3? " (continues+continued)":
|
||||
val == 16? " (DataBlock-expected)":"");
|
||||
print_pr_data (msg, msglen, 10);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
print_p2r_getparameters (const unsigned char *msg, size_t msglen)
|
||||
{
|
||||
print_p2r_header ("PC_to_RDR_GetParameters", msg, msglen);
|
||||
print_pr_data (msg, msglen, 7);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
print_p2r_resetparameters (const unsigned char *msg, size_t msglen)
|
||||
{
|
||||
print_p2r_header ("PC_to_RDR_ResetParameters", msg, msglen);
|
||||
print_pr_data (msg, msglen, 7);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
print_p2r_setparameters (const unsigned char *msg, size_t msglen)
|
||||
{
|
||||
print_p2r_header ("PC_to_RDR_SetParameters", msg, msglen);
|
||||
if (msglen < 10)
|
||||
return;
|
||||
printf (" bProtocolNum ......: 0x%02x\n", msg[7]);
|
||||
print_pr_data (msg, msglen, 8);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
print_p2r_escape (const unsigned char *msg, size_t msglen)
|
||||
{
|
||||
if (skip_escape)
|
||||
return;
|
||||
print_p2r_header ("PC_to_RDR_Escape", msg, msglen);
|
||||
print_pr_data (msg, msglen, 7);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
print_p2r_iccclock (const unsigned char *msg, size_t msglen)
|
||||
{
|
||||
print_p2r_header ("PC_to_RDR_IccClock", msg, msglen);
|
||||
if (msglen < 10)
|
||||
return;
|
||||
printf (" bClockCommand .....: 0x%02x\n", msg[7]);
|
||||
print_pr_data (msg, msglen, 8);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
print_p2r_to0apdu (const unsigned char *msg, size_t msglen)
|
||||
{
|
||||
print_p2r_header ("PC_to_RDR_T0APDU", msg, msglen);
|
||||
if (msglen < 10)
|
||||
return;
|
||||
printf (" bmChanges .........: 0x%02x\n", msg[7]);
|
||||
printf (" bClassGetResponse .: 0x%02x\n", msg[8]);
|
||||
printf (" bClassEnvelope ....: 0x%02x\n", msg[9]);
|
||||
print_pr_data (msg, msglen, 10);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
print_p2r_secure (const unsigned char *msg, size_t msglen)
|
||||
{
|
||||
unsigned int val;
|
||||
|
||||
print_p2r_header ("PC_to_RDR_Secure", msg, msglen);
|
||||
if (msglen < 10)
|
||||
return;
|
||||
printf (" bBMI ..............: 0x%02x\n", msg[7]);
|
||||
val = convert_le_u16 (msg+8);
|
||||
printf (" wLevelParameter ...: 0x%04x%s\n", val,
|
||||
val == 1? " (continued)":
|
||||
val == 2? " (continues+ends)":
|
||||
val == 3? " (continues+continued)":
|
||||
val == 16? " (DataBlock-expected)":"");
|
||||
print_pr_data (msg, msglen, 10);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
print_p2r_mechanical (const unsigned char *msg, size_t msglen)
|
||||
{
|
||||
print_p2r_header ("PC_to_RDR_Mechanical", msg, msglen);
|
||||
if (msglen < 10)
|
||||
return;
|
||||
printf (" bFunction .........: 0x%02x\n", msg[7]);
|
||||
print_pr_data (msg, msglen, 8);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
print_p2r_abort (const unsigned char *msg, size_t msglen)
|
||||
{
|
||||
print_p2r_header ("PC_to_RDR_Abort", msg, msglen);
|
||||
print_pr_data (msg, msglen, 7);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
print_p2r_setdatarate (const unsigned char *msg, size_t msglen)
|
||||
{
|
||||
print_p2r_header ("PC_to_RDR_SetDataRate", msg, msglen);
|
||||
if (msglen < 10)
|
||||
return;
|
||||
print_pr_data (msg, msglen, 7);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
print_p2r_unknown (const unsigned char *msg, size_t msglen)
|
||||
{
|
||||
print_p2r_header ("Unknown PC_to_RDR command", msg, msglen);
|
||||
if (msglen < 10)
|
||||
return;
|
||||
print_pr_data (msg, msglen, 0);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
print_p2r (const unsigned char *msg, size_t msglen)
|
||||
{
|
||||
switch (msglen? msg[0]:0)
|
||||
{
|
||||
case PC_to_RDR_IccPowerOn:
|
||||
print_p2r_iccpoweron (msg, msglen);
|
||||
break;
|
||||
case PC_to_RDR_IccPowerOff:
|
||||
print_p2r_iccpoweroff (msg, msglen);
|
||||
break;
|
||||
case PC_to_RDR_GetSlotStatus:
|
||||
print_p2r_getslotstatus (msg, msglen);
|
||||
break;
|
||||
case PC_to_RDR_XfrBlock:
|
||||
print_p2r_xfrblock (msg, msglen);
|
||||
break;
|
||||
case PC_to_RDR_GetParameters:
|
||||
print_p2r_getparameters (msg, msglen);
|
||||
break;
|
||||
case PC_to_RDR_ResetParameters:
|
||||
print_p2r_resetparameters (msg, msglen);
|
||||
break;
|
||||
case PC_to_RDR_SetParameters:
|
||||
print_p2r_setparameters (msg, msglen);
|
||||
break;
|
||||
case PC_to_RDR_Escape:
|
||||
print_p2r_escape (msg, msglen);
|
||||
break;
|
||||
case PC_to_RDR_IccClock:
|
||||
print_p2r_iccclock (msg, msglen);
|
||||
break;
|
||||
case PC_to_RDR_T0APDU:
|
||||
print_p2r_to0apdu (msg, msglen);
|
||||
break;
|
||||
case PC_to_RDR_Secure:
|
||||
print_p2r_secure (msg, msglen);
|
||||
break;
|
||||
case PC_to_RDR_Mechanical:
|
||||
print_p2r_mechanical (msg, msglen);
|
||||
break;
|
||||
case PC_to_RDR_Abort:
|
||||
print_p2r_abort (msg, msglen);
|
||||
break;
|
||||
case PC_to_RDR_SetDataRate:
|
||||
print_p2r_setdatarate (msg, msglen);
|
||||
break;
|
||||
default:
|
||||
print_p2r_unknown (msg, msglen);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
print_r2p_header (const char *name, const unsigned char *msg, size_t msglen)
|
||||
{
|
||||
printf ("%s:\n", name);
|
||||
if (msglen < 9)
|
||||
return;
|
||||
printf (" dwLength ..........: %u\n", convert_le_u32 (msg+1));
|
||||
printf (" bSlot .............: %u\n", msg[5]);
|
||||
printf (" bSeq ..............: %u\n", msg[6]);
|
||||
printf (" bStatus ...........: %u\n", msg[7]);
|
||||
if (msg[8])
|
||||
printf (" bError ............: %u\n", msg[8]);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
print_r2p_datablock (const unsigned char *msg, size_t msglen)
|
||||
{
|
||||
print_r2p_header ("RDR_to_PC_DataBlock", msg, msglen);
|
||||
if (msglen < 10)
|
||||
return;
|
||||
if (msg[9])
|
||||
printf (" bChainParameter ...: 0x%02x%s\n", msg[9],
|
||||
msg[9] == 1? " (continued)":
|
||||
msg[9] == 2? " (continues+ends)":
|
||||
msg[9] == 3? " (continues+continued)":
|
||||
msg[9] == 16? " (XferBlock-expected)":"");
|
||||
print_pr_data (msg, msglen, 10);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
print_r2p_slotstatus (const unsigned char *msg, size_t msglen)
|
||||
{
|
||||
print_r2p_header ("RDR_to_PC_SlotStatus", msg, msglen);
|
||||
if (msglen < 10)
|
||||
return;
|
||||
printf (" bClockStatus ......: 0x%02x%s\n", msg[9],
|
||||
msg[9] == 0? " (running)":
|
||||
msg[9] == 1? " (stopped-L)":
|
||||
msg[9] == 2? " (stopped-H)":
|
||||
msg[9] == 3? " (stopped)":"");
|
||||
print_pr_data (msg, msglen, 10);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
print_r2p_parameters (const unsigned char *msg, size_t msglen)
|
||||
{
|
||||
print_r2p_header ("RDR_to_PC_Parameters", msg, msglen);
|
||||
if (msglen < 10)
|
||||
return;
|
||||
|
||||
printf (" protocol ..........: T=%d\n", msg[9]);
|
||||
if (msglen == 17 && msg[9] == 1)
|
||||
{
|
||||
/* Protocol T=1. */
|
||||
printf (" bmFindexDindex ....: %02X\n", msg[10]);
|
||||
printf (" bmTCCKST1 .........: %02X\n", msg[11]);
|
||||
printf (" bGuardTimeT1 ......: %02X\n", msg[12]);
|
||||
printf (" bmWaitingIntegersT1: %02X\n", msg[13]);
|
||||
printf (" bClockStop ........: %02X\n", msg[14]);
|
||||
printf (" bIFSC .............: %d\n", msg[15]);
|
||||
printf (" bNadValue .........: %d\n", msg[16]);
|
||||
}
|
||||
else
|
||||
print_pr_data (msg, msglen, 10);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
print_r2p_escape (const unsigned char *msg, size_t msglen)
|
||||
{
|
||||
if (skip_escape)
|
||||
return;
|
||||
print_r2p_header ("RDR_to_PC_Escape", msg, msglen);
|
||||
if (msglen < 10)
|
||||
return;
|
||||
printf (" buffer[9] .........: %02X\n", msg[9]);
|
||||
print_pr_data (msg, msglen, 10);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
print_r2p_datarate (const unsigned char *msg, size_t msglen)
|
||||
{
|
||||
print_r2p_header ("RDR_to_PC_DataRate", msg, msglen);
|
||||
if (msglen < 10)
|
||||
return;
|
||||
if (msglen >= 18)
|
||||
{
|
||||
printf (" dwClockFrequency ..: %u\n", convert_le_u32 (msg+10));
|
||||
printf (" dwDataRate ..... ..: %u\n", convert_le_u32 (msg+14));
|
||||
print_pr_data (msg, msglen, 18);
|
||||
}
|
||||
else
|
||||
print_pr_data (msg, msglen, 10);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
print_r2p_unknown (const unsigned char *msg, size_t msglen)
|
||||
{
|
||||
print_r2p_header ("Unknown RDR_to_PC command", msg, msglen);
|
||||
if (msglen < 10)
|
||||
return;
|
||||
printf (" bMessageType ......: %02X\n", msg[0]);
|
||||
printf (" buffer[9] .........: %02X\n", msg[9]);
|
||||
print_pr_data (msg, msglen, 10);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
print_r2p (const unsigned char *msg, size_t msglen)
|
||||
{
|
||||
switch (msglen? msg[0]:0)
|
||||
{
|
||||
case RDR_to_PC_DataBlock:
|
||||
print_r2p_datablock (msg, msglen);
|
||||
break;
|
||||
case RDR_to_PC_SlotStatus:
|
||||
print_r2p_slotstatus (msg, msglen);
|
||||
break;
|
||||
case RDR_to_PC_Parameters:
|
||||
print_r2p_parameters (msg, msglen);
|
||||
break;
|
||||
case RDR_to_PC_Escape:
|
||||
print_r2p_escape (msg, msglen);
|
||||
break;
|
||||
case RDR_to_PC_DataRate:
|
||||
print_r2p_datarate (msg, msglen);
|
||||
break;
|
||||
default:
|
||||
print_r2p_unknown (msg, msglen);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
flush_data (void)
|
||||
{
|
||||
if (!databuffer.count)
|
||||
return;
|
||||
|
||||
if (verbose)
|
||||
printf ("Address: %s\n", databuffer.address);
|
||||
if (databuffer.is_bi)
|
||||
{
|
||||
print_r2p (databuffer.data, databuffer.count);
|
||||
if (verbose)
|
||||
putchar ('\n');
|
||||
}
|
||||
else
|
||||
print_p2r (databuffer.data, databuffer.count);
|
||||
|
||||
databuffer.count = 0;
|
||||
}
|
||||
|
||||
static void
|
||||
collect_data (char *hexdata, const char *address, unsigned int lineno)
|
||||
{
|
||||
size_t length;
|
||||
int is_bi;
|
||||
char *s;
|
||||
unsigned int value;
|
||||
|
||||
is_bi = (*address && address[1] == 'i');
|
||||
|
||||
if (databuffer.is_bi != is_bi || strcmp (databuffer.address, address))
|
||||
flush_data ();
|
||||
databuffer.is_bi = is_bi;
|
||||
if (strlen (address) >= sizeof databuffer.address)
|
||||
die ("address field too long");
|
||||
strcpy (databuffer.address, address);
|
||||
|
||||
length = databuffer.count;
|
||||
for (s=hexdata; *s; s++ )
|
||||
{
|
||||
if (ascii_isspace (*s))
|
||||
continue;
|
||||
if (!hexdigitp (*s))
|
||||
{
|
||||
err ("invalid hex digit in line %u - line skipped", lineno);
|
||||
break;
|
||||
}
|
||||
value = xtoi_1 (*s) * 16;
|
||||
s++;
|
||||
if (!hexdigitp (*s))
|
||||
{
|
||||
err ("invalid hex digit in line %u - line skipped", lineno);
|
||||
break;
|
||||
}
|
||||
value += xtoi_1 (*s);
|
||||
|
||||
if (length >= sizeof (databuffer.data))
|
||||
{
|
||||
err ("too much data at line %u - can handle only up to % bytes",
|
||||
lineno, sizeof (databuffer.data));
|
||||
break;
|
||||
}
|
||||
databuffer.data[length++] = value;
|
||||
}
|
||||
databuffer.count = length;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
parse_line (char *line, unsigned int lineno)
|
||||
{
|
||||
char *p;
|
||||
char *event_type, *address, *data, *status, *datatag;
|
||||
|
||||
if (debug)
|
||||
printf ("line[%u] =`%s'\n", lineno, line);
|
||||
|
||||
p = strtok (line, " ");
|
||||
if (!p)
|
||||
die ("invalid line %d (no URB)");
|
||||
p = strtok (NULL, " ");
|
||||
if (!p)
|
||||
die ("invalid line %d (no timestamp)");
|
||||
event_type = strtok (NULL, " ");
|
||||
if (!event_type)
|
||||
die ("invalid line %d (no event type)");
|
||||
address = strtok (NULL, " ");
|
||||
if (!address)
|
||||
die ("invalid line %d (no address");
|
||||
if (usb_bus || usb_dev)
|
||||
{
|
||||
int bus, dev;
|
||||
|
||||
p = strchr (address, ':');
|
||||
if (!p)
|
||||
die ("invalid line %d (invalid address");
|
||||
p++;
|
||||
bus = atoi (p);
|
||||
p = strchr (p, ':');
|
||||
if (!p)
|
||||
die ("invalid line %d (invalid address");
|
||||
p++;
|
||||
dev = atoi (p);
|
||||
|
||||
if ((usb_bus && usb_bus != bus) || (usb_dev && usb_dev != dev))
|
||||
return; /* We don't want that one. */
|
||||
}
|
||||
if (*address != 'B' || (address[1] != 'o' && address[1] != 'i'))
|
||||
return; /* We only want block in and block out. */
|
||||
status = strtok (NULL, " ");
|
||||
if (!status)
|
||||
return;
|
||||
if (!strchr ("-0123456789", *status))
|
||||
return; /* Setup packet. */
|
||||
/* We don't support "Z[io]" types thus we don't need to check here. */
|
||||
p = strtok (NULL, " ");
|
||||
if (!p)
|
||||
return; /* No data length. */
|
||||
|
||||
datatag = strtok (NULL, " ");
|
||||
if (datatag && *datatag == '=')
|
||||
{
|
||||
data = strtok (NULL, "");
|
||||
collect_data (data?data:"", address, lineno);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
parse_input (FILE *fp)
|
||||
{
|
||||
char line[2000];
|
||||
size_t length;
|
||||
unsigned int lineno = 0;
|
||||
|
||||
while (fgets (line, sizeof (line), fp))
|
||||
{
|
||||
lineno++;
|
||||
length = strlen (line);
|
||||
if (length && line[length - 1] == '\n')
|
||||
line[--length] = 0;
|
||||
else
|
||||
err ("line number %u too long or last line not terminated", lineno);
|
||||
if (length && line[length - 1] == '\r')
|
||||
line[--length] = 0;
|
||||
parse_line (line, lineno);
|
||||
}
|
||||
flush_data ();
|
||||
if (ferror (fp))
|
||||
err ("error reading input at line %u: %s", lineno, strerror (errno));
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
main (int argc, char **argv)
|
||||
{
|
||||
int last_argc = -1;
|
||||
|
||||
if (argc)
|
||||
{
|
||||
argc--; argv++;
|
||||
}
|
||||
while (argc && last_argc != argc )
|
||||
{
|
||||
last_argc = argc;
|
||||
if (!strcmp (*argv, "--"))
|
||||
{
|
||||
argc--; argv++;
|
||||
break;
|
||||
}
|
||||
else if (!strcmp (*argv, "--version"))
|
||||
{
|
||||
fputs (PGM " (GnuPG) " PACKAGE_VERSION "\n", stdout);
|
||||
exit (0);
|
||||
}
|
||||
else if (!strcmp (*argv, "--help"))
|
||||
{
|
||||
puts ("Usage: " PGM " [BUS:DEV]\n"
|
||||
"Parse the output of usbmod assuming it is CCID compliant.\n\n"
|
||||
" --skip-escape do not show escape packets\n"
|
||||
" --verbose enable extra informational output\n"
|
||||
" --debug enable additional debug output\n"
|
||||
" --help display this help and exit\n\n"
|
||||
"Report bugs to " PACKAGE_BUGREPORT ".");
|
||||
exit (0);
|
||||
}
|
||||
else if (!strcmp (*argv, "--verbose"))
|
||||
{
|
||||
verbose = 1;
|
||||
argc--; argv++;
|
||||
}
|
||||
else if (!strcmp (*argv, "--debug"))
|
||||
{
|
||||
verbose = debug = 1;
|
||||
argc--; argv++;
|
||||
}
|
||||
else if (!strcmp (*argv, "--skip-escape"))
|
||||
{
|
||||
skip_escape = 1;
|
||||
argc--; argv++;
|
||||
}
|
||||
}
|
||||
|
||||
if (argc > 1)
|
||||
die ("usage: " PGM " [BUS:DEV] (try --help for more information)\n");
|
||||
|
||||
if (argc == 1)
|
||||
{
|
||||
const char *s = strchr (argv[0], ':');
|
||||
|
||||
usb_bus = atoi (argv[0]);
|
||||
if (s)
|
||||
usb_dev = atoi (s+1);
|
||||
if (usb_bus < 1 || usb_bus > 999 || usb_dev < 1 || usb_dev > 999)
|
||||
die ("invalid bus:dev specified");
|
||||
}
|
||||
|
||||
|
||||
signal (SIGPIPE, SIG_IGN);
|
||||
|
||||
parse_input (stdin);
|
||||
|
||||
return any_error? 1:0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Local Variables:
|
||||
compile-command: "gcc -Wall -Wno-pointer-sign -g -o ccidmon ccidmon.c"
|
||||
End:
|
||||
*/
|
Loading…
x
Reference in New Issue
Block a user