/*****************************************************************************\ ** ** ** PBX4Linux ** ** ** **---------------------------------------------------------------------------** ** Copyright: Andreas Eversberg ** ** ** ** information elements encode and decode ** ** ** \*****************************************************************************/ #include #include #include #include "../libdebug/debug.h" #ifndef u_char #define u_char unsigned char #endif #include #include #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); }