osmo-cc-misdn-endpoint/src/isdn/ie.c

1311 lines
31 KiB
C
Raw Normal View History

2020-01-25 07:50:20 +00:00
/*****************************************************************************\
** **
** PBX4Linux **
** **
**---------------------------------------------------------------------------**
** Copyright: Andreas Eversberg **
** **
** information elements encode and decode **
** **
\*****************************************************************************/
#include <string.h>
#include <stdint.h>
#include <errno.h>
#include "../libdebug/debug.h"
#ifndef u_char
#define u_char unsigned char
#endif
#include <mISDN/mlayer3.h>
#include <mISDN/q931.h>
#include "ie.h"
/*
the pointer of enc_ie_* always points to the IE itself
if qi is not NULL (TE-mode), offset is set
*/
/* support stuff */
static void strnncpy(char *dst, uint8_t *src, int len, int dst_len)
{
if (len > dst_len - 1)
len = dst_len - 1;
memcpy(dst, src, len);
dst[len] = '\0';
}
/* IE_COMPLETE */
void enc_ie_complete(struct l3_msg *l3m, int complete)
{
PDEBUG(DDSS1, DEBUG_DEBUG, " Encode IE COMPLETE\n");
if (complete<0 || complete>1) {
PDEBUG(DDSS1, DEBUG_ERROR, "complete(%d) is out of range.\n", complete);
return;
}
if (complete) {
PDEBUG(DDSS1, DEBUG_DEBUG, " -> complete\n");
l3m->sending_complete++;
}
}
void dec_ie_complete(struct l3_msg *l3m, int *complete)
{
*complete = 0;
// special case: p is not a pointer, it's a value
uint8_t p = l3m->sending_complete;
if (p) {
*complete = 1;
PDEBUG(DDSS1, DEBUG_DEBUG, " Decode IE COMPLETE\n");
}
if (*complete)
PDEBUG(DDSS1, DEBUG_DEBUG, " -> complete\n");
}
/* IE_BEARER */
void enc_ie_bearer(struct l3_msg *l3m, uint8_t coding, uint8_t capability, int has_mode, uint8_t mode, uint8_t rate, int has_multi, uint8_t multi, int has_user, uint8_t user)
{
uint8_t p[256];
int l;
PDEBUG(DDSS1, DEBUG_DEBUG, " Encode IE BEARER\n");
if (coding > 3) {
PDEBUG(DDSS1, DEBUG_ERROR, "coding(%d) is out of range.\n", coding);
return;
}
if (capability > 31) {
PDEBUG(DDSS1, DEBUG_ERROR, "capability(%d) is out of range.\n", capability);
return;
}
if (mode > 3) {
PDEBUG(DDSS1, DEBUG_ERROR, "mode(%d) is out of range.\n", mode);
return;
}
if (rate > 31) {
PDEBUG(DDSS1, DEBUG_ERROR, "rate(%d) is out of range.\n", rate);
return;
}
if (multi > 127) {
PDEBUG(DDSS1, DEBUG_ERROR, "multi(%d) is out of range.\n", multi);
return;
}
if (user > 31) {
PDEBUG(DDSS1, DEBUG_ERROR, "user L1(%d) is out of range.\n", user);
return;
}
if (rate != 24 && has_multi) {
PDEBUG(DDSS1, DEBUG_ERROR, "multi(%d) is only possible if rate(%d) would be 24.\n", multi, rate);
has_multi = 0;
}
PDEBUG(DDSS1, DEBUG_DEBUG, " -> coding = %d\n", coding);
PDEBUG(DDSS1, DEBUG_DEBUG, " -> capability = %d\n", capability);
if (has_mode) {
PDEBUG(DDSS1, DEBUG_DEBUG, " -> mode = %d\n", mode);
PDEBUG(DDSS1, DEBUG_DEBUG, " -> rate = %d\n", rate);
}
if (has_multi)
PDEBUG(DDSS1, DEBUG_DEBUG, " -> multi = %d\n", multi);
if (has_user)
PDEBUG(DDSS1, DEBUG_DEBUG, " -> user = %d\n", user);
l = 1 + (!!has_mode) + (!!has_multi) + (!!has_user);
p[0] = IE_BEARER;
p[1] = l;
p[2] = 0x80 + (coding << 5) + capability;
if (has_mode) {
p[3] = 0x80 + (mode << 5) + rate;
if (has_multi)
p[4] = 0x80 + multi;
if (has_user)
p[4 + (!!has_multi)] = 0xa0 + user;
}
add_layer3_ie(l3m, p[0], p[1], p + 2);
}
int dec_ie_bearer(struct l3_msg *l3m, uint8_t *coding, uint8_t *capability, int *has_mode, uint8_t *mode, uint8_t *rate, int *has_multi, uint8_t *multi, int *has_user, uint8_t *user)
{
uint8_t *p = l3m->bearer_capability;
if (!p)
return -EINVAL;
PDEBUG(DDSS1, DEBUG_DEBUG, " Decode IE BEARER\n");
if (p[0] < 2) {
PDEBUG(DDSS1, DEBUG_DEBUG, " -> error IE too short (len=%d)\n", p[0]);
return -EINVAL;
}
*has_mode = 0;
*has_multi = 0;
*has_user = 0;
*coding = (p[1]&0x60) >> 5;
*capability = p[1] & 0x1f;
if (p[0]>=2) {
*has_mode = 1;
*mode = (p[2]&0x60) >> 5;
*rate = p[2] & 0x1f;
}
if (p[0]>=3 && *rate==0x18) {
*has_multi = 1;
*multi = p[3] & 0x7f;
if (p[0]>=4) {
*has_user = 1;
*user = p[4] & 0x1f;
}
} else {
if (p[0]>=3) {
*has_user = 1;
*user = p[3] & 0x1f;
}
}
PDEBUG(DDSS1, DEBUG_DEBUG, " -> coding = %d\n", *coding);
PDEBUG(DDSS1, DEBUG_DEBUG, " -> capability = %d\n", *capability);
if (*has_mode) {
PDEBUG(DDSS1, DEBUG_DEBUG, " -> mode = %d\n", *mode);
PDEBUG(DDSS1, DEBUG_DEBUG, " -> rate = %d\n", *rate);
}
if (*has_multi)
PDEBUG(DDSS1, DEBUG_DEBUG, " -> multi = %d\n", *multi);
if (*has_user)
PDEBUG(DDSS1, DEBUG_DEBUG, " -> user = %d\n", *user);
return 0;
}
/* IE_HLC */
void enc_ie_hlc(struct l3_msg *l3m, uint8_t coding, uint8_t interpretation, uint8_t presentation, uint8_t hlc, int has_exthlc, uint8_t exthlc)
{
uint8_t p[256];
int l;
PDEBUG(DDSS1, DEBUG_DEBUG, " Encode IE HLC\n");
if (coding > 3) {
PDEBUG(DDSS1, DEBUG_ERROR, "coding(%d) is out of range.\n", coding);
return;
}
if (interpretation > 7) {
PDEBUG(DDSS1, DEBUG_ERROR, "interpretation(%d) is out of range.\n", interpretation);
return;
}
if (presentation > 3) {
PDEBUG(DDSS1, DEBUG_ERROR, "presentation(%d) is out of range.\n", presentation);
return;
}
if (hlc > 127) {
PDEBUG(DDSS1, DEBUG_ERROR, "hlc(%d) is out of range.\n", hlc);
return;
}
if (exthlc > 127) {
PDEBUG(DDSS1, DEBUG_ERROR, "hlc(%d) is out of range.\n", exthlc);
return;
}
PDEBUG(DDSS1, DEBUG_DEBUG, " -> coding = %d\n", coding);
PDEBUG(DDSS1, DEBUG_DEBUG, " -> interpretation = %d\n", interpretation);
PDEBUG(DDSS1, DEBUG_DEBUG, " -> presentation = %d\n", presentation);
PDEBUG(DDSS1, DEBUG_DEBUG, " -> hlc = %d\n", hlc);
if (has_exthlc)
PDEBUG(DDSS1, DEBUG_DEBUG, " -> exthlc = %d\n", exthlc);
l = 2 + (!!has_exthlc);
p[0] = IE_HLC;
p[1] = l;
p[2] = 0x80 + (coding << 5) + (interpretation << 2) + presentation;
if (has_exthlc) {
p[3] = hlc;
p[4] = 0x80 + exthlc;
} else
p[3] = 0x80 + hlc;
add_layer3_ie(l3m, p[0], p[1], p+2);
}
int dec_ie_hlc(struct l3_msg *l3m, uint8_t *coding, uint8_t *interpretation, uint8_t *presentation, uint8_t *hlc, int *has_exthlc, uint8_t *exthlc)
{
*has_exthlc = 0;
uint8_t *p = l3m->hlc;
if (!p)
return -EINVAL;
PDEBUG(DDSS1, DEBUG_DEBUG, " Decode IE HLC\n");
if (p[0] < 2) {
PDEBUG(DDSS1, DEBUG_DEBUG, " -> error IE too short (len=%d)\n", p[0]);
return -EINVAL;
}
*coding = (p[1]&0x60) >> 5;
*interpretation = (p[1]&0x1c) >> 2;
*presentation = p[1] & 0x03;
*hlc = p[2] & 0x7f;
if (p[0]>=3) {
*has_exthlc = 1;
*exthlc = p[3] & 0x7f;
}
PDEBUG(DDSS1, DEBUG_DEBUG, " -> coding = %d\n", *coding);
PDEBUG(DDSS1, DEBUG_DEBUG, " -> interpretation = %d\n", *interpretation);
PDEBUG(DDSS1, DEBUG_DEBUG, " -> presentation = %d\n", *presentation);
PDEBUG(DDSS1, DEBUG_DEBUG, " -> hlc = %d\n", *hlc);
if (*has_exthlc)
PDEBUG(DDSS1, DEBUG_DEBUG, " -> exthlc = %d\n", *exthlc);
return 0;
}
/* IE_CALL_ID */
void enc_ie_call_id(struct l3_msg *l3m, uint8_t *callid, int callid_len)
{
uint8_t p[256];
int l;
PDEBUG(DDSS1, DEBUG_DEBUG, " Encode IE CALL ID\n");
if (callid_len == 0) {
return;
}
if (callid_len > 8) {
PDEBUG(DDSS1, DEBUG_ERROR, "callid_len(%d) is out of range.\n", callid_len);
return;
}
PDEBUG(DDSS1, DEBUG_DEBUG, " -> value = %s\n", debug_hex(callid, callid_len));
l = callid_len;
p[0] = IE_CALL_ID;
p[1] = l;
memcpy(p+2, callid, callid_len);
add_layer3_ie(l3m, p[0], p[1], p + 2);
}
int dec_ie_call_id(struct l3_msg *l3m, uint8_t *callid, int *callid_len)
{
uint8_t *p = l3m->call_id;
if (!p)
return -EINVAL;
PDEBUG(DDSS1, DEBUG_DEBUG, " Decode IE CALL ID\n");
if (p[0] > 8) {
PDEBUG(DDSS1, DEBUG_DEBUG, " -> error IE too long (len=%d)\n", p[0]);
return -EINVAL;
}
*callid_len = p[0];
memcpy(callid, p+1, *callid_len);
PDEBUG(DDSS1, DEBUG_DEBUG, " -> value = %s\n", debug_hex(callid, *callid_len));
return 0;
}
/* IE_CALLED_PN */
void enc_ie_called_pn(struct l3_msg *l3m, uint8_t type, uint8_t plan, char *number)
{
uint8_t p[256];
int l;
PDEBUG(DDSS1, DEBUG_DEBUG, " Encode IE CALLED PN\n");
if (type > 7) {
PDEBUG(DDSS1, DEBUG_ERROR, "type(%d) is out of range.\n", type);
return;
}
if (plan > 15) {
PDEBUG(DDSS1, DEBUG_ERROR, "plan(%d) is out of range.\n", plan);
return;
}
if (!number[0]) {
PDEBUG(DDSS1, DEBUG_ERROR, "number is not given.\n");
return;
}
PDEBUG(DDSS1, DEBUG_DEBUG, " -> type = %d\n", type);
PDEBUG(DDSS1, DEBUG_DEBUG, " -> plan = %d\n", plan);
PDEBUG(DDSS1, DEBUG_DEBUG, " -> number = %s\n", number);
l = 1;
if (number[0])
l += strlen(number);
p[0] = IE_CALLED_PN;
p[1] = l;
p[2] = 0x80 + (type << 4) + plan;
memcpy(p + 3, number, strlen(number));
add_layer3_ie(l3m, p[0], p[1], p + 2);
}
int dec_ie_called_pn(struct l3_msg *l3m, uint8_t *type, uint8_t *plan, char *number, int number_len)
{
uint8_t *p = l3m->called_nr;
if (!p)
return -EINVAL;
PDEBUG(DDSS1, DEBUG_DEBUG, " Decode IE CALLED PN\n");
if (p[0] < 2) {
PDEBUG(DDSS1, DEBUG_DEBUG, " -> error IE too short (len=%d)\n", p[0]);
return -EINVAL;
}
*type = (p[1]&0x70) >> 4;
*plan = p[1] & 0xf;
strnncpy(number, p + 2, p[0] - 1, number_len);
PDEBUG(DDSS1, DEBUG_DEBUG, " -> type = %d\n", *type);
PDEBUG(DDSS1, DEBUG_DEBUG, " -> plan = %d\n", *plan);
PDEBUG(DDSS1, DEBUG_DEBUG, " -> number = %s\n", number);
return 0;
}
/* IE_CALLING_PN */
void enc_ie_calling_pn(struct l3_msg *l3m, uint8_t type, uint8_t plan, int has_present, uint8_t present, uint8_t screen, char *number)
{
uint8_t p[256];
int l;
PDEBUG(DDSS1, DEBUG_DEBUG, " Encode IE CALLING PN\n");
if (type > 7) {
PDEBUG(DDSS1, DEBUG_ERROR, "type(%d) is out of range.\n", type);
return;
}
if (plan > 15) {
PDEBUG(DDSS1, DEBUG_ERROR, "plan(%d) is out of range.\n", plan);
return;
}
if (present > 3) {
PDEBUG(DDSS1, DEBUG_ERROR, "present(%d) is out of range.\n", present);
return;
}
if (screen > 3) {
PDEBUG(DDSS1, DEBUG_ERROR, "screen(%d) is out of range.\n", screen);
return;
}
PDEBUG(DDSS1, DEBUG_DEBUG, " -> type = %d\n", type);
PDEBUG(DDSS1, DEBUG_DEBUG, " -> plan = %d\n", plan);
if (has_present) {
PDEBUG(DDSS1, DEBUG_DEBUG, " -> present = %d\n", present);
PDEBUG(DDSS1, DEBUG_DEBUG, " -> screen = %d\n", screen);
}
PDEBUG(DDSS1, DEBUG_DEBUG, " -> number = %s\n", number);
l = 1;
if (number[0])
l += strlen(number);
if (has_present)
l += 1;
p[0] = IE_CALLING_PN;
p[1] = l;
if (has_present) {
p[2] = 0x00 + (type << 4) + plan;
p[3] = 0x80 + (present << 5) + screen;
memcpy(p + 4, number, strlen(number));
} else {
p[2] = 0x80 + (type << 4) + plan;
memcpy(p + 3, number, strlen(number));
}
add_layer3_ie(l3m, p[0], p[1], p + 2);
}
int dec_ie_calling_pn(struct l3_msg *l3m, int secondary_ie, uint8_t *type, uint8_t *plan, int *has_present, uint8_t *present, uint8_t *screen, char *number, int number_len)
{
uint8_t *p;
*has_present = 0;
if (secondary_ie) {
unsigned int numextra = sizeof(l3m->extra) / sizeof(struct m_extie);
unsigned int i;
/* second calling party number */
p = NULL;
i = 0;
while(i < numextra) {
if (!l3m->extra[i].val)
break;
if (l3m->extra[i].ie == IE_CALLING_PN) {
p = l3m->extra[i].val;
break;
}
i++;
}
} else {
p = l3m->calling_nr;
}
if (!p)
return -EINVAL;
PDEBUG(DDSS1, DEBUG_DEBUG, " Decode IE CALLING PN\n");
if (p[0] < 1) {
PDEBUG(DDSS1, DEBUG_DEBUG, " -> error IE too short (len=%d)\n", p[0]);
return -EINVAL;
}
*type = (p[1] & 0x70) >> 4;
*plan = p[1] & 0xf;
if (!(p[1] & 0x80)) {
if (p[0] < 2) {
PDEBUG(DDSS1, DEBUG_DEBUG, " -> error IE too short (len=%d)\n", p[0]);
return -EINVAL;
}
*has_present = 1;
*present = (p[2]&0x60) >> 5;
*screen = p[2] & 0x3;
strnncpy(number, p + 3, p[0] - 2, number_len);
} else {
strnncpy(number, p + 2, p[0] - 1, number_len);
}
PDEBUG(DDSS1, DEBUG_DEBUG, " -> type = %d\n", *type);
PDEBUG(DDSS1, DEBUG_DEBUG, " -> plan = %d\n", *plan);
if (*has_present) {
PDEBUG(DDSS1, DEBUG_DEBUG, " -> present = %d\n", *present);
PDEBUG(DDSS1, DEBUG_DEBUG, " -> screen = %d\n", *screen);
}
PDEBUG(DDSS1, DEBUG_DEBUG, " -> number = %s\n", number);
return 0;
}
/* IE_CONNECTED_PN */
void enc_ie_connected_pn(struct l3_msg *l3m, uint8_t type, uint8_t plan, int has_present, uint8_t present, uint8_t screen, char *number)
{
uint8_t p[256];
int l;
PDEBUG(DDSS1, DEBUG_DEBUG, " Encode IE CONNECTED PN\n");
if (type > 7) {
PDEBUG(DDSS1, DEBUG_ERROR, "type(%d) is out of range.\n", type);
return;
}
if (plan > 15) {
PDEBUG(DDSS1, DEBUG_ERROR, "plan(%d) is out of range.\n", plan);
return;
}
if (present > 3) {
PDEBUG(DDSS1, DEBUG_ERROR, "present(%d) is out of range.\n", present);
return;
}
if (screen > 3) {
PDEBUG(DDSS1, DEBUG_ERROR, "screen(%d) is out of range.\n", screen);
return;
}
PDEBUG(DDSS1, DEBUG_DEBUG, " -> type = %d\n", type);
PDEBUG(DDSS1, DEBUG_DEBUG, " -> plan = %d\n", plan);
if (has_present) {
PDEBUG(DDSS1, DEBUG_DEBUG, " -> present = %d\n", present);
PDEBUG(DDSS1, DEBUG_DEBUG, " -> screen = %d\n", screen);
}
PDEBUG(DDSS1, DEBUG_DEBUG, " -> number = %s\n", number);
l = 1;
l += strlen(number);
if (has_present)
l += 1;
p[0] = IE_CONNECT_PN;
p[1] = l;
if (has_present) {
p[2] = 0x00 + (type << 4) + plan;
p[3] = 0x80 + (present << 5) + screen;
memcpy(p + 4, number, strlen(number));
} else {
p[2] = 0x80 + (type << 4) + plan;
memcpy(p + 3, number, strlen(number));
}
add_layer3_ie(l3m, p[0], p[1], p + 2);
}
int dec_ie_connected_pn(struct l3_msg *l3m, uint8_t *type, uint8_t *plan, int *has_present, uint8_t *present, uint8_t *screen, char *number, int number_len)
{
*has_present = 0;
uint8_t *p = l3m->connected_nr;
if (!p)
return -EINVAL;
PDEBUG(DDSS1, DEBUG_DEBUG, " Decode IE CONNECTED PN\n");
if (p[0] < 1) {
PDEBUG(DDSS1, DEBUG_DEBUG, " -> error IE too short (len=%d)\n", p[0]);
return -EINVAL;
}
*type = (p[1] & 0x70) >> 4;
*plan = p[1] & 0xf;
if (!(p[1] & 0x80)) {
if (p[0] < 2) {
PDEBUG(DDSS1, DEBUG_DEBUG, " -> error IE too short (len=%d)\n", p[0]);
return -EINVAL;
}
*has_present = 1;
*present = (p[2]&0x60) >> 5;
*screen = p[2] & 0x3;
strnncpy(number, p+3, p[0]-2, number_len);
} else {
strnncpy(number, p+2, p[0]-1, number_len);
}
PDEBUG(DDSS1, DEBUG_DEBUG, " -> type = %d\n", *type);
PDEBUG(DDSS1, DEBUG_DEBUG, " -> plan = %d\n", *plan);
if (*has_present) {
PDEBUG(DDSS1, DEBUG_DEBUG, " -> present = %d\n", *present);
PDEBUG(DDSS1, DEBUG_DEBUG, " -> screen = %d\n", *screen);
}
PDEBUG(DDSS1, DEBUG_DEBUG, " -> number = %s\n", number);
return 0;
}
/* IE_CAUSE */
void enc_ie_cause(struct l3_msg *l3m, uint8_t location, uint8_t cause)
{
uint8_t p[256];
int l;
PDEBUG(DDSS1, DEBUG_DEBUG, " Encode IE CAUSE\n");
if (location > 10) {
PDEBUG(DDSS1, DEBUG_ERROR, "location(%d) is out of range.\n", location);
return;
}
if (cause > 127) {
PDEBUG(DDSS1, DEBUG_ERROR, "cause(%d) is out of range.\n", cause);
return;
}
PDEBUG(DDSS1, DEBUG_DEBUG, " -> location = %d\n", location);
PDEBUG(DDSS1, DEBUG_DEBUG, " -> value = %d\n", cause);
l = 2;
p[0] = IE_CAUSE;
p[1] = l;
p[2] = 0x80 + location;
p[3] = 0x80 + cause;
add_layer3_ie(l3m, p[0], p[1], p + 2);
}
int dec_ie_cause(struct l3_msg *l3m, uint8_t *location, uint8_t *cause)
{
uint8_t *p = l3m->cause;
if (!p)
return -EINVAL;
PDEBUG(DDSS1, DEBUG_DEBUG, " Decode IE CAUSE\n");
if (p[0] < 2) {
PDEBUG(DDSS1, DEBUG_DEBUG, " -> error IE too short (len=%d)\n", p[0]);
return -EINVAL;
}
*location = p[1] & 0x0f;
*cause = p[2] & 0x7f;
PDEBUG(DDSS1, DEBUG_DEBUG, " -> location = %d\n", *location);
PDEBUG(DDSS1, DEBUG_DEBUG, " -> value = %d\n", *cause);
return 0;
}
/* IE_CHANNEL_ID */
void enc_ie_channel_id(struct l3_msg *l3m, int pri, int exclusive, int channel)
{
uint8_t p[256];
int l;
PDEBUG(DDSS1, DEBUG_DEBUG, " Encode IE CHANNEL ID\n");
if (exclusive < 0 || exclusive > 1) {
PDEBUG(DDSS1, DEBUG_ERROR, "exclusive(%d) is out of range.\n", exclusive);
return;
}
if ((channel<=0 && channel != CHANNEL_NO && channel != CHANNEL_ANY)
|| (!pri && channel > 2)
|| (pri && channel > 127)
|| (pri && channel == 16)) {
PDEBUG(DDSS1, DEBUG_ERROR, "channel(%d) is out of range.\n", channel);
return;
}
PDEBUG(DDSS1, DEBUG_DEBUG, " -> exclusive = %d\n", exclusive);
switch(channel) {
case CHANNEL_ANY:
PDEBUG(DDSS1, DEBUG_DEBUG, " -> channel = any channel\n");
break;
case CHANNEL_NO:
PDEBUG(DDSS1, DEBUG_DEBUG, " -> channel = no channel\n");
break;
default:
PDEBUG(DDSS1, DEBUG_DEBUG, " -> channel = %d\n", channel);
}
if (!pri) {
/* BRI */
l = 1;
p[0] = IE_CHANNEL_ID;
p[1] = l;
if (channel == CHANNEL_NO)
channel = 0;
else if (channel == CHANNEL_ANY)
channel = 3;
p[2] = 0x80 + (exclusive << 3) + channel;
add_layer3_ie(l3m, p[0], p[1], p + 2);
} else {
/* PRI */
if (channel == CHANNEL_NO || channel == CHANNEL_ANY) {
if (channel == CHANNEL_NO)
channel = 0;
else
channel = 3;
l = 1;
p[0] = IE_CHANNEL_ID;
p[1] = l;
p[2] = 0x80 + 0x20 + channel;
add_layer3_ie(l3m, p[0], p[1], p + 2);
return; /* end */
}
l = 3;
p[0] = IE_CHANNEL_ID;
p[1] = l;
p[2] = 0x80 + 0x20 + (exclusive << 3) + 0x01;
p[3] = 0x80 + 3; /* CCITT, Number, B-type */
p[4] = 0x80 + channel;
add_layer3_ie(l3m, p[0], p[1], p + 2);
}
}
int dec_ie_channel_id(struct l3_msg *l3m, int pri, int *exclusive, int *channel)
{
*exclusive = -1;
*channel = -1;
uint8_t *p = l3m->channel_id;
if (!p)
return -EINVAL;
PDEBUG(DDSS1, DEBUG_DEBUG, " Decode IE CHANNEL ID\n");
if (p[0] < 1) {
PDEBUG(DDSS1, DEBUG_DEBUG, " -> error IE too short (len=%d)\n", p[0]);
return -EINVAL;
}
if (p[1] & 0x40) {
PDEBUG(DDSS1, DEBUG_DEBUG, " -> error refering to channels of other interfaces is not supported\n");
return -EINVAL;
}
if (p[1] & 0x04) {
PDEBUG(DDSS1, DEBUG_DEBUG, " -> error using d-channel is not supported\n");
return -EINVAL;
}
*exclusive = (p[1] & 0x08) >> 3;
if (!pri) {
/* BRI */
if (p[1] & 0x20) {
PDEBUG(DDSS1, DEBUG_DEBUG, " -> error extended channel ID with non PRI interface\n");
return -EINVAL;
}
*channel = p[1] & 0x03;
if (*channel == 3)
*channel = CHANNEL_ANY;
else if (*channel == 0)
*channel = CHANNEL_NO;
} else {
/* PRI */
if (p[0] < 1) {
PDEBUG(DDSS1, DEBUG_DEBUG, " -> error IE too short for PRI (len=%d)\n", p[0]);
return -EINVAL;
}
if (!(p[1] & 0x20)) {
PDEBUG(DDSS1, DEBUG_DEBUG, " -> error basic channel ID with PRI interface\n");
return -EINVAL;
}
if ((p[1]&0x03) == 0x00) {
/* no channel */
*channel = CHANNEL_NO;
return -EINVAL;
}
if ((p[1]&0x03) == 0x03) {
/* any channel */
*channel = CHANNEL_ANY;
return -EINVAL;
}
if (p[0] < 3) {
PDEBUG(DDSS1, DEBUG_DEBUG, " -> error IE too short for PRI with channel (len=%d)\n", p[0]);
return -EINVAL;
}
if (p[2] & 0x10) {
PDEBUG(DDSS1, DEBUG_DEBUG, " -> error channel map not supported\n");
return -EINVAL;
}
*channel = p[3] & 0x7f;
if ((*channel<1) || (*channel==16)) {
PDEBUG(DDSS1, DEBUG_DEBUG, " -> error PRI interface channel out of range (%d)\n", *channel);
return -EINVAL;
}
}
PDEBUG(DDSS1, DEBUG_DEBUG, " -> exclusive = %d\n", *exclusive);
switch(*channel) {
case CHANNEL_ANY:
PDEBUG(DDSS1, DEBUG_DEBUG, " -> channel = any channel\n");
break;
case CHANNEL_NO:
PDEBUG(DDSS1, DEBUG_DEBUG, " -> channel = no channel\n");
break;
default:
PDEBUG(DDSS1, DEBUG_DEBUG, " -> channel = %d\n", *channel);
}
return 0;
}
/* IE_DATE */
void enc_ie_date(struct l3_msg *l3m, time_t ti, int no_seconds)
{
uint8_t p[256];
int l;
PDEBUG(DDSS1, DEBUG_DEBUG, " Encode IE DATE\n");
struct tm *tm;
tm = localtime(&ti);
if (!tm) {
PDEBUG(DDSS1, DEBUG_ERROR, "localtime() returned NULL.\n");
return;
}
PDEBUG(DDSS1, DEBUG_DEBUG, " -> day = %d.%d.%d\n", tm->tm_mday, tm->tm_mon+1, tm->tm_year%100);
PDEBUG(DDSS1, DEBUG_DEBUG, " -> time = %d:%d:%d\n", tm->tm_hour, tm->tm_min, tm->tm_sec);
l = 5 + (!no_seconds);
p[0] = IE_DATE;
p[1] = l;
p[2] = tm->tm_year % 100;
p[3] = tm->tm_mon + 1;
p[4] = tm->tm_mday;
p[5] = tm->tm_hour;
p[6] = tm->tm_min;
if (!no_seconds)
p[7] = tm->tm_sec;
add_layer3_ie(l3m, p[0], p[1], p + 2);
}
/* IE_DISPLAY */
void enc_ie_display(struct l3_msg *l3m, char *display)
{
uint8_t p[256];
int l;
PDEBUG(DDSS1, DEBUG_DEBUG, " Encode IE DISPLAY\n");
if (!display[0]) {
PDEBUG(DDSS1, DEBUG_ERROR, "display text not given.\n");
return;
}
if (strlen(display) > 80) {
PDEBUG(DDSS1, DEBUG_ERROR, "display text too long (max 80 chars), cutting.\n");
display[80] = '\0';
}
PDEBUG(DDSS1, DEBUG_DEBUG, " -> text = %s\n", display);
l = strlen(display);
p[0] = IE_DISPLAY;
p[1] = l;
memcpy(p + 2, display, strlen(display));
add_layer3_ie(l3m, p[0], p[1], p + 2);
}
int dec_ie_display(struct l3_msg *l3m, char *display, int display_len)
{
uint8_t *p = l3m->display;
if (!p)
return -EINVAL;
PDEBUG(DDSS1, DEBUG_DEBUG, " Decode IE DISPLAY\n");
if (p[0] < 1) {
PDEBUG(DDSS1, DEBUG_DEBUG, " -> error IE too short (len=%d)\n", p[0]);
return -EINVAL;
}
strnncpy(display, p+1, p[0], display_len);
PDEBUG(DDSS1, DEBUG_DEBUG, " -> text = %s\n", display);
return 0;
}
/* IE_KEYPAD */
void enc_ie_keypad(struct l3_msg *l3m, char *keypad)
{
PDEBUG(DDSS1, DEBUG_DEBUG, " Encode IE KEYPAD\n");
uint8_t p[256];
int l;
if (!keypad[0]) {
PDEBUG(DDSS1, DEBUG_ERROR, "keypad info not given.\n");
return;
}
PDEBUG(DDSS1, DEBUG_DEBUG, " -> keypad = %s\n", keypad);
l = strlen((char *)keypad);
p[0] = IE_KEYPAD;
p[1] = l;
memcpy(p + 2, keypad, strlen(keypad));
add_layer3_ie(l3m, p[0], p[1], p + 2);
}
int dec_ie_keypad(struct l3_msg *l3m, char *keypad, int keypad_len)
{
uint8_t *p = l3m->keypad;
if (!p)
return -EINVAL;
PDEBUG(DDSS1, DEBUG_DEBUG, " Decode IE KEYPAD\n");
if (p[0] < 1) {
PDEBUG(DDSS1, DEBUG_DEBUG, " -> error IE too short (len=%d)\n", p[0]);
return -EINVAL;
}
strnncpy(keypad, p + 1, p[0], keypad_len);
PDEBUG(DDSS1, DEBUG_DEBUG, " -> keypad = %s\n", keypad);
return 0;
}
/* IE_NOTIFY */
void enc_ie_notify(struct l3_msg *l3m, uint8_t notify)
{
uint8_t p[256];
int l;
PDEBUG(DDSS1, DEBUG_DEBUG, " Encode IE NOTIFY\n");
if (notify > 0x7f) {
PDEBUG(DDSS1, DEBUG_ERROR, "notify(%d) is out of range.\n", notify);
return;
}
PDEBUG(DDSS1, DEBUG_DEBUG, " -> notify = %d\n", notify);
l = 1;
p[0] = IE_NOTIFY;
p[1] = l;
p[2] = 0x80 + notify;
add_layer3_ie(l3m, p[0], p[1], p+2);
}
int dec_ie_notify(struct l3_msg *l3m, uint8_t *notify)
{
*notify = -1;
uint8_t *p = l3m->notify;
if (!p)
return -EINVAL;
PDEBUG(DDSS1, DEBUG_DEBUG, " Decode IE NOTIFY\n");
if (p[0] < 1) {
PDEBUG(DDSS1, DEBUG_DEBUG, " -> error IE too short (len=%d)\n", p[0]);
return -EINVAL;
}
*notify = p[1] & 0x7f;
PDEBUG(DDSS1, DEBUG_DEBUG, " -> notify = %d\n", *notify);
return 0;
}
/* IE_PROGRESS */
void enc_ie_progress(struct l3_msg *l3m, uint8_t coding, uint8_t location, uint8_t progress)
{
uint8_t p[256];
int l;
PDEBUG(DDSS1, DEBUG_DEBUG, " Encode IE PROGRESS\n");
if (coding > 0x03) {
PDEBUG(DDSS1, DEBUG_ERROR, "coding(%d) is out of range.\n", coding);
return;
}
if (location > 0x0f) {
PDEBUG(DDSS1, DEBUG_ERROR, "location(%d) is out of range.\n", location);
return;
}
if (progress > 0x7f) {
PDEBUG(DDSS1, DEBUG_ERROR, "progress(%d) is out of range.\n", progress);
return;
}
PDEBUG(DDSS1, DEBUG_DEBUG, " -> coding = %d\n", coding);
PDEBUG(DDSS1, DEBUG_DEBUG, " -> location = %d\n", location);
PDEBUG(DDSS1, DEBUG_DEBUG, " -> indicator = %d\n", progress);
l = 2;
p[0] = IE_PROGRESS;
p[1] = l;
p[2] = 0x80 + (coding<<5) + location;
p[3] = 0x80 + progress;
add_layer3_ie(l3m, p[0], p[1], p + 2);
}
int dec_ie_progress(struct l3_msg *l3m, uint8_t *coding, uint8_t *location, uint8_t *progress)
{
uint8_t *p = l3m->progress;
if (!p)
return -EINVAL;
PDEBUG(DDSS1, DEBUG_DEBUG, " Decode IE PROGRESS\n");
if (p[0] < 1) {
PDEBUG(DDSS1, DEBUG_DEBUG, " -> error IE too short (len=%d)\n", p[0]);
return -EINVAL;
}
*coding = (p[1] & 0x60) >> 5;
*location = p[1] & 0x0f;
*progress = p[2] & 0x7f;
PDEBUG(DDSS1, DEBUG_DEBUG, " -> coding = %d\n", *coding);
PDEBUG(DDSS1, DEBUG_DEBUG, " -> location = %d\n", *location);
PDEBUG(DDSS1, DEBUG_DEBUG, " -> indicator = %d\n", *progress);
return 0;
}
/* IE_REDIRECTING_NR (redirecting = during MT_SETUP) */
void enc_ie_redirecting(struct l3_msg *l3m, uint8_t type, uint8_t plan, int has_present, uint8_t present, uint8_t screen, int has_reason, uint8_t reason, char *number)
{
uint8_t p[256];
int l;
PDEBUG(DDSS1, DEBUG_DEBUG, " Encode IE REDIRECTING NR\n");
if (type > 7) {
PDEBUG(DDSS1, DEBUG_ERROR, "type(%d) is out of range.\n", type);
return;
}
if (plan > 15) {
PDEBUG(DDSS1, DEBUG_ERROR, "plan(%d) is out of range.\n", plan);
return;
}
if (present > 3) {
PDEBUG(DDSS1, DEBUG_ERROR, "present(%d) is out of range.\n", present);
return;
}
if (screen > 3) {
PDEBUG(DDSS1, DEBUG_ERROR, "screen(%d) is out of range.\n", screen);
return;
}
if (reason > 0x0f) {
PDEBUG(DDSS1, DEBUG_ERROR, "reason(%d) is out of range.\n", reason);
return;
}
PDEBUG(DDSS1, DEBUG_DEBUG, " -> type = %d\n", type);
PDEBUG(DDSS1, DEBUG_DEBUG, " -> plan = %d\n", plan);
if (has_present) {
PDEBUG(DDSS1, DEBUG_DEBUG, " -> present = %d\n", present);
PDEBUG(DDSS1, DEBUG_DEBUG, " -> screen = %d\n", screen);
if (has_reason)
PDEBUG(DDSS1, DEBUG_DEBUG, " -> reason = %d\n", reason);
}
PDEBUG(DDSS1, DEBUG_DEBUG, " -> number = %s\n", number);
l = 1;
l += strlen(number);
if (has_present) {
l += 1;
if (has_reason)
l += 1;
}
p[0] = IE_REDIRECTING_NR;
p[1] = l;
if (has_present) {
if (has_reason) {
p[2] = 0x00 + (type << 4) + plan;
p[3] = 0x00 + (present << 5) + screen;
p[4] = 0x80 + reason;
memcpy(p + 5, number, strlen(number));
} else {
p[2] = 0x00 + (type << 4) + plan;
p[3] = 0x80 + (present << 5) + screen;
memcpy(p + 4, number, strlen(number));
}
} else {
p[2] = 0x80 + (type << 4) + plan;
memcpy(p + 3, number, strlen(number));
}
add_layer3_ie(l3m, p[0], p[1], p + 2);
}
int dec_ie_redirecting(struct l3_msg *l3m, uint8_t *type, uint8_t *plan, int *has_present, uint8_t *present, uint8_t *screen, int *has_reason, uint8_t *reason, char *number, int number_len)
{
*has_present = 0;
*has_reason = 0;
uint8_t *p = l3m->redirecting_nr;
if (!p)
return -EINVAL;
PDEBUG(DDSS1, DEBUG_DEBUG, " Decode IE REDIRECTING NR\n");
if (p[0] < 1) {
PDEBUG(DDSS1, DEBUG_DEBUG, " -> error IE too short (len=%d)\n", p[0]);
return -EINVAL;
}
*type = (p[1] & 0x70) >> 4;
*plan = p[1] & 0xf;
if (!(p[1] & 0x80)) {
*has_present = 1;
*present = (p[2] & 0x60) >> 5;
*screen = p[2] & 0x3;
if (!(p[2] & 0x80)) {
*has_reason = 1;
*reason = p[3] & 0x0f;
strnncpy(number, p + 4, p[0] - 3, number_len);
} else {
strnncpy(number, p + 3, p[0] - 2, number_len);
}
} else {
strnncpy(number, p+2, p[0]-1, number_len);
}
PDEBUG(DDSS1, DEBUG_DEBUG, " -> type = %d\n", *type);
PDEBUG(DDSS1, DEBUG_DEBUG, " -> plan = %d\n", *plan);
if (*has_present) {
PDEBUG(DDSS1, DEBUG_DEBUG, " -> present = %d\n", *present);
PDEBUG(DDSS1, DEBUG_DEBUG, " -> screen = %d\n", *screen);
if (*has_reason)
PDEBUG(DDSS1, DEBUG_DEBUG, " -> reason = %d\n", *reason);
}
PDEBUG(DDSS1, DEBUG_DEBUG, " -> number = %s\n", number);
return 0;
}
/* IE_REDIRECTION (redirection = during MT_NOTIFY) */
void enc_ie_redirection(struct l3_msg *l3m, uint8_t type, uint8_t plan, int has_present, uint8_t present, char *number)
{
uint8_t p[256];
int l;
PDEBUG(DDSS1, DEBUG_DEBUG, " Encode IE REDIRECTION NR\n");
if (type > 7) {
PDEBUG(DDSS1, DEBUG_ERROR, "type(%d) is out of range.\n", type);
return;
}
if (plan > 15) {
PDEBUG(DDSS1, DEBUG_ERROR, "plan(%d) is out of range.\n", plan);
return;
}
if (present > 3) {
PDEBUG(DDSS1, DEBUG_ERROR, "present(%d) is out of range.\n", present);
return;
}
PDEBUG(DDSS1, DEBUG_DEBUG, " -> type = %d\n", type);
PDEBUG(DDSS1, DEBUG_DEBUG, " -> plan = %d\n", plan);
if (has_present)
PDEBUG(DDSS1, DEBUG_DEBUG, " -> present = %d\n", present);
PDEBUG(DDSS1, DEBUG_DEBUG, " -> number = %s\n", number);
l = 1;
l += strlen(number);
if (has_present)
l += 1;
p[0] = IE_REDIRECTION_NR;
p[1] = l;
if (has_present) {
p[2] = 0x00 + (type << 4) + plan;
p[3] = 0x80 + (present << 5);
memcpy(p + 4, number, strlen(number));
} else {
p[2] = 0x80 + (type << 4) + plan;
memcpy(p + 3, number, strlen(number));
}
add_layer3_ie(l3m, p[0], p[1], p + 2);
}
int dec_ie_redirection(struct l3_msg *l3m, uint8_t *type, uint8_t *plan, int *has_present, uint8_t *present, char *number, int number_len)
{
*has_present = 0;
uint8_t *p = l3m->redirection_nr;
if (!p)
return -EINVAL;
PDEBUG(DDSS1, DEBUG_DEBUG, " Decode IE REDIRECTION NR\n");
if (p[0] < 1) {
PDEBUG(DDSS1, DEBUG_DEBUG, " -> error IE too short (len=%d)\n", p[0]);
return -EINVAL;
}
*type = (p[1] & 0x70) >> 4;
*plan = p[1] & 0xf;
if (!(p[1] & 0x80)) {
*has_present = 1;
*present = (p[2]&0x60) >> 5;
strnncpy(number, p + 3, p[0] - 2, number_len);
} else {
strnncpy(number, p + 2, p[0] - 1, number_len);
}
PDEBUG(DDSS1, DEBUG_DEBUG, " -> type = %d\n", *type);
PDEBUG(DDSS1, DEBUG_DEBUG, " -> plan = %d\n", *plan);
if (*has_present)
PDEBUG(DDSS1, DEBUG_DEBUG, " -> present = %d\n", *present);
PDEBUG(DDSS1, DEBUG_DEBUG, " -> number = %s\n", number);
return 0;
}
/* IE_FACILITY */
void enc_ie_facility(struct l3_msg *l3m, uint8_t *facility, int facility_len)
{
uint8_t p[256];
int l;
PDEBUG(DDSS1, DEBUG_DEBUG, " Encode IE FACILITY\n");
if (!facility || facility_len <= 0)
return;
PDEBUG(DDSS1, DEBUG_DEBUG, " -> value = %s\n", debug_hex(facility, facility_len));
l = facility_len;
p[0] = IE_FACILITY;
p[1] = l;
memcpy(p + 2, facility, facility_len);
add_layer3_ie(l3m, p[0], p[1], p+2);
}
int dec_ie_facility(struct l3_msg *l3m, uint8_t *facility, int *facility_len)
{
*facility_len = 0;
uint8_t *p = l3m->facility;
if (!p)
return -EINVAL;
PDEBUG(DDSS1, DEBUG_DEBUG, " Decode IE FACILITY\n");
*facility_len = p[0];
memcpy(facility, p + 1, *facility_len);
PDEBUG(DDSS1, DEBUG_DEBUG, " -> value = %s\n", debug_hex(facility, *facility_len));
return 0;
}
/* IE_USERUSER */
void enc_ie_useruser(struct l3_msg *l3m, uint8_t protocol, uint8_t *user, int user_len)
{
uint8_t p[256];
int l;
PDEBUG(DDSS1, DEBUG_DEBUG, " Encode IE USER-USER\n");
if (protocol > 127) {
PDEBUG(DDSS1, DEBUG_ERROR, "protocol(%d) is out of range.\n", protocol);
return;
}
if (!user || user_len <= 0) {
return;
}
PDEBUG(DDSS1, DEBUG_DEBUG, " -> protocol = %d\n", protocol);
PDEBUG(DDSS1, DEBUG_DEBUG, " -> value = %s\n", debug_hex(user, user_len));
l = user_len;
p[0] = IE_USER_USER;
p[1] = l;
p[2] = 0x80 + protocol;
memcpy(p + 3, user, user_len);
add_layer3_ie(l3m, p[0], p[1], p + 2);
}
int dec_ie_useruser(struct l3_msg *l3m, uint8_t *protocol, uint8_t *user, int *user_len)
{
*user_len = 0;
uint8_t *p = l3m->useruser;
if (!p)
return -EINVAL;
PDEBUG(DDSS1, DEBUG_DEBUG, " Decode IE USER-USER\n");
*user_len = p[0] - 1;
if (p[0] < 1)
return -EINVAL;
*protocol = p[1];
memcpy(user, p + 2, (*user_len <= 128) ? *user_len : 128); /* clip to 128 maximum */
PDEBUG(DDSS1, DEBUG_DEBUG, " -> protocol = %d\n", *protocol);
PDEBUG(DDSS1, DEBUG_DEBUG, " -> value = %s\n", debug_hex(user, *user_len));
return 0;
}
/* IE_SIGNAL */
void enc_ie_signal(struct l3_msg *l3m, uint8_t signal)
{
uint8_t p[256];
PDEBUG(DDSS1, DEBUG_DEBUG, " Encode IE SIGNAL\n");
PDEBUG(DDSS1, DEBUG_DEBUG, " -> signal = %d\n", signal);
p[0] = IE_SIGNAL;
p[1] = 1;
p[2] = signal;
add_layer3_ie(l3m, p[0], p[1], p + 2);
}