diff --git a/pri.c b/pri.c index b56b140..fb8354f 100644 --- a/pri.c +++ b/pri.c @@ -543,10 +543,13 @@ int pri_channel_bridge(q931_call *call1, q931_call *call2) if (call1->pri != call2->pri) return -1; - if (eect_initiate_transfer(call1->pri, call1, call2)) - return -1; + if (call1->pri->switchtype == PRI_SWITCH_LUCENT5E) + return eect_initiate_transfer(call1->pri, call1, call2); - return 0; + if (call1->pri->switchtype == PRI_SWITCH_DMS100) + return rlt_initiate_transfer(call1->pri, call1, call2); + + return -1; } int pri_hangup(struct pri *pri, q931_call *call, int cause) diff --git a/pri_facility.c b/pri_facility.c index f2f45e1..b4a0e7d 100644 --- a/pri_facility.c +++ b/pri_facility.c @@ -838,6 +838,84 @@ static int rose_diverting_leg_information2_encode(struct pri *pri, q931_call *ca return 0; } +/* Send the rltThirdParty: Invoke */ +extern int rlt_initiate_transfer(struct pri *pri, q931_call *c1, q931_call *c2) +{ + int i = 0; + unsigned char buffer[256]; + struct rose_component *comp = NULL, *compstk[10]; + const unsigned char rlt_3rd_pty = RLT_THIRD_PARTY; + q931_call *callwithid = NULL, *apdubearer = NULL; + int compsp = 0; + + if (c2->transferable) { + apdubearer = c1; + callwithid = c2; + } else if (c1->transferable) { + apdubearer = c2; + callwithid = c1; + } else + return -1; + + buffer[i++] = (0x80 | Q932_PROTOCOL_ROSE); + buffer[i++] = (0x80 | RLT_SERVICE_ID); /* Service Identifier octet */ + + ASN1_ADD_SIMPLE(comp, COMP_TYPE_INVOKE, buffer, i); + ASN1_PUSH(compstk, compsp, comp); + + /* Invoke ID is set to the operation ID */ + ASN1_ADD_BYTECOMP(comp, ASN1_INTEGER, buffer, i, rlt_3rd_pty); + + /* Operation Tag */ + ASN1_ADD_BYTECOMP(comp, ASN1_INTEGER, buffer, i, rlt_3rd_pty); + + /* Additional RLT invoke info - Octet 12 */ + ASN1_ADD_SIMPLE(comp, (ASN1_CONSTRUCTOR | ASN1_SEQUENCE), buffer, i); + ASN1_PUSH(compstk, compsp, comp); + + ASN1_ADD_WORDCOMP(comp, (ASN1_CONTEXT_SPECIFIC | ASN1_TAG_0), buffer, i, callwithid->rlt_call_id & 0xFFFFFF); /* Length is 3 octets */ + /* Reason for redirect - unused, set to 129 */ + ASN1_ADD_BYTECOMP(comp, (ASN1_CONTEXT_SPECIFIC | ASN1_TAG_1), buffer, i, 0); + ASN1_FIXUP(compstk, compsp, buffer, i); + ASN1_FIXUP(compstk, compsp, buffer, i); + + if (pri_call_apdu_queue(apdubearer, Q931_FACILITY, buffer, i, NULL, NULL)) + return -1; + + if (q931_facility(apdubearer->pri, apdubearer)) { + pri_message(pri, "Could not schedule facility message for call %d\n", apdubearer->cr); + return -1; + } + return 0; +} + +static int add_dms100_transfer_ability_apdu(struct pri *pri, q931_call *c) +{ + int i = 0; + unsigned char buffer[256]; + struct rose_component *comp = NULL, *compstk[10]; + const unsigned char rlt_op_ind = RLT_OPERATION_IND; + int compsp = 0; + + buffer[i++] = (0x80 | Q932_PROTOCOL_ROSE); + buffer[i++] = (0x80 | RLT_SERVICE_ID); /* Service Identifier octet */ + + ASN1_ADD_SIMPLE(comp, COMP_TYPE_INVOKE, buffer, i); + ASN1_PUSH(compstk, compsp, comp); + + /* Invoke ID is set to the operation ID */ + ASN1_ADD_BYTECOMP(comp, ASN1_INTEGER, buffer, i, rlt_op_ind); + + /* Operation Tag - basically the same as the invoke ID tag */ + ASN1_ADD_BYTECOMP(comp, ASN1_INTEGER, buffer, i, rlt_op_ind); + ASN1_FIXUP(compstk, compsp, buffer, i); + + if (pri_call_apdu_queue(c, Q931_SETUP, buffer, i, NULL, NULL)) + return -1; + else + return 0; +} + /* Sending callername information functions */ static int add_callername_facility_ies(struct pri *pri, q931_call *c, int cpe) { @@ -1270,6 +1348,200 @@ static int aoc_aoce_charging_unit_encode(struct pri *pri, q931_call *c, long cha } /* End AOC */ +extern int rose_reject_decode(struct pri *pri, q931_call *call, unsigned char *data, int len) +{ + int i = 0; + int problemtag = -1; + int problem = -1; + int invokeidvalue = -1; + unsigned char *vdata = data; + struct rose_component *comp = NULL; + char *problemtagstr, *problemstr; + + do { + /* Invoke ID stuff */ + GET_COMPONENT(comp, i, vdata, len); + CHECK_COMPONENT(comp, INVOKE_IDENTIFIER, "Don't know what to do if first ROSE component is of type 0x%x\n"); + ASN1_GET_INTEGER(comp, invokeidvalue); + NEXT_COMPONENT(comp, i); + + GET_COMPONENT(comp, i, vdata, len); + problemtag = comp->type; + problem = comp->data[0]; + + if (pri->switchtype == PRI_SWITCH_DMS100) { + switch (problemtag) { + case 0x80: + problemtagstr = "General problem"; + break; + case 0x81: + problemtagstr = "Invoke problem"; + break; + case 0x82: + problemtagstr = "Return result problem"; + break; + case 0x83: + problemtagstr = "Return error problem"; + break; + default: + problemtagstr = "Unknown"; + } + + switch (problem) { + case 0x00: + problemstr = "Unrecognized component"; + break; + case 0x01: + problemstr = "Mistyped component"; + break; + case 0x02: + problemstr = "Badly structured component"; + break; + default: + problemstr = "Unknown"; + } + + pri_error(pri, "ROSE REJECT:\n"); + pri_error(pri, "\tINVOKE ID: 0x%X\n", invokeidvalue); + pri_error(pri, "\tPROBLEM TYPE: %s (0x%x)\n", problemtagstr, problemtag); + pri_error(pri, "\tPROBLEM: %s (0x%x)\n", problemstr, problem); + + return 0; + } else { + pri_message(pri, "Unable to handle return result on switchtype %d!\n", pri->switchtype); + return -1; + } + + } while(0); + + return -1; +} +extern int rose_return_error_decode(struct pri *pri, q931_call *call, unsigned char *data, int len) +{ + int i = 0; + int errorvalue = -1; + int invokeidvalue = -1; + unsigned char *vdata = data; + struct rose_component *comp = NULL; + char *invokeidstr, *errorstr; + + do { + /* Invoke ID stuff */ + GET_COMPONENT(comp, i, vdata, len); + CHECK_COMPONENT(comp, INVOKE_IDENTIFIER, "Don't know what to do if first ROSE component is of type 0x%x\n"); + ASN1_GET_INTEGER(comp, invokeidvalue); + NEXT_COMPONENT(comp, i); + + GET_COMPONENT(comp, i, vdata, len); + CHECK_COMPONENT(comp, ASN1_INTEGER, "Don't know what to do if second component in return error is 0x%x\n"); + ASN1_GET_INTEGER(comp, errorvalue); + + if (pri->switchtype == PRI_SWITCH_DMS100) { + switch (invokeidvalue) { + case RLT_OPERATION_IND: + invokeidstr = "RLT_OPERATION_IND"; + break; + case RLT_THIRD_PARTY: + invokeidstr = "RLT_THIRD_PARTY"; + break; + default: + invokeidstr = "Unknown"; + } + + switch (errorvalue) { + case 0x10: + errorstr = "RLT Bridge Fail"; + break; + case 0x11: + errorstr = "RLT Call ID Not Found"; + break; + case 0x12: + errorstr = "RLT Not Allowed"; + break; + case 0x13: + errorstr = "RLT Switch Equip Congs"; + break; + default: + errorstr = "Unknown"; + } + + pri_error(pri, "ROSE RETURN ERROR:\n"); + pri_error(pri, "\tOPERATION: %s\n", invokeidstr); + pri_error(pri, "\tERROR: %s\n", errorstr); + + return 0; + } else { + pri_message(pri, "Unable to handle return result on switchtype %d!\n", pri->switchtype); + return -1; + } + + } while(0); + + return -1; +} + +extern int rose_return_result_decode(struct pri *pri, q931_call *call, unsigned char *data, int len) +{ + int i = 0; + int operationidvalue = -1; + int invokeidvalue = -1; + unsigned char *vdata = data; + struct rose_component *comp = NULL; + + do { + /* Invoke ID stuff */ + GET_COMPONENT(comp, i, vdata, len); + CHECK_COMPONENT(comp, INVOKE_IDENTIFIER, "Don't know what to do if first ROSE component is of type 0x%x\n"); + ASN1_GET_INTEGER(comp, invokeidvalue); + NEXT_COMPONENT(comp, i); + + if (pri->switchtype == PRI_SWITCH_DMS100) { + switch (invokeidvalue) { + case RLT_THIRD_PARTY: + if (pri->debug & PRI_DEBUG_APDU) pri_message(pri, "Successfully completed RLT transfer!\n"); + return 0; + case RLT_OPERATION_IND: + if (pri->debug & PRI_DEBUG_APDU) pri_message(pri, "Received RLT_OPERATION_IND\n"); + /* Have to take out the rlt_call_id */ + GET_COMPONENT(comp, i, vdata, len); + CHECK_COMPONENT(comp, ASN1_SEQUENCE, "Protocol error detected in parsing RLT_OPERATION_IND return result!\n"); + + /* Traverse the contents of this sequence */ + /* First is the Operation Value */ + SUB_COMPONENT(comp, i); + GET_COMPONENT(comp, i, vdata, len); + CHECK_COMPONENT(comp, ASN1_INTEGER, "RLT_OPERATION_IND should be of type ASN1_INTEGER!\n"); + ASN1_GET_INTEGER(comp, operationidvalue); + + if (operationidvalue != RLT_OPERATION_IND) { + pri_message(pri, "Invalid Operation ID value (0x%x) in return result!\n", operationidvalue); + return -1; + } + + /* Next is the Call ID */ + NEXT_COMPONENT(comp, i); + GET_COMPONENT(comp, i, vdata, len); + CHECK_COMPONENT(comp, (ASN1_CONTEXT_SPECIFIC|ASN1_TAG_0), "Error check failed on Call ID!\n"); + ASN1_GET_INTEGER(comp, call->rlt_call_id); + /* We have enough data to transfer the call */ + call->transferable = 1; + + return 0; + + default: + pri_message(pri, "Could not parse invoke of type 0x%x!\n", invokeidvalue); + return -1; + } + } else { + pri_message(pri, "Unable to handle return result on switchtype %d!\n", pri->switchtype); + return -1; + } + + } while(0); + + return -1; +} + extern int rose_invoke_decode(struct pri *pri, q931_call *call, unsigned char *data, int len) { int i = 0; @@ -1474,6 +1746,12 @@ extern int pri_call_add_standard_apdus(struct pri *pri, q931_call *call) add_callername_facility_ies(pri, call, (pri->localtype == PRI_CPE)); #endif + if ((pri->switchtype == PRI_SWITCH_DMS100) && (pri->localtype == PRI_CPE)) { + add_dms100_transfer_ability_apdu(pri, call); + } + + + return 0; } diff --git a/pri_facility.h b/pri_facility.h index 944c2bf..c75e3ff 100644 --- a/pri_facility.h +++ b/pri_facility.h @@ -104,7 +104,7 @@ #define ASN1_CLAN_MASK 0xc0 #define ASN1_UNIVERSAL 0x00 #define ASN1_APPLICATION 0x40 -#define ASN1_CONTEXT_SPECIFIC 0x80 +#define ASN1_CONTEXT_SPECIFIC 0x80 #define ASN1_PRIVATE 0xc0 /* ASN.1 Length masks */ @@ -135,6 +135,11 @@ #define Q932_TON_SUBSCRIBER 0x04 #define Q932_TON_ABBREVIATED 0x06 +/* RLT related Operations */ +#define RLT_SERVICE_ID 0x3e +#define RLT_OPERATION_IND 0x01 +#define RLT_THIRD_PARTY 0x02 + struct rose_component { u_int8_t type; u_int8_t len; @@ -230,9 +235,18 @@ struct rose_component { (stack)[(stackpointer)]->len = (unsigned char *)&((data)[(idx)]) - (unsigned char *)(stack)[(stackpointer)] - 2; \ } while (0) -/* Decoder for the invoke part of a ROSE request */ +/* Decoder for the invoke ROSE component */ extern int rose_invoke_decode(struct pri *pri, struct q931_call *call, unsigned char *data, int len); +/* Decoder for the return result ROSE component */ +extern int rose_return_result_decode(struct pri *pri, struct q931_call *call, unsigned char *data, int len); + +/* Decoder for the return error ROSE component */ +extern int rose_return_error_decode(struct pri *pri, struct q931_call *call, unsigned char *data, int len); + +/* Decoder for the reject ROSE component */ +extern int rose_reject_decode(struct pri *pri, struct q931_call *call, unsigned char *data, int len); + extern int asn1_copy_string(char * buf, int buflen, struct rose_component *comp); extern int asn1_string_encode(unsigned char asn1_type, void *data, int len, int max_len, void *src, int src_len); @@ -250,6 +264,8 @@ extern int mwi_message_send(struct pri *pri, q931_call *call, struct pri_sr *req /* starts a 2BCT */ extern int eect_initiate_transfer(struct pri *pri, q931_call *c1, q931_call *c2); +extern int rlt_initiate_transfer(struct pri *pri, q931_call *c1, q931_call *c2); + /* Use this function to queue a facility-IE born APDU onto a call * call is the call to use, messagetype is any one of the Q931 messages, * apdu is the apdu data, apdu_len is the length of the apdu data */ diff --git a/pri_internal.h b/pri_internal.h index 8912a30..6f52a36 100644 --- a/pri_internal.h +++ b/pri_internal.h @@ -130,6 +130,7 @@ struct pri_sr { int redirectingreason; int justsignalling; const char *useruserinfo; + int transferable; }; /* Internal switch types */ @@ -239,6 +240,9 @@ struct q931_call { long aoc_units; /* Advice of Charge Units */ struct apdu_event *apdus; /* APDU queue for call */ + + int transferable; + unsigned int rlt_call_id; /* RLT call id */ }; extern int pri_schedule_event(struct pri *pri, int ms, void (*function)(void *data), void *data); diff --git a/q931.c b/q931.c index c0f8698..0ae1a83 100644 --- a/q931.c +++ b/q931.c @@ -1216,9 +1216,9 @@ static FUNC_RECV(receive_facility) case Q932_PROTOCOL_ROSE: switch (comp->type) { Q932_HANDLE_PROC(COMP_TYPE_INVOKE, Q932_STATE_SERVICE, "ROSE Invoke", rose_invoke_decode); - Q932_HANDLE_NULL(COMP_TYPE_RETURN_RESULT, Q932_STATE_SERVICE, "ROSE return result", NULL); - Q932_HANDLE_NULL(COMP_TYPE_RETURN_ERROR, Q932_STATE_SERVICE, "ROSE return error", NULL); - Q932_HANDLE_NULL(COMP_TYPE_REJECT, Q932_STATE_SERVICE, "ROSE reject", NULL); + Q932_HANDLE_PROC(COMP_TYPE_RETURN_RESULT, Q932_STATE_SERVICE, "ROSE return result", rose_return_result_decode); + Q932_HANDLE_PROC(COMP_TYPE_RETURN_ERROR, Q932_STATE_SERVICE, "ROSE return error", rose_return_error_decode); + Q932_HANDLE_PROC(COMP_TYPE_REJECT, Q932_STATE_SERVICE, "ROSE reject", rose_reject_decode); default: if (pri->debug & PRI_DEBUG_APDU) pri_message(pri, "Don't know how to handle ROSE component of type 0x%X\n", comp->type);