704 lines
13 KiB
C
704 lines
13 KiB
C
/*
|
|
* ISDN4Linux Remote-CAPI Server
|
|
*
|
|
* Copyright 1998 by Christian A. Lademann (cal@zls.de)
|
|
*
|
|
* 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, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
*
|
|
*/
|
|
|
|
#include <sys/time.h>
|
|
#include <sys/types.h>
|
|
#include <unistd.h>
|
|
#include <stdio.h>
|
|
#include <fcntl.h>
|
|
#include <stdlib.h>
|
|
#include <ctype.h>
|
|
#include <string.h>
|
|
#include "capicmd.h"
|
|
#include "rcapicmd.h"
|
|
#include "capi20.h"
|
|
|
|
#define RCAPID_VERSION_INFO "CAPI 2.0 / RCAPID by Christian A. Lademann"
|
|
|
|
#define MAX_CAPIMSGLEN 3072 + 128 + 10 // 64 * 1024 does not seem to be necessary -- KG
|
|
|
|
#define LOGFILE_PATH_TEMPL "/tmp/rcapid.log"
|
|
char logfile_path [64];
|
|
FILE *logfile = NULL;
|
|
int loglevel = 0;
|
|
|
|
#define log(level,args...) (void)(((level) <= loglevel) && \
|
|
((logfile || (logfile = fopen(logfile_path, "a+"))) && \
|
|
fprintf(logfile, "%d: ", getpid()) && \
|
|
fprintf(logfile, ## args) && \
|
|
fflush(logfile)))
|
|
char logbuf [10240], *lptr;
|
|
|
|
|
|
typedef unsigned char byte;
|
|
typedef unsigned short word;
|
|
typedef unsigned int dword;
|
|
|
|
typedef struct {
|
|
int len;
|
|
char *data;
|
|
} structure;
|
|
|
|
|
|
struct capi_message {
|
|
structure raw;
|
|
word TotalLength,
|
|
ApplID;
|
|
byte Command,
|
|
Subcommand;
|
|
word MessageNumber;
|
|
structure Parameters;
|
|
};
|
|
|
|
|
|
word remote_ApplID, local_ApplID;
|
|
int capi_fd = -1;
|
|
|
|
|
|
#define SIZE_TABMNR2DHDL 16
|
|
struct {
|
|
int used;
|
|
word MessageNumber;
|
|
word DataHandle;
|
|
} tabMnr2Dhdl [SIZE_TABMNR2DHDL];
|
|
|
|
int tabMnr2Dhdl_initd = 0;
|
|
|
|
|
|
int
|
|
setDataHandle(word mnr, word dhdl) {
|
|
int i;
|
|
|
|
if(! tabMnr2Dhdl_initd) {
|
|
for(i = 0; i < SIZE_TABMNR2DHDL; i++)
|
|
tabMnr2Dhdl [i] .used = 0;
|
|
|
|
tabMnr2Dhdl_initd = 1;
|
|
}
|
|
|
|
for(i = 0; i < SIZE_TABMNR2DHDL; i++)
|
|
if(! tabMnr2Dhdl [i] .used) {
|
|
tabMnr2Dhdl [i] .used = 1;
|
|
tabMnr2Dhdl [i] .MessageNumber = mnr;
|
|
tabMnr2Dhdl [i] .DataHandle = dhdl;
|
|
|
|
log(9, "set mnr/dhdl (0x%04x, 0x%04x)\n", mnr, dhdl);
|
|
|
|
return(0);
|
|
}
|
|
|
|
return(-1);
|
|
}
|
|
|
|
|
|
int
|
|
getDataHandle(word mnr, word *dhdl) {
|
|
int i;
|
|
|
|
if(! tabMnr2Dhdl_initd)
|
|
return(-1);
|
|
|
|
for(i = 0; i < SIZE_TABMNR2DHDL; i++)
|
|
if(tabMnr2Dhdl [i] .used && tabMnr2Dhdl [i] .MessageNumber == mnr) {
|
|
*dhdl = tabMnr2Dhdl [i] .DataHandle;
|
|
tabMnr2Dhdl [i] .used = 0;
|
|
|
|
log(9, "get mnr/dhdl (0x%04x, 0x%04x)\n", mnr, *dhdl);
|
|
return(0);
|
|
}
|
|
|
|
return(1);
|
|
}
|
|
|
|
|
|
void
|
|
log_hd(int level, char *buf, int len) {
|
|
char hline [50], aline [17], *p, *q;
|
|
unsigned char c;
|
|
int i;
|
|
|
|
|
|
if(level > loglevel)
|
|
return;
|
|
|
|
memset(hline, 0, sizeof(hline));
|
|
memset(aline, 0, sizeof(aline));
|
|
|
|
p = hline;
|
|
q = aline;
|
|
|
|
for(i = 0; i < len; i++) {
|
|
c = (unsigned char)buf [i];
|
|
|
|
p += sprintf(p, "%02x ", c);
|
|
if(i % 8 == 0)
|
|
p += sprintf(p, " ");
|
|
|
|
q += sprintf(q, "%c", (isprint(c) ? c : '.'));
|
|
|
|
if(i == len - 1 || (i + 1) % 16 == 0) {
|
|
log(level, " %-50.50s%s\n", hline, aline);
|
|
|
|
p = hline;
|
|
q = aline;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
byte
|
|
get_byte(char **p) {
|
|
*p += 1;
|
|
|
|
return((unsigned char)*(*p - 1));
|
|
}
|
|
|
|
|
|
word
|
|
get_word(char **p) {
|
|
return(get_byte(p) | (get_byte(p) << 8));
|
|
}
|
|
|
|
|
|
word
|
|
get_netword(char **p) {
|
|
return((get_byte(p) << 8) | get_byte(p));
|
|
}
|
|
|
|
|
|
dword
|
|
get_dword(char **p) {
|
|
return(get_word(p) | (get_word(p) << 16));
|
|
}
|
|
|
|
|
|
char *
|
|
put_byte(char **p, byte val) {
|
|
**p = val;
|
|
*p += 1;
|
|
return(*p);
|
|
}
|
|
|
|
|
|
char *
|
|
put_word(char **p, word val) {
|
|
put_byte(p, val & 0xff);
|
|
put_byte(p, (val & 0xff00) >> 8);
|
|
return(*p);
|
|
}
|
|
|
|
|
|
char *
|
|
put_netword(char **p, word val) {
|
|
put_byte(p, (val & 0xff00) >> 8);
|
|
put_byte(p, val & 0xff);
|
|
return(*p);
|
|
}
|
|
|
|
|
|
char *
|
|
put_dword(char **p, dword val) {
|
|
put_word(p, val & 0xffff);
|
|
put_word(p, (val & 0xffff0000) >> 16);
|
|
return(*p);
|
|
}
|
|
|
|
|
|
char *
|
|
put_struct(char **p, int len, char *val) {
|
|
if(len < 255)
|
|
put_byte(p, len);
|
|
else {
|
|
put_byte(p, 0xff);
|
|
put_word(p, len);
|
|
}
|
|
|
|
if(len) {
|
|
memcpy(*p, val, len);
|
|
*p += len;
|
|
}
|
|
|
|
return(*p);
|
|
}
|
|
|
|
|
|
int
|
|
getMessageFromBuffer(struct capi_message *msg, char *buf, int len) {
|
|
char *p = buf;
|
|
|
|
msg->raw.len = len;
|
|
msg->raw.data = buf;
|
|
msg->TotalLength = get_word(&p);
|
|
msg->ApplID = get_word(&p);
|
|
msg->Command = get_byte(&p);
|
|
msg->Subcommand = get_byte(&p);
|
|
msg->MessageNumber = get_word(&p);
|
|
if(p - buf < len) {
|
|
msg->Parameters.len = p - buf + 1;
|
|
msg->Parameters.data = p;
|
|
} else {
|
|
msg->Parameters.len = 0;
|
|
msg->Parameters.data = NULL;
|
|
}
|
|
|
|
log(9, "gotMessageFromBuffer: cmd=(%02x/%02x)\n", msg->Command, msg->Subcommand);
|
|
|
|
return(0);
|
|
}
|
|
|
|
|
|
int
|
|
rcv_message(struct capi_message *msg) {
|
|
static char buf [MAX_CAPIMSGLEN],
|
|
*p;
|
|
int len, rlen;
|
|
|
|
|
|
if(read(0, buf, 2) == 2) {
|
|
p = buf;
|
|
len = (int)get_netword(&p);
|
|
|
|
log(5, "rcapi-message: len=%d (%2x%2x)\n", len, buf [0], buf [1]);
|
|
len -= 2;
|
|
if((rlen = read(0, buf, len)) != len) {
|
|
log(5, "short message received! (received=%d, expected=%d)\n", rlen, len);
|
|
/* exit(1); */
|
|
return(-1);
|
|
}
|
|
|
|
log_hd(6, buf, len);
|
|
|
|
getMessageFromBuffer(msg, buf, rlen);
|
|
|
|
return(0);
|
|
}
|
|
|
|
return(-1);
|
|
}
|
|
|
|
|
|
int
|
|
snd_message(struct capi_message *orig, int cmd, structure *params, structure *data) {
|
|
static char buf [MAX_CAPIMSGLEN],
|
|
*p;
|
|
int len, rlen, slen;
|
|
|
|
|
|
len = 8;
|
|
if(params)
|
|
len += params->len;
|
|
|
|
rlen = len + 2;
|
|
if(data)
|
|
rlen += data->len;
|
|
|
|
p = buf;
|
|
put_netword(&p, rlen);
|
|
|
|
put_word(&p, len);
|
|
put_word(&p, orig->ApplID);
|
|
put_byte(&p, (cmd >> 8) & 0xff);
|
|
put_byte(&p, cmd & 0xff);
|
|
put_word(&p, orig->MessageNumber);
|
|
|
|
if(params) {
|
|
memcpy(p, params->data, params->len);
|
|
p += params->len;
|
|
}
|
|
|
|
if(data) {
|
|
memcpy(p, data->data, data->len);
|
|
p += data->len;
|
|
}
|
|
|
|
log(5, "send rcapi-message: %d bytes\n", rlen);
|
|
log_hd(6, buf, rlen);
|
|
|
|
if((slen = write(1, buf, rlen)) < rlen) {
|
|
log(5, "sending interrupted (%d of %d bytes).\n", slen, rlen);
|
|
return(-1);
|
|
}
|
|
|
|
return(0);
|
|
}
|
|
|
|
|
|
int
|
|
hdl_RCAPI_REGISTER_REQ(struct capi_message *msg) {
|
|
CAPI_REGISTER_ERROR err;
|
|
char *p;
|
|
dword Buffer;
|
|
word messageBufferSize,
|
|
maxLogicalConnections,
|
|
maxBDataBlocks,
|
|
maxBDataLen;
|
|
byte capiVersion;
|
|
int capierr = 0;
|
|
|
|
structure retstruct;
|
|
char retstr [8];
|
|
|
|
|
|
if(! (p = msg->Parameters.data)) {
|
|
log(5, "RCAPI_REGISTER_REQ: parameters missing.\n");
|
|
return(-1);
|
|
}
|
|
|
|
Buffer = get_dword(&p);
|
|
messageBufferSize = get_word(&p);
|
|
maxLogicalConnections = get_word(&p);
|
|
maxBDataBlocks = get_word(&p);
|
|
maxBDataLen = get_word(&p);
|
|
capiVersion = get_byte(&p);
|
|
|
|
log(5, "RCAPI_REGISTER_REQ\n");
|
|
log(5, "\tBuffer: 0x%x\n", Buffer);
|
|
log(5, "\tmessageBufferSize: %d\n", messageBufferSize);
|
|
log(5, "\tmaxLogicalConnections: %d\n", maxLogicalConnections);
|
|
log(5, "\tmaxBDataBlocks: %d\n", maxBDataBlocks);
|
|
log(5, "\tmaxBDataLen: %d\n", maxBDataLen);
|
|
log(5, "\tcapiVersion: %d\n", capiVersion);
|
|
|
|
|
|
p = retstr;
|
|
if(capiVersion == 2) {
|
|
if((err = CAPI20_REGISTER(maxLogicalConnections, maxBDataBlocks, maxBDataLen, &capi_fd)) < 0) {
|
|
capierr = err;
|
|
log(5, "registration not successful\n");
|
|
} else {
|
|
local_ApplID = capi_fd;
|
|
log(5, "registration successful: appl_id = %d\n", capi_fd);
|
|
capi_fd = capi20_fileno(capi_fd);
|
|
log(5, " capi_fd = %d\n", capi_fd);
|
|
}
|
|
} else
|
|
capierr = 0x0003;
|
|
|
|
put_word(&p, capierr);
|
|
|
|
retstruct.len = sizeof(word);
|
|
retstruct.data = retstr;
|
|
|
|
return(snd_message(msg, RCAPI_REGISTER_CONF, &retstruct, NULL));
|
|
}
|
|
|
|
|
|
int
|
|
hdl_RCAPI_GET_SERIAL_NUMBER_REQ(struct capi_message *msg) {
|
|
char *p;
|
|
|
|
structure retstruct;
|
|
char retval [80];
|
|
char serial_number [64];
|
|
|
|
|
|
log(5, "RCAPI_GET_SERIAL_NUMBER_REQ\n");
|
|
|
|
CAPI20_GET_SERIAL_NUMBER(0, serial_number);
|
|
memset(serial_number + strlen(serial_number), 0, sizeof(serial_number) - strlen(serial_number));
|
|
|
|
p = retval;
|
|
put_struct(&p, 64, serial_number);
|
|
|
|
retstruct.len = p - retval;
|
|
retstruct.data = (char *)&retval;
|
|
|
|
return(snd_message(msg, RCAPI_GET_SERIAL_NUMBER_CONF, &retstruct, NULL));
|
|
}
|
|
|
|
|
|
int
|
|
hdl_RCAPI_GET_MANUFACTURER_REQ(struct capi_message *msg) {
|
|
char *p;
|
|
|
|
structure retstruct;
|
|
char retval [80];
|
|
char manufacturer [64] = {0,};
|
|
|
|
|
|
log(5, "RCAPI_GET_MANUFACTURER_REQ\n");
|
|
|
|
CAPI20_GET_MANUFACTURER(0, manufacturer);
|
|
|
|
p = retval;
|
|
put_struct(&p, 64, manufacturer);
|
|
|
|
retstruct.len = p - retval;
|
|
retstruct.data = (char *)&retval;
|
|
|
|
return(snd_message(msg, RCAPI_GET_MANUFACTURER_CONF, &retstruct, NULL));
|
|
}
|
|
|
|
|
|
int
|
|
hdl_RCAPI_GET_VERSION_REQ(struct capi_message *msg) {
|
|
char *p;
|
|
|
|
structure retstruct;
|
|
char retval [80];
|
|
char info [64];
|
|
|
|
|
|
log(5, "RCAPI_GET_VERSION_REQ\n");
|
|
|
|
memset(info, 0, sizeof(info));
|
|
sprintf(info, "%s", RCAPID_VERSION_INFO);
|
|
memset(info + strlen(info), 0, sizeof(info) - strlen(info));
|
|
|
|
p = retval;
|
|
put_byte(&p, 2);
|
|
put_byte(&p, 0);
|
|
put_byte(&p, 0);
|
|
put_byte(&p, 0);
|
|
put_struct(&p, 64, info);
|
|
|
|
retstruct.len = p - retval;
|
|
retstruct.data = (char *)&retval;
|
|
|
|
return(snd_message(msg, RCAPI_GET_VERSION_CONF, &retstruct, NULL));
|
|
}
|
|
|
|
|
|
int
|
|
hdl_RCAPI_GET_PROFILE_REQ(struct capi_message *msg) {
|
|
word CtrlNr;
|
|
char *p;
|
|
|
|
structure retstruct;
|
|
char retval [80];
|
|
|
|
|
|
log(5, "RCAPI_GET_PROFILE_REQ\n");
|
|
|
|
if(! (p = msg->Parameters.data)) {
|
|
log(5, "RCAPI_GET_PROFILE_REG: parameters missing.\n");
|
|
return(-1);
|
|
}
|
|
|
|
CtrlNr = get_dword(&p);
|
|
log(5, "\tCtrlNr: %d\n", CtrlNr);
|
|
|
|
memset(retval, 0, sizeof(retval));
|
|
*(unsigned short *)retval = CAPI20_GET_PROFILE(CtrlNr, retval+2);
|
|
retstruct.len = 66;
|
|
retstruct.data = retval;
|
|
|
|
return(snd_message(msg, RCAPI_GET_PROFILE_CONF, &retstruct, NULL));
|
|
}
|
|
|
|
|
|
int
|
|
hdl_RCAPI_AUTH_USER_REQ(struct capi_message *msg)
|
|
{
|
|
word w1,w2,b1;
|
|
char *p;
|
|
|
|
structure retstruct;
|
|
char retval [80];
|
|
|
|
|
|
log(5, "RCAPI_AUTH_USER_REQ\n");
|
|
|
|
if(! (p = msg->Parameters.data)) {
|
|
log(5, "RCAPI_AUTH_USER_REQ: parameters missing.\n");
|
|
return(-1);
|
|
}
|
|
|
|
|
|
w1 = get_word(&p);
|
|
w2 = get_word(&p);
|
|
b1 = get_byte(&p);
|
|
log(5, "w1 0x%4x w2 0x%4x b1 0x%2x", w1,w2,b1);
|
|
|
|
p = retval;
|
|
#if 0
|
|
put_word(&p, w1);
|
|
put_word(&p, w2);
|
|
put_byte(&p, b1);
|
|
put_byte(&p, 0);
|
|
#endif
|
|
put_word(&p, 0);
|
|
put_word(&p, 0x19);
|
|
put_word(&p, 0);
|
|
|
|
retstruct.len = p - retval;
|
|
retstruct.data = (char *)&retval;
|
|
|
|
return(snd_message(msg, RCAPI_AUTH_USER_CONF, &retstruct, NULL));
|
|
}
|
|
|
|
int
|
|
hdl_raw(struct capi_message *msg) {
|
|
char *p;
|
|
word dhdl;
|
|
|
|
if(capi_fd < 0)
|
|
return(-1);
|
|
|
|
p = msg->raw.data + 2;
|
|
put_word(&p, local_ApplID);
|
|
|
|
log(5, "CAPICMD = %x\n", (int)CAPICMD(msg->Command, msg->Subcommand));
|
|
switch(CAPICMD(msg->Command, msg->Subcommand)) {
|
|
case CAPI_DATA_B3_RESP:
|
|
if(getDataHandle(msg->MessageNumber, &dhdl) == 0) {
|
|
p = msg->raw.data + 12;
|
|
put_word(&p, dhdl);
|
|
}
|
|
}
|
|
|
|
log(5, "forward raw message: %d bytes, capi_fd = %d\n", msg->raw.len, capi_fd);
|
|
log_hd(6, msg->raw.data, msg->raw.len);
|
|
|
|
write(capi_fd, msg->raw.data, msg->raw.len);
|
|
return(0);
|
|
}
|
|
|
|
|
|
int
|
|
hdl_message(struct capi_message *msg) {
|
|
log(5, "message: Tl=%d, AI=%x, C=%x, Sc=%x, Mn=%d\n",
|
|
msg->TotalLength,
|
|
msg->ApplID,
|
|
msg->Command,
|
|
msg->Subcommand,
|
|
msg->MessageNumber);
|
|
|
|
remote_ApplID = msg->ApplID;
|
|
|
|
switch(CAPICMD(msg->Command, msg->Subcommand)) {
|
|
case RCAPI_REGISTER_REQ:
|
|
return(hdl_RCAPI_REGISTER_REQ(msg));
|
|
break;
|
|
|
|
case RCAPI_GET_SERIAL_NUMBER_REQ:
|
|
return(hdl_RCAPI_GET_SERIAL_NUMBER_REQ(msg));
|
|
break;
|
|
|
|
case RCAPI_GET_MANUFACTURER_REQ:
|
|
return(hdl_RCAPI_GET_MANUFACTURER_REQ(msg));
|
|
break;
|
|
|
|
case RCAPI_GET_VERSION_REQ:
|
|
return(hdl_RCAPI_GET_VERSION_REQ(msg));
|
|
break;
|
|
|
|
case RCAPI_GET_PROFILE_REQ:
|
|
return(hdl_RCAPI_GET_PROFILE_REQ(msg));
|
|
break;
|
|
|
|
case RCAPI_AUTH_USER_REQ:
|
|
return(hdl_RCAPI_AUTH_USER_REQ(msg));
|
|
break;
|
|
|
|
default:
|
|
msg->ApplID = local_ApplID;
|
|
return(hdl_raw(msg));
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
int main(int argc, char *argv []) {
|
|
struct capi_message msg;
|
|
extern int optind;
|
|
extern char *optarg;
|
|
char c;
|
|
// int i;
|
|
|
|
loglevel = 0;
|
|
|
|
while((c = getopt(argc, argv, "l:")) != EOF)
|
|
switch(c) {
|
|
case 'l': loglevel = atoi(optarg); break;
|
|
}
|
|
|
|
|
|
sprintf(logfile_path, "/tmp/rcapid.log");
|
|
log(5, "rcapid started (PID=%d)\n", getpid());
|
|
|
|
while(! feof(stdin)) {
|
|
fd_set ifd, efd;
|
|
int max_fd, s;
|
|
|
|
|
|
FD_ZERO(&ifd);
|
|
FD_ZERO(&efd);
|
|
FD_SET(0, &ifd);
|
|
FD_SET(0, &efd);
|
|
max_fd = 1;
|
|
if(capi_fd >= 0) {
|
|
FD_SET(capi_fd, &ifd);
|
|
max_fd = capi_fd + 1;
|
|
}
|
|
|
|
s = select(max_fd, &ifd, NULL, &efd, NULL);
|
|
if(s <= 0) {
|
|
log(5, "exit: %s, %d\n", __FILE__, __LINE__);
|
|
exit(1);
|
|
}
|
|
|
|
if(FD_ISSET(0, &ifd)) {
|
|
if(rcv_message(&msg) == 0)
|
|
hdl_message(&msg);
|
|
else
|
|
exit(1);
|
|
}
|
|
|
|
if(capi_fd >= 0 && FD_ISSET(capi_fd, &ifd)) {
|
|
char buf [MAX_CAPIMSGLEN], *p;
|
|
int len, i;
|
|
struct capi_message msg;
|
|
word dhdl;
|
|
|
|
if((len = read(capi_fd, buf + 2, sizeof(buf) - 2)) <= 0)
|
|
continue;
|
|
|
|
p = buf;
|
|
len += 2;
|
|
put_netword(&p, len);
|
|
p = buf + 4;
|
|
put_word(&p, remote_ApplID);
|
|
|
|
log(5, "forward raw answer: %d bytes\n", len);
|
|
log_hd(6, buf, len);
|
|
|
|
i = write(1, buf, len);
|
|
log(5, "written %d bytes\n", i);
|
|
|
|
getMessageFromBuffer(&msg, buf + 2, len - 2);
|
|
switch(CAPICMD(msg.Command, msg.Subcommand)) {
|
|
case CAPI_DATA_B3_IND:
|
|
p = msg.raw.data + 18;
|
|
dhdl = get_word(&p);
|
|
setDataHandle(msg.MessageNumber, dhdl);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(FD_ISSET(0, &efd)) {
|
|
log(5, "exit: %s, %d\n", __FILE__, __LINE__);
|
|
exit(0);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|