Refactory of the proxy stubs code.
Added the IOBlks proxystub. Included reliance edge test. git-svn-id: svn://svn.code.sf.net/p/chibios/svn/trunk@12020 110e8d01-0319-4d1e-a829-52ad28d1bb01
This commit is contained in:
parent
fe8665fe9e
commit
dd882e357e
|
@ -38,16 +38,43 @@
|
|||
#define SKEL_REQ_PUTRES 3
|
||||
#define SKEL_REQ_READY 4
|
||||
|
||||
#define STUB_OP_SOCKET 0
|
||||
#define STUB_OP_CLOSE 1
|
||||
#define STUB_OP_CONNECT 2
|
||||
#define STUB_OP_RECV 3
|
||||
#define STUB_OP_SEND 4
|
||||
#define STUB_OP_SELECT 5
|
||||
#define STUB_OP_BIND 6
|
||||
#define STUB_OP_LISTEN 7
|
||||
/* Sockets stub defines.*/
|
||||
#define SOCK_OP_SOCKET 0
|
||||
#define SOCK_OP_CLOSE 1
|
||||
#define SOCK_OP_CONNECT 2
|
||||
#define SOCK_OP_RECV 3
|
||||
#define SOCK_OP_SEND 4
|
||||
#define SOCK_OP_SELECT 5
|
||||
#define SOCK_OP_BIND 6
|
||||
#define SOCK_OP_LISTEN 7
|
||||
|
||||
#define EVT_F_SOCK_NEW_OP 1
|
||||
/* Socket new op event.*/
|
||||
#define EVT_F_SOCK_NEW_OP 1
|
||||
|
||||
/* Sockets stub service name.*/
|
||||
#define SOCKS_SVC_NAME "TsSocksStubService"
|
||||
|
||||
/* IOBlocks stub defines.*/
|
||||
#define IOBLKS_OP_OPEN 0
|
||||
#define IOBLKS_OP_CLOSE 1
|
||||
#define IOBLKS_OP_READ 2
|
||||
#define IOBLKS_OP_WRITE 3
|
||||
#define IOBLKS_OP_FLUSH 4
|
||||
|
||||
/* IOBlock new op event.*/
|
||||
#define EVT_F_IOBLK_NEW_OP 2
|
||||
|
||||
/* IOBlock stub service name.*/
|
||||
#define IOBLKS_SVC_NAME "TsIOBlksStubService"
|
||||
|
||||
/* Sector size.*/
|
||||
#define IOBLKS_SECT_SIZE 512U
|
||||
|
||||
/* Remote Partition size, in sectors.*/
|
||||
#define IOBLKS_PART_SIZE 96256U
|
||||
|
||||
/* Remote partition offset, in sectors.*/
|
||||
#define IOBLKS_PART_OFFS 952320U
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Module pre-compile time settings. */
|
||||
|
@ -63,13 +90,16 @@
|
|||
/*===========================================================================*/
|
||||
/* Module data structures and types. */
|
||||
/*===========================================================================*/
|
||||
typedef struct skel_ctx skel_ctx_t;
|
||||
|
||||
typedef struct skel_req {
|
||||
uint32_t req; /* getop, cpyprms, putres */
|
||||
uint32_t stub_op;
|
||||
uint32_t stub_op_code;
|
||||
uint32_t stub_op_result;
|
||||
uint32_t stub_op_p_sz[METHOD_MAX_PARAMS];
|
||||
uint32_t stub_op_p[METHOD_MAX_PARAMS];
|
||||
uint32_t req; /* getop, cpyprms, putres */
|
||||
uint32_t stub_op;
|
||||
uint32_t stub_op_code;
|
||||
uint32_t stub_op_result;
|
||||
uint32_t stub_op_p_sz[METHOD_MAX_PARAMS];
|
||||
uint32_t stub_op_p[METHOD_MAX_PARAMS];
|
||||
skel_ctx_t *scp; /* the skeleton context this req come from.*/
|
||||
} skel_req_t;
|
||||
|
||||
/*===========================================================================*/
|
||||
|
|
|
@ -0,0 +1,174 @@
|
|||
/*
|
||||
ChibiOS - Copyright (C) 2006..2018 Giovanni Di Sirio
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file tsioblksstub.c
|
||||
* @brief IOBlks stub for trusted services implementing the
|
||||
* Reliance Edge api.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "ch.h"
|
||||
#include "chobjfifos.h"
|
||||
#include "chtssi.h"
|
||||
#include "tsproxystubs.h"
|
||||
#include "tsioblksstub.h"
|
||||
#include <redfs.h>
|
||||
#include <redvolume.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Module local definitions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Module exported variables. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Module local types. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Module local variables. */
|
||||
/*===========================================================================*/
|
||||
static stub_ctx_t stub_ctx;
|
||||
const VOLCONF gaRedVolConf[REDCONF_VOLUME_COUNT] = {
|
||||
{
|
||||
512, /* ulSectorSize.*/
|
||||
IOBLKS_PART_SIZE, /* ullSectorCount.*/
|
||||
0U, /* ullSectorOffset.*/
|
||||
false, /* fAtomicSectorWrite.*/
|
||||
1024U, /* ulInodeCount.*/
|
||||
2U, /* bBlockIoRetries.*/
|
||||
""
|
||||
}
|
||||
};
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Module local functions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Module exported functions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief The IOBlks stub service.
|
||||
*/
|
||||
THD_WORKING_AREA(waTsIOBlksStubsService, 1024);
|
||||
THD_FUNCTION(TsIOBlksStubsService, tsstatep) {
|
||||
stub_ctx.event_flag = EVT_F_IOBLK_NEW_OP;
|
||||
TsStubService((ts_state_t *)tsstatep, &stub_ctx);
|
||||
}
|
||||
|
||||
/**
|
||||
* @name The IOBlks api, in the Reliance Edge fashion.
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Open the block device.
|
||||
*/
|
||||
REDSTATUS RedOsBDevOpen(uint8_t bVolNum, BDEVOPENMODE mode) {
|
||||
(void)mode;
|
||||
|
||||
if (bVolNum != 0)
|
||||
return -RED_EINVAL;
|
||||
stub_op_t *op = getNewOp(&stub_ctx);
|
||||
op->op_code = IOBLKS_OP_OPEN;
|
||||
op->op_p[0].dir = OP_PRMDIR_NONE;
|
||||
op->op_p[0].val = (uint32_t)bVolNum;
|
||||
op->op_p[1].dir = OP_PRMDIR_NONE;
|
||||
op->op_p[1].val = (uint32_t)mode;
|
||||
return (REDSTATUS)callRemote(op);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Close the block device.
|
||||
*/
|
||||
REDSTATUS RedOsBDevClose(uint8_t bVolNum) {
|
||||
if (bVolNum != 0)
|
||||
return -RED_EINVAL;
|
||||
stub_op_t *op = getNewOp(&stub_ctx);
|
||||
op->op_code = IOBLKS_OP_CLOSE;
|
||||
op->op_p[0].dir = OP_PRMDIR_NONE;
|
||||
op->op_p[0].val = (uint32_t)bVolNum;
|
||||
return (REDSTATUS)callRemote(op);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Read from the block device.
|
||||
*/
|
||||
REDSTATUS RedOsBDevRead(uint8_t bVolNum, uint64_t ullSectorStart, uint32_t ulSectorCount, void *pBuffer) {
|
||||
if ((bVolNum != 0) ||
|
||||
(ullSectorStart >= gaRedVolConf[bVolNum].ullSectorCount) ||
|
||||
((gaRedVolConf[bVolNum].ullSectorCount - ullSectorStart) < ulSectorCount) ||
|
||||
(pBuffer == NULL))
|
||||
return -RED_EINVAL;
|
||||
stub_op_t *op = getNewOp(&stub_ctx);
|
||||
op->op_code = IOBLKS_OP_READ;
|
||||
op->op_p[0].dir = OP_PRMDIR_NONE;
|
||||
op->op_p[0].val = (uint32_t)bVolNum;
|
||||
op->op_p[1].dir = OP_PRMDIR_NONE;
|
||||
op->op_p[1].val = (uint32_t)ullSectorStart;
|
||||
op->op_p[2].dir = OP_PRMDIR_NONE;
|
||||
op->op_p[2].val = (uint32_t)ulSectorCount;
|
||||
op->op_p[3].dir = OP_PRMDIR_OUT;
|
||||
op->op_p[3].val = (uint32_t)pBuffer;
|
||||
op->op_p[3].size = (uint32_t)(ulSectorCount * IOBLKS_SECT_SIZE);
|
||||
return (int)callRemote(op);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Write to the block device.
|
||||
*/
|
||||
REDSTATUS RedOsBDevWrite(uint8_t bVolNum, uint64_t ullSectorStart, uint32_t ulSectorCount, const void *pBuffer) {
|
||||
if ((bVolNum != 0) ||
|
||||
(ullSectorStart >= gaRedVolConf[bVolNum].ullSectorCount) ||
|
||||
((gaRedVolConf[bVolNum].ullSectorCount - ullSectorStart) < ulSectorCount) ||
|
||||
(pBuffer == NULL))
|
||||
return -RED_EINVAL;
|
||||
stub_op_t *op = getNewOp(&stub_ctx);
|
||||
op->op_code = IOBLKS_OP_WRITE;
|
||||
op->op_p[0].dir = OP_PRMDIR_NONE;
|
||||
op->op_p[0].val = (uint32_t)bVolNum;
|
||||
op->op_p[1].dir = OP_PRMDIR_NONE;
|
||||
op->op_p[1].val = (uint32_t)ullSectorStart;
|
||||
op->op_p[2].dir = OP_PRMDIR_NONE;
|
||||
op->op_p[2].val = (uint32_t)ulSectorCount;
|
||||
op->op_p[3].dir = OP_PRMDIR_IN;
|
||||
op->op_p[3].val = (uint32_t)pBuffer;
|
||||
op->op_p[3].size = (uint32_t)(ulSectorCount * IOBLKS_SECT_SIZE);
|
||||
return (int)callRemote(op);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Flush the block device.
|
||||
*/
|
||||
REDSTATUS RedOsBDevFlush(uint8_t bVolNum) {
|
||||
if (bVolNum != 0)
|
||||
return -RED_EINVAL;
|
||||
stub_op_t *op = getNewOp(&stub_ctx);
|
||||
op->op_code = IOBLKS_OP_FLUSH;
|
||||
op->op_p[0].dir = OP_PRMDIR_NONE;
|
||||
op->op_p[0].val = (uint32_t)bVolNum;
|
||||
return (REDSTATUS)callRemote(op);
|
||||
}
|
||||
|
||||
/** @} */
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
/*
|
||||
ChibiOS - Copyright (C) 2006..2018 Giovanni Di Sirio.
|
||||
|
||||
This file is part of ChibiOS.
|
||||
|
||||
ChibiOS 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.
|
||||
|
||||
ChibiOS 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/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file tsioblksstub.h
|
||||
* @brief IOBlocks stub module macros and structures.
|
||||
*
|
||||
*/
|
||||
#ifndef TSIOBLKSSTUB_H
|
||||
#define TSIOBLKSSTUB_H
|
||||
|
||||
#include "ch.h"
|
||||
#include "ccportab.h"
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Module constants. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Module pre-compile time settings. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Derived constants and error checks. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Module data structures and types. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Module macros. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* External declarations. */
|
||||
/*===========================================================================*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
THD_FUNCTION(TsIOBlksStubsService, tsstatep);
|
||||
extern THD_WORKING_AREA(waTsIOBlksStubsService, 1024);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Module inline functions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
#endif /* TSIOBLKSSTUB_H */
|
|
@ -0,0 +1,240 @@
|
|||
/*
|
||||
ChibiOS - Copyright (C) 2006..2018 Giovanni Di Sirio
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file tssockstub.c
|
||||
* @brief Sockets stub for trusted services.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "ch.h"
|
||||
#include "chobjfifos.h"
|
||||
#include "chtssi.h"
|
||||
#include "tsproxystubs.h"
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Module local definitions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Module exported variables. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Module local types. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Module local variables. */
|
||||
/*===========================================================================*/
|
||||
static eventflags_t tsSkelIsReadyMask = 0;
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Module local functions. */
|
||||
/*===========================================================================*/
|
||||
static bool isOpValid(stub_ctx_t *scp, stub_op_t *op)
|
||||
{
|
||||
if ((op < &(scp->ops[0])) || (op >= &(scp->ops[STUB_MAX_OPS])))
|
||||
return FALSE;
|
||||
if (((char *)op - (char *)&(scp->ops[0])) % sizeof scp->ops[0])
|
||||
return FALSE;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Module exported functions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief Implement an a call to a NSEC function.
|
||||
* @details It activates the channel between the stubs service and
|
||||
* the skels daemon running in the nsec world.
|
||||
* To do it, it uses an event to signal the corresponding skel
|
||||
* daemon that a new op request is ready to be executed.
|
||||
* Behind the scenes, the skel daemon will then gets the op, calling
|
||||
* the stub service via smc. The daemon executes it and then calls
|
||||
* the stub service again to post the result and to wake up the
|
||||
* calling thread of this function.
|
||||
*
|
||||
* @param[in] op the 'remote' method description.
|
||||
*
|
||||
* @return the return value of 'remote' method.
|
||||
*/
|
||||
uint32_t callRemote(stub_op_t *op) {
|
||||
uint32_t r;
|
||||
|
||||
chSysLock();
|
||||
chFifoSendObjectI(&op->scp->ops_fifo, op);
|
||||
chEvtBroadcastFlagsI(&tsEventSource, op->scp->event_flag);
|
||||
chThdSuspendS(&op->op_wthdp);
|
||||
op->op_state = FREE;
|
||||
chSysUnlock();
|
||||
r = op->op_code;
|
||||
chFifoReturnObject(&op->scp->ops_fifo, op);
|
||||
return r;
|
||||
}
|
||||
|
||||
stub_op_t *getNewOp(stub_ctx_t *scp) {
|
||||
stub_op_t *op = chFifoTakeObjectTimeout(&scp->ops_fifo, TIME_INFINITE);
|
||||
memset(op, 0, sizeof *op);
|
||||
op->op_state = CALLING;
|
||||
op->scp = scp;
|
||||
return op;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief The stubs service.
|
||||
* @details And this is where the magic happens.
|
||||
*/
|
||||
void TsStubService(ts_state_t *svcp, stub_ctx_t *scp) {
|
||||
skel_req_t *skrp;
|
||||
stub_op_t *op;
|
||||
msg_t r;
|
||||
int i;
|
||||
|
||||
chFifoObjectInit(&scp->ops_fifo, sizeof (stub_op_t), STUB_MAX_OPS,
|
||||
sizeof (uint8_t), scp->ops, scp->ops_msgs);
|
||||
for (;/* ever */;) {
|
||||
|
||||
/* Wait a service request.*/
|
||||
(void)tssiWaitRequest(svcp);
|
||||
skrp = (skel_req_t *)TS_GET_DATA(svcp);
|
||||
r = SMC_SVC_OK;
|
||||
|
||||
/* Process the request.*/
|
||||
if (TS_GET_DATALEN(svcp) != sizeof (skel_req_t)) {
|
||||
TS_SET_STATUS(svcp, SMC_SVC_INVALID);
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (skrp->req) {
|
||||
case SKEL_REQ_READY:
|
||||
tsSkelIsReadyMask |= (eventflags_t)skrp->stub_op;
|
||||
break;
|
||||
|
||||
case SKEL_REQ_GETOP:
|
||||
|
||||
/* The nsec skeleton calls us to get a new op ready to be executed.*/
|
||||
if (chFifoReceiveObjectTimeout(&scp->ops_fifo, (void **)&op,
|
||||
TIME_IMMEDIATE) == MSG_TIMEOUT) {
|
||||
|
||||
/* no op ready to be executed.*/
|
||||
r = SMC_SVC_NHND;
|
||||
break;
|
||||
}
|
||||
skrp->stub_op = (uint32_t)op;
|
||||
skrp->stub_op_code = op->op_code;
|
||||
|
||||
/* Pass all the 'by value' arguments from stub to skel.*/
|
||||
for (i = 0; i < METHOD_MAX_PARAMS; ++i) {
|
||||
if (op->op_p[i].dir == OP_PRMDIR_NONE)
|
||||
skrp->stub_op_p[i] = op->op_p[i].val;
|
||||
}
|
||||
op->op_state = PENDING;
|
||||
break;
|
||||
|
||||
case SKEL_REQ_CPYPRMS:
|
||||
|
||||
/* The nsec skel calls us to get a copy of the 'in' parameters of
|
||||
the specified op.
|
||||
An 'in' parameter is an indirect argument, that is an argument
|
||||
the value of which is a pointer to a memory buffer, that
|
||||
must be copied in a non secure memory buffer.
|
||||
It represents data to be consumed by the callee.*/
|
||||
op = (stub_op_t *)skrp->stub_op;
|
||||
if (!isOpValid(scp, op) || op->op_state != PENDING ||
|
||||
op->op_code != skrp->stub_op_code) {
|
||||
r = SMC_SVC_INVALID;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Copy all 'in' parameters.
|
||||
For each parameter check that the destination memory area
|
||||
is in the non secure memory arena.*/
|
||||
for (i = 0; i < METHOD_MAX_PARAMS; ++i) {
|
||||
if ((op->op_p[i].dir & OP_PRMDIR_IN) == 0)
|
||||
continue;
|
||||
if (!tsIsAddrSpaceValid((void *)skrp->stub_op_p[i], op->op_p[i].size)) {
|
||||
r = SMC_SVC_INVALID;
|
||||
break;
|
||||
}
|
||||
memcpy((void *)skrp->stub_op_p[i], (void *)op->op_p[i].val,
|
||||
op->op_p[i].size);
|
||||
}
|
||||
break;
|
||||
|
||||
case SKEL_REQ_PUTRES:
|
||||
|
||||
/* The nsec skel calls us to put a copy of the 'out' parameters of
|
||||
the specified op.
|
||||
An 'out' parameter is an indirect argument, that is an argument
|
||||
the value of which is a pointer to a memory buffer, that
|
||||
must be copied in a secure memory buffer.
|
||||
It represents data produced by the callee.*/
|
||||
op = (stub_op_t *)skrp->stub_op;
|
||||
if (!isOpValid(scp, op) || op->op_state != PENDING ||
|
||||
op->op_code != skrp->stub_op_code) {
|
||||
r = SMC_SVC_INVALID;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Copy all 'out' parameters.
|
||||
For each parameter check that the source memory area
|
||||
is in the non secure memory arena, and that the size returned
|
||||
fits in the caller buffer size.*/
|
||||
for (i = 0; i < METHOD_MAX_PARAMS; ++i) {
|
||||
if ((op->op_p[i].dir & OP_PRMDIR_OUT) == 0)
|
||||
continue;
|
||||
if (!tsIsAddrSpaceValid((void *)skrp->stub_op_p[i], skrp->stub_op_p_sz[i])
|
||||
|| (skrp->stub_op_p_sz[i] > op->op_p[i].size)) {
|
||||
r = SMC_SVC_INVALID;
|
||||
break;
|
||||
}
|
||||
memcpy((void *)op->op_p[i].val, (void *)skrp->stub_op_p[i],
|
||||
skrp->stub_op_p_sz[i]);
|
||||
}
|
||||
if (r != SMC_SVC_OK)
|
||||
break;
|
||||
|
||||
/* Set the return value of the 'remote' callee method,
|
||||
and wake up the caller.*/
|
||||
op->op_code = skrp->stub_op_result;
|
||||
chThdResume(&op->op_wthdp, MSG_OK);
|
||||
break;
|
||||
|
||||
default:
|
||||
r = SMC_SVC_INVALID;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Set the response.*/
|
||||
TS_SET_STATUS(svcp, r);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Is the skeletons daemon ready to operate?
|
||||
* @details It is used at the startup to synchronize the
|
||||
* stub service with the skeleton daemon.
|
||||
*/
|
||||
void tsWaitStubSkelReady(eventflags_t mask) {
|
||||
while ((tsSkelIsReadyMask & mask) != mask) {
|
||||
chThdSleepMilliseconds(100);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,102 @@
|
|||
/*
|
||||
ChibiOS - Copyright (C) 2006..2018 Giovanni Di Sirio.
|
||||
|
||||
This file is part of ChibiOS.
|
||||
|
||||
ChibiOS 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.
|
||||
|
||||
ChibiOS 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/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file tsproxystubs.h
|
||||
* @brief Proxy stubs module macros and structures.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef TSPROXYSTUBS_H
|
||||
#define TSPROXYSTUBS_H
|
||||
|
||||
#include "ch.h"
|
||||
#include "ccportab.h"
|
||||
#include "tscommon.h"
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Module constants. */
|
||||
/*===========================================================================*/
|
||||
#define METHOD_MAX_PARAMS 6
|
||||
#define STUB_MAX_OPS 32
|
||||
|
||||
#define OP_PRMDIR_NONE 0
|
||||
#define OP_PRMDIR_IN 1
|
||||
#define OP_PRMDIR_OUT 2
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Module pre-compile time settings. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Derived constants and error checks. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Module data structures and types. */
|
||||
/*===========================================================================*/
|
||||
|
||||
typedef enum {FREE=0, CALLING, PENDING} op_state_t;
|
||||
typedef struct stub_ctx stub_ctx_t;
|
||||
|
||||
typedef struct stub_param {
|
||||
uint32_t dir;
|
||||
uint32_t val;
|
||||
uint32_t size;
|
||||
} stub_parm_t;
|
||||
|
||||
typedef struct stub_op {
|
||||
uint32_t op_code; /* the stub method op code.*/
|
||||
op_state_t op_state; /* calling, pending, free.*/
|
||||
stub_parm_t op_p[METHOD_MAX_PARAMS];
|
||||
thread_reference_t op_wthdp; /* TS internal client thread (the caller).*/
|
||||
stub_ctx_t *scp; /* the stub ctx this stub_op relates to.*/
|
||||
} stub_op_t;
|
||||
|
||||
typedef struct stub_ctx {
|
||||
eventflags_t event_flag;
|
||||
objects_fifo_t ops_fifo;
|
||||
msg_t ops_msgs[STUB_MAX_OPS];
|
||||
stub_op_t ops[STUB_MAX_OPS];
|
||||
} stub_ctx_t;
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Module macros. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* External declarations. */
|
||||
/*===========================================================================*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
void tsWaitStubSkelReady(eventflags_t mask);
|
||||
void TsStubService(ts_state_t *svcp, stub_ctx_t *scp);
|
||||
uint32_t callRemote(stub_op_t *op);
|
||||
stub_op_t *getNewOp(stub_ctx_t *scp);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Module inline functions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
#endif /* TSPROXYSTUBS_H */
|
|
@ -0,0 +1,74 @@
|
|||
/*
|
||||
ChibiOS - Copyright (C) 2006..2018 Giovanni Di Sirio.
|
||||
|
||||
This file is part of ChibiOS.
|
||||
|
||||
ChibiOS 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.
|
||||
|
||||
ChibiOS 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/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file tssockets.h
|
||||
*/
|
||||
|
||||
#ifndef TSSOCKETS_H
|
||||
#define TSSOCKETS_H
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Module constants. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Module pre-compile time settings. */
|
||||
/*===========================================================================*/
|
||||
#define socket ts_socket
|
||||
#define connect ts_connect
|
||||
#define send ts_send
|
||||
#define recv ts_recv
|
||||
#define select ts_select
|
||||
#define close ts_close
|
||||
#define bind ts_bind
|
||||
#define listen ts_listen
|
||||
#define write ts_write
|
||||
#define read ts_read
|
||||
#define getaddrinfo ts_getaddrinfo
|
||||
#define freeaddrinfo ts_freeaddrinfo
|
||||
|
||||
#define fd_set ts_fd_set
|
||||
#define timeval ts_timeval
|
||||
|
||||
#define FD_ZERO TS_FD_ZERO
|
||||
#define FD_SET TS_FD_SET
|
||||
#define FD_CLR TS_FD_CLR
|
||||
#define FD_ISSET TS_FD_ISSET
|
||||
/*===========================================================================*/
|
||||
/* Derived constants and error checks. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Module data structures and types. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Module macros. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* External declarations. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Module inline functions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
#endif /* TSSOCKETS_H */
|
|
@ -23,6 +23,7 @@
|
|||
#include "ch.h"
|
||||
#include "chobjfifos.h"
|
||||
#include "chtssi.h"
|
||||
#include "tsproxystubs.h"
|
||||
#include "tssockstub.h"
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
@ -31,13 +32,6 @@
|
|||
/* Module local definitions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
#define METHOD_MAX_PARAMS 6
|
||||
#define STUB_MAX_OPS 32
|
||||
|
||||
#define OP_PRMDIR_NONE 0
|
||||
#define OP_PRMDIR_IN 1
|
||||
#define OP_PRMDIR_OUT 2
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Module exported variables. */
|
||||
/*===========================================================================*/
|
||||
|
@ -45,231 +39,35 @@
|
|||
/*===========================================================================*/
|
||||
/* Module local types. */
|
||||
/*===========================================================================*/
|
||||
typedef struct stub_op stub_op_t;
|
||||
typedef enum {FREE=0, CALLING, PENDING} op_state_t;
|
||||
|
||||
typedef struct stub_param {
|
||||
uint32_t dir;
|
||||
uint32_t val;
|
||||
uint32_t size;
|
||||
} stub_parm_t;
|
||||
|
||||
typedef struct stub_op {
|
||||
uint32_t op_code; /* e.g. connect, recv, sendv, close, etc.*/
|
||||
op_state_t op_state; /* calling, pending, free.*/
|
||||
stub_parm_t op_p[METHOD_MAX_PARAMS];
|
||||
thread_reference_t op_wthdp; /* TS internal client thread (the caller).*/
|
||||
} stub_op_t;
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Module local variables. */
|
||||
/*===========================================================================*/
|
||||
|
||||
static objects_fifo_t ops_fifo;
|
||||
static msg_t ops_msgs[STUB_MAX_OPS];
|
||||
static struct stub_op ops[STUB_MAX_OPS] = {0};
|
||||
static bool tsSkelIsReady = false;
|
||||
static stub_ctx_t stub_ctx;
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Module local functions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
static bool isOpValid(stub_op_t *op)
|
||||
{
|
||||
if ((op < &ops[0]) || (op >= &ops[STUB_MAX_OPS]))
|
||||
return FALSE;
|
||||
if (((char *)op - (char *)&ops[0]) % sizeof ops[0])
|
||||
return FALSE;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Implement an a call to a NSEC function.
|
||||
* @details It activates the channel between the stubs service and
|
||||
* the skels daemon running in the nsec world.
|
||||
* To do it, it uses an event to signal the skels
|
||||
* daemon that a new op request is ready to be executed.
|
||||
* Behind the scenes, the skels daemon will then gets the op, calling
|
||||
* the stubs service via smc. The daemon executes it and then calls
|
||||
* the stubs service again to post the result and to wake up the
|
||||
* calling thread of this function.
|
||||
*
|
||||
* @param[in] op the 'remote' method description.
|
||||
*
|
||||
* @return the return value of 'remote' method.
|
||||
*/
|
||||
static uint32_t callRemote(stub_op_t *op) {
|
||||
uint32_t r;
|
||||
|
||||
chSysLock();
|
||||
chFifoSendObjectI(&ops_fifo, op);
|
||||
chEvtBroadcastFlagsI(&tsEventSource, EVT_F_SOCK_NEW_OP);
|
||||
chThdSuspendS(&op->op_wthdp);
|
||||
chSysUnlock();
|
||||
r = op->op_code;
|
||||
chFifoReturnObject(&ops_fifo, op);
|
||||
return r;
|
||||
}
|
||||
|
||||
static stub_op_t *getNewOp(void) {
|
||||
stub_op_t *op = chFifoTakeObjectTimeout(&ops_fifo, TIME_INFINITE);
|
||||
memset(op, 0, sizeof *op);
|
||||
op->op_state = CALLING;
|
||||
return op;
|
||||
}
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Module exported functions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief The stubs service.
|
||||
* @details And this is where the magic happens.
|
||||
* @brief The sockets stub service.
|
||||
*/
|
||||
THD_WORKING_AREA(waTsStubsService, 1024);
|
||||
THD_FUNCTION(TsStubsService, tsstate) {
|
||||
ts_state_t *svcp = tsstate;
|
||||
skel_req_t *skrp;
|
||||
stub_op_t *op;
|
||||
msg_t r;
|
||||
int i;
|
||||
|
||||
chFifoObjectInit(&ops_fifo, sizeof (stub_op_t), STUB_MAX_OPS,
|
||||
sizeof (uint8_t), ops, ops_msgs);
|
||||
for (;/* ever */;) {
|
||||
|
||||
/* Wait a service request.*/
|
||||
(void)tssiWaitRequest(svcp);
|
||||
skrp = (skel_req_t *)TS_GET_DATA(svcp);
|
||||
r = SMC_SVC_OK;
|
||||
|
||||
/* Process the request.*/
|
||||
if (TS_GET_DATALEN(svcp) != sizeof (skel_req_t)) {
|
||||
TS_SET_STATUS(svcp, SMC_SVC_INVALID);
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (skrp->req) {
|
||||
case SKEL_REQ_READY:
|
||||
tsSkelIsReady = true;
|
||||
break;
|
||||
|
||||
case SKEL_REQ_GETOP:
|
||||
|
||||
/* The nsec skeleton calls us to get a new op ready to be executed.*/
|
||||
if (chFifoReceiveObjectTimeout(&ops_fifo, (void **)&op, TIME_IMMEDIATE) ==
|
||||
MSG_TIMEOUT) {
|
||||
|
||||
/* no op ready to be executed.*/
|
||||
r = SMC_SVC_NHND;
|
||||
break;
|
||||
}
|
||||
skrp->stub_op = (uint32_t)op;
|
||||
skrp->stub_op_code = op->op_code;
|
||||
|
||||
/* Pass all the 'by value' arguments from stub to skel.*/
|
||||
for (i = 0; i < METHOD_MAX_PARAMS; ++i) {
|
||||
if (op->op_p[i].dir == OP_PRMDIR_NONE)
|
||||
skrp->stub_op_p[i] = op->op_p[i].val;
|
||||
}
|
||||
op->op_state = PENDING;
|
||||
break;
|
||||
|
||||
case SKEL_REQ_CPYPRMS:
|
||||
|
||||
/* The nsec skel calls us to get a copy of the 'in' parameters of
|
||||
the specified op.
|
||||
An 'in' parameter is an indirect argument, that is an argument
|
||||
the value of which is a pointer to a memory buffer, that
|
||||
must be copied in a non secure memory buffer.
|
||||
It represents data to be consumed by the callee.*/
|
||||
op = (stub_op_t *)skrp->stub_op;
|
||||
if (!isOpValid(op) || op->op_state != PENDING ||
|
||||
op->op_code != skrp->stub_op_code) {
|
||||
r = SMC_SVC_INVALID;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Copy all 'in' parameters.
|
||||
For each parameter check that the destination memory area
|
||||
is in the non secure memory arena.*/
|
||||
for (i = 0; i < METHOD_MAX_PARAMS; ++i) {
|
||||
if ((op->op_p[i].dir & OP_PRMDIR_IN) == 0)
|
||||
continue;
|
||||
if (!tsIsAddrSpaceValid((void *)skrp->stub_op_p[i], op->op_p[i].size)) {
|
||||
r = SMC_SVC_INVALID;
|
||||
break;
|
||||
}
|
||||
memcpy((void *)skrp->stub_op_p[i], (void *)op->op_p[i].val,
|
||||
op->op_p[i].size);
|
||||
}
|
||||
break;
|
||||
|
||||
case SKEL_REQ_PUTRES:
|
||||
|
||||
/* The nsec skel calls us to put a copy of the 'out' parameters of
|
||||
the specified op.
|
||||
An 'out' parameter is an indirect argument, that is an argument
|
||||
the value of which is a pointer to a memory buffer, that
|
||||
must be copied in a secure memory buffer.
|
||||
It represents data produced by the callee.*/
|
||||
op = (stub_op_t *)skrp->stub_op;
|
||||
if (!isOpValid(op) || op->op_state != PENDING ||
|
||||
op->op_code != skrp->stub_op_code) {
|
||||
r = SMC_SVC_INVALID;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Copy all 'out' parameters.
|
||||
For each parameter check that the source memory area
|
||||
is in the non secure memory arena, and that the size returned
|
||||
fits in the caller buffer size.*/
|
||||
for (i = 0; i < METHOD_MAX_PARAMS; ++i) {
|
||||
if ((op->op_p[i].dir & OP_PRMDIR_OUT) == 0)
|
||||
continue;
|
||||
if (!tsIsAddrSpaceValid((void *)skrp->stub_op_p[i], skrp->stub_op_p_sz[i])
|
||||
|| (skrp->stub_op_p_sz[i] > op->op_p[i].size)) {
|
||||
r = SMC_SVC_INVALID;
|
||||
break;
|
||||
}
|
||||
memcpy((void *)op->op_p[i].val, (void *)skrp->stub_op_p[i],
|
||||
skrp->stub_op_p_sz[i]);
|
||||
}
|
||||
if (r != SMC_SVC_OK)
|
||||
break;
|
||||
|
||||
/* Set the return value of the 'remote' callee method,
|
||||
and wake up the caller.*/
|
||||
op->op_code = skrp->stub_op_result;
|
||||
chThdResume(&op->op_wthdp, MSG_OK);
|
||||
break;
|
||||
|
||||
default:
|
||||
r = SMC_SVC_INVALID;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Set the response.*/
|
||||
TS_SET_STATUS(svcp, r);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @brief Is the skeletons daemon ready to operate?
|
||||
* @details It is used at the startup to synchronize the
|
||||
* stub service with the skeleton daemon.
|
||||
*/
|
||||
void tsWaitStubSkelReady(void) {
|
||||
while (!tsSkelIsReady) {
|
||||
chThdSleepMilliseconds(100);
|
||||
}
|
||||
THD_WORKING_AREA(waTsSocksStubsService, 1024);
|
||||
THD_FUNCTION(TsSocksStubsService, tsstatep) {
|
||||
stub_ctx.event_flag = EVT_F_SOCK_NEW_OP;
|
||||
TsStubService((ts_state_t *)tsstatep, &stub_ctx);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief The sockets API.
|
||||
*/
|
||||
int socket(int domain, int type, int protocol) {
|
||||
stub_op_t *op = getNewOp();
|
||||
op->op_code = STUB_OP_SOCKET;
|
||||
stub_op_t *op = getNewOp(&stub_ctx);
|
||||
op->op_code = SOCK_OP_SOCKET;
|
||||
op->op_p[0].dir = OP_PRMDIR_NONE;
|
||||
op->op_p[0].val = (uint32_t)domain;
|
||||
op->op_p[1].dir = OP_PRMDIR_NONE;
|
||||
|
@ -280,8 +78,8 @@ int socket(int domain, int type, int protocol) {
|
|||
}
|
||||
|
||||
int connect(int s, const struct sockaddr *name, socklen_t namelen) {
|
||||
stub_op_t *op = getNewOp();
|
||||
op->op_code = STUB_OP_CONNECT;
|
||||
stub_op_t *op = getNewOp(&stub_ctx);
|
||||
op->op_code = SOCK_OP_CONNECT;
|
||||
op->op_p[0].dir = OP_PRMDIR_NONE;
|
||||
op->op_p[0].val = (uint32_t)s;
|
||||
op->op_p[1].dir = OP_PRMDIR_IN;
|
||||
|
@ -293,16 +91,16 @@ int connect(int s, const struct sockaddr *name, socklen_t namelen) {
|
|||
}
|
||||
|
||||
int close(int s) {
|
||||
stub_op_t *op = getNewOp();
|
||||
op->op_code = STUB_OP_CLOSE;
|
||||
stub_op_t *op = getNewOp(&stub_ctx);
|
||||
op->op_code = SOCK_OP_CLOSE;
|
||||
op->op_p[0].dir = OP_PRMDIR_NONE;
|
||||
op->op_p[0].val = (uint32_t)s;
|
||||
return (int)callRemote(op);
|
||||
}
|
||||
|
||||
int recv(int s, void *mem, size_t len, int flags) {
|
||||
stub_op_t *op = getNewOp();
|
||||
op->op_code = STUB_OP_RECV;
|
||||
stub_op_t *op = getNewOp(&stub_ctx);
|
||||
op->op_code = SOCK_OP_RECV;
|
||||
op->op_p[0].dir = OP_PRMDIR_NONE;
|
||||
op->op_p[0].val = (uint32_t)s;
|
||||
op->op_p[1].dir = OP_PRMDIR_OUT;
|
||||
|
@ -316,8 +114,8 @@ int recv(int s, void *mem, size_t len, int flags) {
|
|||
}
|
||||
|
||||
int send(int s, const void *dataptr, size_t size, int flags) {
|
||||
stub_op_t *op = getNewOp();
|
||||
op->op_code = STUB_OP_SEND;
|
||||
stub_op_t *op = getNewOp(&stub_ctx);
|
||||
op->op_code = SOCK_OP_SEND;
|
||||
op->op_p[0].dir = OP_PRMDIR_NONE;
|
||||
op->op_p[0].val = (uint32_t)s;
|
||||
op->op_p[1].dir = OP_PRMDIR_IN;
|
||||
|
@ -332,8 +130,8 @@ int send(int s, const void *dataptr, size_t size, int flags) {
|
|||
|
||||
int select(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset,
|
||||
struct timeval *timeout) {
|
||||
stub_op_t *op = getNewOp();
|
||||
op->op_code = STUB_OP_SELECT;
|
||||
stub_op_t *op = getNewOp(&stub_ctx);
|
||||
op->op_code = SOCK_OP_SELECT;
|
||||
op->op_p[0].dir = OP_PRMDIR_NONE;
|
||||
op->op_p[0].val = (uint32_t)maxfdp1;
|
||||
op->op_p[1].dir = OP_PRMDIR_IN|OP_PRMDIR_OUT;
|
||||
|
@ -352,8 +150,8 @@ int select(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset,
|
|||
}
|
||||
|
||||
int bind(int s, const struct sockaddr *name, socklen_t namelen) {
|
||||
stub_op_t *op = getNewOp();
|
||||
op->op_code = STUB_OP_BIND;
|
||||
stub_op_t *op = getNewOp(&stub_ctx);
|
||||
op->op_code = SOCK_OP_BIND;
|
||||
op->op_p[0].dir = OP_PRMDIR_NONE;
|
||||
op->op_p[0].val = (uint32_t)s;
|
||||
op->op_p[1].dir = OP_PRMDIR_IN;
|
||||
|
@ -365,8 +163,8 @@ int bind(int s, const struct sockaddr *name, socklen_t namelen) {
|
|||
}
|
||||
|
||||
int listen(int s, int backlog) {
|
||||
stub_op_t *op = getNewOp();
|
||||
op->op_code = STUB_OP_LISTEN;
|
||||
stub_op_t *op = getNewOp(&stub_ctx);
|
||||
op->op_code = SOCK_OP_LISTEN;
|
||||
op->op_p[0].dir = OP_PRMDIR_NONE;
|
||||
op->op_p[0].val = (uint32_t)s;
|
||||
op->op_p[1].dir = OP_PRMDIR_NONE;
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
|
||||
#include "ch.h"
|
||||
#include "ccportab.h"
|
||||
#include "tscommon.h"
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Module constants. */
|
||||
/*===========================================================================*/
|
||||
|
@ -376,9 +376,8 @@ extern "C" {
|
|||
int listen(int s, int backlog);
|
||||
in_addr_t inet_addr(const char *cp);
|
||||
int inet_aton(const char *cp, struct in_addr *addr);
|
||||
void tsWaitStubSkelReady(void);
|
||||
THD_FUNCTION(TsStubsService, tsstate);
|
||||
extern THD_WORKING_AREA(waTsStubsService, 1024);
|
||||
THD_FUNCTION(TsSocksStubsService, tsstatep);
|
||||
extern THD_WORKING_AREA(waTsSocksStubsService, 1024);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
Loading…
Reference in New Issue