1311 lines
31 KiB
C
1311 lines
31 KiB
C
|
|
/*****************************************************************************\
|
|
** **
|
|
** 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);
|
|
}
|
|
|