isdn4k-utils/capi20/capi_mod_rcapi.c

509 lines
13 KiB
C

/*****************************************************************
* CAPI 2.0 Library *
* All parts are distributed under the terms of GPL. See COPYING *
*****************************************************************/
/**
* \file capi_mod_rcapi.c
* \brief Melware remote capi implementation
*/
#include <sys/socket.h>
#include <netdb.h>
#include <string.h>
#include <stdio.h>
#include <fcntl.h>
#include <linux/capi.h>
#include <errno.h>
#include <unistd.h>
#include "capi20.h"
#include "capi_mod.h"
#include "capi_mod_fritzbox.h"
/* REMOTE-CAPI commands */
#define RCAPI_REGISTER_REQ CAPICMD(0xf2, 0xff)
#define RCAPI_REGISTER_CONF CAPICMD(0xf3, 0xff)
#define RCAPI_GET_MANUFACTURER_REQ CAPICMD(0xfa, 0xff)
#define RCAPI_GET_MANUFACTURER_CONF CAPICMD(0xfb, 0xff)
#define RCAPI_GET_SERIAL_NUMBER_REQ CAPICMD(0xfe, 0xff)
#define RCAPI_GET_SERIAL_NUMBER_CONF CAPICMD(0xff, 0xff)
#define RCAPI_GET_VERSION_REQ CAPICMD(0xfc, 0xff)
#define RCAPI_GET_VERSION_CONF CAPICMD(0xfd, 0xff)
#define RCAPI_GET_PROFILE_REQ CAPICMD(0xe0, 0xff)
#define RCAPI_GET_PROFILE_CONF CAPICMD(0xe1, 0xff)
#define RCAPI_AUTH_USER_REQ CAPICMD(0xff, 0x00)
#define RCAPI_AUTH_USER_CONF CAPICMD(0xff, 0x01)
/* add a fixed trace file for now */
static char *rcapiTraceFile = "/tmp/rcapiTraceFile";
static char *getTraceFile(void)
{
return rcapiTraceFile;
}
/**
* \brief Create a socket to hostname:port
* \return socket number
*/
static int rcapiOpenSocket( void ) {
struct hostent *psHostInfo;
struct sockaddr_in sAddr;
int nHandle;
/* Create new socket */
nHandle = socket( PF_INET, SOCK_STREAM, 0 );
if ( nHandle == -1 ) {
return nHandle;
}
sAddr.sin_family = PF_INET;
sAddr.sin_port = htons( getPort() );
/* Retrieve hostname information */
psHostInfo = gethostbyname( getHostName() );
if ( psHostInfo != NULL ) {
sAddr.sin_addr = *( struct in_addr * ) psHostInfo -> h_addr;
/* Connect socket to address */
if ( !connect( nHandle, ( struct sockaddr * ) &sAddr, sizeof( sAddr ) ) ) {
/* no errors, return handle */
return nHandle;
}
}
close( nHandle );
return -1;
}
/**
* \brief Read data from socket
* \param nHandle socket handle
* \param pnBuffer buffer pointer
* \param nLen length of buffer
* \return read len
*/
static int rcapiReadSocket( int nHandle, unsigned char *pnBuffer, int nLen ) {
unsigned char anTemp[ 4096 ], *pnPtr;
int nTotLen, nReadLen, nActLen, nOrigLen;
time_t nTime;
/* try to read the header, 2 bytes */
if( read( nHandle, anTemp, 2 ) == 2 ) {
pnPtr = anTemp;
/* Get message len */
nTotLen = nOrigLen = ( int ) get_netword( &pnPtr ) - 2;
nTime = time( NULL );
nReadLen = 0;
/* read message */
while ( ( ( nActLen = read( nHandle, &anTemp[ nReadLen ], nTotLen ) ) < nTotLen) && ( time( NULL ) < ( nTime + 5 ) ) ) {
if ( nActLen > 0 ) {
nTotLen -= nActLen;
nReadLen += nActLen;
}
nActLen = 0;
}
if ( nActLen > 0 ) {
nReadLen += nActLen;
}
if ( nReadLen != nOrigLen ) {
return 0;
}
if ( !nLen ) {
nLen = nOrigLen;
}
nOrigLen = ( nLen < nReadLen ) ? nLen : nReadLen;
/* copy informations to buffer, cut to nLen if needed */
memcpy( pnBuffer, anTemp, nOrigLen );
return nOrigLen;
}
/* could not read header, return 0 */
return 0;
}
/**
* \brief Send message to socket and wait for response
* \param nHandle socket handle
* \param pnBuffer data buffer pointer
* \param nLen number of bytes to write from pnBuffer
* \param nConf current configuration id
* \return number of bytes read
*/
static int rcapiRemoteCommand( int nHandle, unsigned char *pnBuffer, int nLen, int nConf ) {
unsigned char *pnPtr;
int nNum;
/* write message to socket */
if ( write( nHandle, pnBuffer, nLen ) < nLen ) {
return 0;
}
/* read data */
if ( ( nLen = rcapiReadSocket( nHandle, pnBuffer, 0 ) ) < 1 ) {
return 0;
}
pnPtr = pnBuffer + 4;
/* check if'we got the current configuration */
if ( get_netword( &pnPtr ) == nConf ) {
memmove( pnBuffer, pnBuffer + 8, nLen - 8 );
return nLen - 8;
}
return 0;
}
/**
* \brief Add standard rcapi header to buffer pointer
* \param ppnPtr data buffer pointer
* \param nLen length of message
* \param nCmd command id
* \param nCtrl controller
*/
static void rcapiSetHeader( unsigned char **ppnPtr, int nLen, _cword nCmd, _cdword nCtrl ) {
/* length of message */
put_netword( ppnPtr, nLen );
put_word( ppnPtr, 0 );
put_word( ppnPtr, 0 );
/* command id */
put_netword( ppnPtr, nCmd );
put_word( ppnPtr, 0 );
/* controller id */
put_dword( ppnPtr, nCtrl );
}
/**
* \brief Debug purpose, write capi data to file
* \param nSend send
* \param pnBuffer data buffer
* \param nLength length of buffer
* \param nDataMsg data message len
*/
static void rcapiWriteCapiTrace(int nSend, unsigned char *pnBuffer, int nLength, int nDataMsg ) {
int nHandle;
_cdword nTime;
unsigned char anHeader[ 7 ];
char *pnTraceFile = getTraceFile();
if ( strlen( pnTraceFile ) <= 0 ) {
return;
}
if ( getTraceLevel() < ( nDataMsg + 1 ) ) {
return;
}
nHandle = open( pnTraceFile, O_WRONLY | O_CREAT | O_APPEND, 0644 );
if ( nHandle >= 0 ) {
nTime = ( _cdword ) time( NULL );
capimsg_setu16( anHeader, 0, nLength + sizeof( anHeader ) );
capimsg_setu32( anHeader, 2, nTime );
anHeader[ 6 ] = ( nSend ) ? 0x80 : 0x81;
close( nHandle );
}
}
/**
* \brief Check if rcapi interface is available
* \return file descriptor of socket, or error code
*/
static unsigned rcapiIsInstalled( void ) {
unsigned nHandle;
if ( strlen( getHostName() ) <= 0 || getPort() == -1 ) {
return -1;
}
/* we check if we can open a new socket to hostname:port */
nHandle = rcapiOpenSocket();
/* return new handle or error code */
return nHandle;
}
/**
* \brief Register at rcapi
* \param nMaxB3Connection maximum b3 connection
* \param nMaxB3Blks maximum b3 blocks
* \param nMaxSizeB3 maximum b3 size
* \param pnApplId pointer where we store the new application id
* \return new socket handle
*/
static unsigned rcapiRegister( unsigned nMaxB3Connection, unsigned nMaxB3Blks, unsigned nMaxSizeB3, unsigned *pnApplId ) {
unsigned char anBuf[ 100 ], *pnPtr = anBuf;
int nSock, nErrCode;;
/* Safety: Set application id to invalid (-1) */
*pnApplId = -1;
/* open a new socket for communication */
nSock = rcapiOpenSocket();
if ( nSock < 0 ) {
return nSock;
}
rcapiSetHeader( &pnPtr, 23, RCAPI_REGISTER_REQ, 0 );
/* Fake size */
put_word( &pnPtr, 2048 );
put_word( &pnPtr, nMaxB3Connection );
put_word( &pnPtr, nMaxB3Blks );
put_word( &pnPtr, nMaxSizeB3 );
/* CAPI version */
put_word( &pnPtr, 2 );
/* Send message to socket and wait for answer */
if ( !( rcapiRemoteCommand( nSock, anBuf, 23, RCAPI_REGISTER_CONF ) ) ) {
close( nSock );
return -2;
}
pnPtr = anBuf;
nErrCode = get_word( &pnPtr );
if ( nErrCode == CapiNoError ) {
/* No error, allocate new applid and set it to pnApplId */
*pnApplId = capi_alloc_applid(nSock);
} else {
/* wuhh, error occured, close socket and return -1 */
close( nSock );
nSock = -1;
}
return nSock;
}
/**
* \brief Put capi message to rcapi
* \param nSock socket handle
* \param nApplId application id
* \param pnMsg message pointer
* \return error code
*/
static unsigned rcapiPutMessage( int nSock, unsigned nApplId, unsigned char *pnMsg ) {
unsigned char anSendBuffer[ SEND_BUFSIZ ], *pnSendBuffer = anSendBuffer;
int nLen = CAPIMSG_LEN( pnMsg );
int nCommand = CAPIMSG_COMMAND( pnMsg );
int nSubCommand = CAPIMSG_SUBCOMMAND( pnMsg );
int nNum;
nLen = capi_processMessage( pnMsg, nApplId, nCommand, nSubCommand, nLen );
nLen += 2;
put_netword( &pnSendBuffer, nLen );
memcpy( pnSendBuffer, pnMsg, nLen );
/* write data to socket */
nNum = write( nSock, anSendBuffer, nLen );
if ( nNum != nLen ) {
return CapiMsgOSResourceErr;
}
return CapiNoError;
}
/**
* \brief Get message from rcapi
* \param nSock socket handle
* \param nApplId application id
* \param ppnBuffer pointer to data buffer pointer (where we store the data)
* \return error code
*/
static unsigned rcapiGetMessage( int nSock, unsigned nApplId, unsigned char **ppnBuffer ) {
unsigned char *pnBuffer;
unsigned nOffset;
size_t nBufSize;
int nRet;
/* try to get a new buffer from queue */
if ( ( *ppnBuffer = pnBuffer = ( unsigned char * ) capi_get_buffer( nApplId, &nBufSize, &nOffset ) ) == 0 ) {
return CapiMsgOSResourceErr;
}
/* Get message */
nRet = rcapiReadSocket( nSock, pnBuffer, nBufSize );
if ( nRet > 0 ) {
/* workaround for old driver */
CAPIMSG_SETAPPID( pnBuffer, nApplId );
/* DATA_B3? Then set buffer address */
if ( ( CAPIMSG_COMMAND( pnBuffer ) == CAPI_DATA_B3 ) && ( CAPIMSG_SUBCOMMAND( pnBuffer ) == CAPI_IND ) ) {
capi_save_datahandle( nApplId, nOffset, CAPIMSG_U16( pnBuffer, 18 ), CAPIMSG_U32( pnBuffer, 8 ) );
/* patch datahandle */
capimsg_setu16( pnBuffer, 18, nOffset );
if ( sizeof( void * ) == 4 ) {
u_int32_t nData = ( u_int32_t ) pnBuffer + CAPIMSG_LEN( pnBuffer );
pnBuffer[ 12 ] = nData & 0xFF;
pnBuffer[ 13 ] = ( nData >> 8 ) & 0xFF;
pnBuffer[ 14 ] = ( nData >> 16 ) & 0xFF;
pnBuffer[ 15 ] = ( nData >> 24 ) & 0xFF;
} else {
u_int64_t nData;
if ( CAPIMSG_LEN( pnBuffer ) < 30 ) {
memmove( pnBuffer + 30, pnBuffer + CAPIMSG_LEN( pnBuffer ), CAPIMSG_DATALEN( pnBuffer ) );
pnBuffer[ 0 ] = 30;
pnBuffer[ 1 ] = 0;
}
nData = ( ( ( ulong ) pnBuffer ) + CAPIMSG_LEN( pnBuffer ) );
pnBuffer[ 12 ] = pnBuffer[ 13 ] = pnBuffer[ 14 ] = pnBuffer[ 15 ] = 0;
pnBuffer[ 22 ] = nData & 0xFF;
pnBuffer[ 23 ] = ( nData >> 8 ) & 0xFF;
pnBuffer[ 24 ] = ( nData >> 16 ) & 0xFF;
pnBuffer[ 25 ] = ( nData >> 24 ) & 0xFF;
pnBuffer[ 26 ] = ( nData >> 32 ) & 0xFF;
pnBuffer[ 27 ] = ( nData >> 40 ) & 0xFF;
pnBuffer[ 28 ] = ( nData >> 48 ) & 0xFF;
pnBuffer[ 29 ] = ( nData >> 56 ) & 0xFF;
}
/* keep buffer */
return CapiNoError;
}
/* buffer is not needed, return it */
capi_return_buffer( nApplId, nOffset );
if ( ( CAPIMSG_COMMAND( pnBuffer ) == CAPI_DISCONNECT) && ( CAPIMSG_SUBCOMMAND( pnBuffer ) == CAPI_IND ) ) {
cleanup_buffers_for_plci( nApplId, CAPIMSG_U32( pnBuffer, 8 ) );
}
return CapiNoError;
}
capi_return_buffer( nApplId, nOffset );
if ( nRet == 0 ) {
return CapiReceiveQueueEmpty;
}
switch ( errno ) {
case EMSGSIZE:
nRet = CapiIllCmdOrSubcmdOrMsgToSmall;
break;
case EAGAIN:
nRet = CapiReceiveQueueEmpty;
break;
default:
nRet = CapiMsgOSResourceErr;
break;
}
return nRet;
}
/**
* \brief Get manufactor informations
* \param nHandle socket handle
* \param nController controller id
* \param pnBuffer buffer pointer we write our informations to
* \return pnBuffer
*/
static unsigned char *rcapiGetManufactor( int nHandle, unsigned nController, unsigned char *pnBuffer ) {
unsigned char anBuf[ 100 ];
unsigned char *pnPtr = anBuf;
rcapiSetHeader( &pnPtr, 14, RCAPI_GET_MANUFACTURER_REQ, nController );
if ( !( rcapiRemoteCommand( nHandle, anBuf, 14, RCAPI_GET_MANUFACTURER_CONF ) ) ) {
return 0;
}
memcpy( pnBuffer, anBuf + 1, CAPI_MANUFACTURER_LEN );
pnBuffer[ CAPI_MANUFACTURER_LEN - 1 ] = 0;
return pnBuffer;
}
/**
* \brief Get version informations
* \param nHandle socket handle
* \param nController controller id
* \param pnBuffer buffer pointer we write our informations to
* \return pnBuffer
*/
static unsigned char *rcapiGetVersion( int nHandle, unsigned nController, unsigned char *pnBuffer ) {
unsigned char anBuf[ 100] ;
unsigned char *pnPtr = anBuf;
rcapiSetHeader( &pnPtr, 14, RCAPI_GET_VERSION_REQ, nController );
if ( !( rcapiRemoteCommand( nHandle, anBuf, 14, RCAPI_GET_VERSION_CONF ) ) ) {
return 0;
}
memcpy( pnBuffer, anBuf + 1, sizeof( capi_version ) );
return pnBuffer;
}
/**
* \brief Get serial number informations
* \param nHandle socket handle
* \param nController controller id
* \param pnBuffer buffer pointer we write our informations to
* \return pnBuffer
*/
static unsigned char *rcapiGetSerialNumber( int nHandle, unsigned nController, unsigned char *pnBuffer ) {
unsigned char anBuf[ 100 ];
unsigned char *pnPtr = anBuf;
rcapiSetHeader( &pnPtr, 14, RCAPI_GET_SERIAL_NUMBER_REQ, nController );
if ( !( rcapiRemoteCommand( nHandle, anBuf, 14, RCAPI_GET_SERIAL_NUMBER_CONF ) ) ) {
return 0;
}
memcpy( pnBuffer, anBuf + 1, CAPI_SERIAL_LEN );
pnBuffer[ CAPI_SERIAL_LEN - 1 ] = 0;
return pnBuffer;
}
/**
* \brief Get profile from fritzbox
* \param nHandle socket handle
* \param nControllerId controller
* \param pnBuf buffer
* \return error code
*/
static unsigned rcapiGetProfile( int nHandle, unsigned nController, unsigned char *pnBuf ) {
unsigned char anBuf[ 100] ;
unsigned char *pnPtr = anBuf;
rcapiSetHeader( &pnPtr, 14, RCAPI_GET_PROFILE_REQ, nController );
if ( !( rcapiRemoteCommand( nHandle, anBuf, 14, RCAPI_GET_PROFILE_CONF ) ) ) {
return CapiMsgOSResourceErr;
}
if( *( unsigned short * ) anBuf == CapiNoError) {
memcpy( pnBuf, anBuf + 2, ( nController ) ? sizeof( struct capi_profile ) : 2 );
}
return *( unsigned short * ) anBuf;
}
/** Module operations structure */
static struct sModuleOperations sRemoteCapi = {
rcapiIsInstalled,
rcapiRegister,
NULL,
rcapiPutMessage,
rcapiGetMessage,
rcapiGetManufactor,
rcapiGetVersion,
rcapiGetSerialNumber,
rcapiGetProfile,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL
};
MODULE_INIT( "rcapi", &sRemoteCapi );