blob: 1a0aa405de6b577a4dc645ed30b2ab1ddee30be6 [file] [log] [blame]
/**
* @file mainRjct.c
* @author NXP Semiconductors
* @version 1.0
* @par License
*
* Copyright 2017,2020 NXP
* SPDX-License-Identifier: Apache-2.0
*
* @par Description
* Connection Oriented TCP/IP Server implementing Remote JCTerminal Protocol.
* The server can connect to the secure element via the
* - SCI2C protocol
* - T1oI2C protocol
* @par History
*
*****************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <signal.h>
#include "rjct.h"
#if defined(SCI2C)
#include "apduComm.h"
#include "sci2c.h"
#endif
#define PORT "8050" // the port users will be connecting to
#define BACKLOG 1 // how many pending connections queue will hold
#define APP_BUFFER 2048
#if defined(SCI2C)
#define MAX_READ_SOCKET 2000
#elif defined(TDA8029_UART)
#define MAX_READ_SOCKET 500
#elif defined(PCSC)
#define MAX_READ_SOCKET 1024
#elif defined(SMCOM_JRCP_V2)
// TODO: Check value
#define MAX_READ_SOCKET 2000
#elif defined(T1oI2C)
// TODO: Check value
#define MAX_READ_SOCKET 2000
#else
#error "No communication channel defined"
#endif
#define ERROR_VERBOSE
#ifndef FLOW_VERBOSE
#define FLOW_VERBOSE
#endif
// #define DBG_VERBOSE
#ifdef ERROR_VERBOSE
#define EPRINTF(...) printf (__VA_ARGS__)
#else
#define EPRINTF(...)
#endif
#ifdef FLOW_VERBOSE
#define FPRINTF(...) printf (__VA_ARGS__)
#else
#define FPRINTF(...)
#endif
#ifdef DBG_VERBOSE
#define DPRINTF(...) printf (__VA_ARGS__)
#else
#define DPRINTF(...)
#endif
void sigchld_handler(int s)
{
while (waitpid(-1, NULL, WNOHANG) > 0);
}
// get sockaddr, IPv4 or IPv6:
void *get_in_addr(struct sockaddr *sa)
{
if (sa->sa_family == AF_INET)
{
return &(((struct sockaddr_in*)sa)->sin_addr);
}
return &(((struct sockaddr_in6*)sa)->sin6_addr);
}
int main(void)
{
int sockfd, new_fd; // listen on sock_fd, new connection on new_fd
struct addrinfo hints, *servinfo, *p;
struct sockaddr_storage their_addr; // connector's address information
socklen_t sin_size;
struct sigaction sa;
int yes=1;
char s[INET6_ADDRSTRLEN];
int rv;
U8 targetBuffer[APP_BUFFER];
U16 targetBufferLen = sizeof(targetBuffer);
U16 statusValue = 0;
U8 respBuf[APP_BUFFER];
U16 respBufLen = sizeof(respBuf);
static bool sessionOpen = FALSE;
// U8 result = 1;
U16 connectStatus = 0;
#if defined(TDA8029_UART) || defined(SCI2C) || defined(PCSC) || defined(SMCOM_JRCP_V2) || defined(T1oI2C)
U8 Atr[64];
U16 AtrLen = sizeof(Atr);
SmCommStateRjct_t commState;
// Scp03SessionState_t sessionState;
#endif
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE; // use my IP
if ((rv = getaddrinfo(NULL, PORT, &hints, &servinfo)) != 0)
{
EPRINTF("getaddrinfo: %s\n", gai_strerror(rv));
return 1;
}
// loop through all the results and bind to the first we can
for (p = servinfo; p != NULL; p = p->ai_next)
{
if ((sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1)
{
perror("server: socket");
continue;
}
if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1)
{
perror("setsockopt");
exit(1);
}
if (bind(sockfd, p->ai_addr, p->ai_addrlen) == -1)
{
close(sockfd);
perror("server: bind");
continue;
}
break;
}
if (p == NULL)
{
EPRINTF("server: failed to bind\n");
return 2;
}
freeaddrinfo(servinfo); // all done with this structure
if (listen(sockfd, BACKLOG) == -1)
{
perror("listen");
exit(1);
}
sa.sa_handler = sigchld_handler; // reap all dead processes
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART;
if (sigaction(SIGCHLD, &sa, NULL) == -1)
{
perror("sigaction");
exit(1);
}
#if defined(TDA8029_UART)
printf("RemoteJCShell Terminal Server (supporting TDA-8029-UART to smartcard) Rev 0.91\n");
#elif defined(SCI2C)
printf("RemoteJCShell Terminal Server (supporting SCI2C) Rev 0.92\n");
#if defined(TGT_A71CH) || defined(TGT_A71CL)
printf("A71 (I2C_CLK_MAX = 400 kHz) - Effective Master Clock depends on Host Platform.\n");
#else
printf("A70 (I2C_CLK_MAX = 100 kHz) - Effective Master Clock depends on Host Platform.\n");
#endif
#elif defined (PCSC)
printf("RemoteJCShell Terminal Server (supporting PCSC) Rev 0.92\n");
#elif defined(SMCOM_JRCP_V2)
printf("RemoteJCShell Terminal Server (supporting JRCPv2 Client Side)\n");
#elif defined(T1oI2C)
printf("RemoteJCShell Terminal Server (supporting T1oI2C Client Side)\n");
#else
#error "No interconnect defined: supported are TDA8029_UART, SCI2C and PCSC"
#endif
printf("******************************************************************************\n");
printf("Establish a connection via JCShell:\n");
printf("\t/term Remote|<ip-address>:<port>\n");
printf("\t e.g.\n");
printf("\t /term Remote|192.168.1.27:8050\n");
printf("\n");
printf("Server: waiting for connections...\n");
while (1) // main accept() loop
{
sin_size = sizeof their_addr;
new_fd = accept(sockfd, (struct sockaddr *)&their_addr, &sin_size);
if (new_fd == -1)
{
perror("accept");
continue;
}
inet_ntop(their_addr.ss_family,
get_in_addr((struct sockaddr *)&their_addr),
s, sizeof s);
FPRINTF("Server: got connection from %s\n", s);
if (!fork())
{ // this is the child process
int nExpectedPayload = 0;
close(sockfd); // child doesn't need the listener
while(1)
{
U8 rcvBuf[APP_BUFFER];
int nRcv;
int nRcvAcc;
int i;
U8 emptyDbgInfo[] = {DEBUG_INFORMATION, 0x00, 0x00, 0x00};
U8 emptyTerminalInfo[] = {TERMINAL_INFO, 0x00, 0x00, 0x00};
nRcv = recv(new_fd, rcvBuf, 4, MSG_WAITALL);
nRcvAcc = nRcv;
switch (nRcv)
{
case -1:
perror("recv");
FPRINTF("recv() failed: %d\n", nRcv);
exit(0);
break;
case 0:
FPRINTF("Connection closed by client (%d)\n", nRcv);
#if defined(SCI2C)
sci2c_TerminateI2C(1);
#endif
exit(0);
break;
default:
DPRINTF("Received: \n");
for (i=0; i<nRcv; i++)
{
DPRINTF("0x%02X:", rcvBuf[i]);
}
DPRINTF("\n");
if (nRcv < 4)
{
EPRINTF("nRcv = %d (nRcv = 0x%08X).\n", nRcv, nRcv);
EPRINTF("Did not expect a payload less than 4 byte. Closing socket. Bye!\n");
close(new_fd);
exit(0);
}
nExpectedPayload = (rcvBuf[2] << 8) + rcvBuf[3];
if (nRcv < (nExpectedPayload + 4))
{
// Read more data from socket
if (nExpectedPayload > MAX_READ_SOCKET)
{
EPRINTF("nExpectPayload too big %d (limit=%d).\n", nExpectedPayload, MAX_READ_SOCKET);
exit(0);
}
nRcv = recv(new_fd, rcvBuf+4, nExpectedPayload, 0);
if (nRcv == -1)
{
perror("Error on additional read");
exit(0);
}
else if (nRcv == 0)
{
EPRINTF("Connection was closed by client on additional read.\n");
exit(0);
}
else if (nRcv < 0)
{
EPRINTF("recv returns error on additional read. nRcv = %d (nRcv = 0x%08X).\n", nRcv, nRcv);
exit(0);
}
else
{
DPRINTF("Received: \n");
for (i=0; i<nRcv; i++)
{
DPRINTF("0x%02X:", rcvBuf[nRcvAcc+i]);
}
DPRINTF("\n");
nRcvAcc += nRcv;
}
}
if (nRcvAcc == (nExpectedPayload + 4))
{
// Interpret the message contained in rcvBuf
switch (rcvBuf[0])
{
case WAIT_FOR_CARD:
//Hanle reset
if(sessionOpen)
{
/*session is already open close the session first */
connectStatus = SM_CloseRjct(1);
if (connectStatus != SW_OK)
{
U8 errMsg[] = {WAIT_FOR_CARD, 0x00, 0x00, 0x00};
EPRINTF("Failed to establish connection to Secure Module: 0x%04X\n", connectStatus);
if (send(new_fd, errMsg, sizeof(errMsg), 0) == -1)
{
perror("send");
close(new_fd);
exit(0);
}
}
sessionOpen = FALSE;
}
DPRINTF("Establish connection and issue an ATR.\n");
AtrLen = sizeof(Atr);
connectStatus = SM_ConnectRjct(&commState, Atr, &AtrLen);
if (connectStatus != SW_OK)
{
U8 errMsg[] = {WAIT_FOR_CARD, 0x00, 0x00, 0x00};
EPRINTF("Failed to establish connection to Secure Module: 0x%04X\n", connectStatus);
if (send(new_fd, errMsg, sizeof(errMsg), 0) == -1)
{
perror("send");
close(new_fd);
exit(0);
}
}
else
{
int i=0;
FPRINTF("ATR=0x");
for (i=0; i<AtrLen; i++) printf("%02X.", Atr[i]);
FPRINTF("\n");
#if defined(TDA8029_UART)
FPRINTF("UART Baudrate Idx: 0x%02X\n", commState.param2);
FPRINTF("T=1 TA1: 0x%02X\n", commState.param1);
#endif
targetBufferLen = sizeof(targetBuffer);
statusValue = rjctPackageApduResponse(WAIT_FOR_CARD, 0x00, Atr, AtrLen, targetBuffer, &targetBufferLen);
if (statusValue == RJCT_OK)
{
if (send(new_fd, targetBuffer, targetBufferLen, 0) == -1)
{
EPRINTF("Returning ATR to JCShell client failed\n");
close(new_fd);
exit(0);
}
}
else
{
EPRINTF("Could not package APDU response (Line=%d).\n", __LINE__);
}
sessionOpen = TRUE;
}
break;
case APDU_DATA:
respBufLen = sizeof(respBuf);
statusValue = SM_SendAPDURjct(&rcvBuf[4], nExpectedPayload, respBuf, &respBufLen);
if (statusValue == SW_OK)
{
targetBufferLen = sizeof(targetBuffer);
statusValue = rjctPackageApduResponse(APDU_DATA, 0x00, respBuf, respBufLen, targetBuffer, &targetBufferLen);
if (statusValue == RJCT_OK)
{
int nRet = send(new_fd, targetBuffer, targetBufferLen, 0);
if (nRet == -1)
{
perror("send (Returning APDU Response to JCShell client failed)");
close(new_fd);
exit(0);
}
else if (nRet != targetBufferLen)
{
EPRINTF("Did not return full response, fix TCP/IP server.\n");
close(new_fd);
exit(0);
}
}
else
{
EPRINTF("Could not package APDU response (Line=%d).\n", __LINE__);
}
}
else
{
EPRINTF("SM_SendAPDU failed with statusValue: 0x%04X.\n", statusValue);
EPRINTF("*********************************************");
close(new_fd);
exit(0);
}
break;
case DEBUG_INFORMATION:
printf("Received a debug info message. Return DEBUG_INFO without data payload.\n");
if (send(new_fd, emptyDbgInfo, sizeof(emptyDbgInfo), 0) == -1)
{
perror("Send failed.\n");
close(new_fd);
exit(0);
}
break;
case TERMINAL_INFO:
printf("Received a terminal info message. Return TERMINAL_INFO without data payload.\n");
if (send(new_fd, emptyTerminalInfo, sizeof(emptyTerminalInfo), 0) == -1)
{
perror("Send failed.\n");
close(new_fd);
exit(0);
}
break;
case STATUS:
case ERROR_MSG:
case INITIALIZATION_DATA:
case INFORMATION_TEXT:
EPRINTF("Don't know how to deal with Message Type 0x%02X.\n", rcvBuf[0]);
exit(0);
break;
default:
EPRINTF("Don't know how to deal with Message Type 0x%02X.\n", rcvBuf[0]);
exit(0);
break;
}
}
else
{
EPRINTF("Expected the full payload. nRcvAcc=%d, nExpectedPayload=%d, sizeofInt(%d)\n", nRcvAcc, nExpectedPayload, (int)sizeof(nRcvAcc));
EPRINTF("NOTE: nRcvAcc should equal (nExpectPayload + 4)\n");
close(new_fd);
exit(0);
}
break;
}
}
exit(0);
}
close(new_fd); // parent doesn't need this
}
return 0;
}