diff --git a/etc/dictionary.opensips b/etc/dictionary.opensips index 89aee3e5543..b30e028a1bb 100644 --- a/etc/dictionary.opensips +++ b/etc/dictionary.opensips @@ -23,7 +23,7 @@ ATTRIBUTE Sip-Call-Duration 227 integer ATTRIBUTE Sip-Call-Setuptime 228 integer ATTRIBUTE Sip-Call-Created 229 integer ATTRIBUTE Sip-Call-MSDuration 230 integer -ATTRIBUTE Transaction-Id 231 string +ATTRIBUTE Transaction-Id 231 string # Proprietary, aaa_diameter ### Acct-Status-Type Values ### VALUE Acct-Status-Type Start 1 # RFC2866, RADIUS acc diff --git a/lib/cJSON.c b/lib/cJSON.c index ffeeaec3f44..6428703e51f 100644 --- a/lib/cJSON.c +++ b/lib/cJSON.c @@ -82,6 +82,16 @@ typedef int cjbool; static const unsigned char *global_ep = NULL; +cJSON_Hooks sys_mem_hooks = { + .malloc_fn = malloc, + .free_fn = free, +}; + +cJSON_Hooks shm_mem_hooks = { + .malloc_fn = osips_shm_malloc, + .free_fn = osips_shm_free, +}; + int cJSON_NumberIsInt(cJSON *item) { return ((FABS((double)item->valueint - item->valuedouble) <= DBL_EPSILON) && diff --git a/lib/cJSON.h b/lib/cJSON.h index 0895dc61415..b09b9340f19 100644 --- a/lib/cJSON.h +++ b/lib/cJSON.h @@ -87,6 +87,8 @@ typedef int (flush_fn)(unsigned char *buf, int len, void *param); /* Supply malloc, realloc and free functions to cJSON */ extern void cJSON_InitHooks(cJSON_Hooks* hooks); +extern cJSON_Hooks sys_mem_hooks; +extern cJSON_Hooks shm_mem_hooks; /* Supply a block of JSON, and this returns a cJSON object you can interrogate. Call cJSON_Delete when finished. */ diff --git a/mi/mi.c b/mi/mi.c index 028afb8943f..a5d1571494d 100644 --- a/mi/mi.c +++ b/mi/mi.c @@ -46,15 +46,6 @@ static struct mi_cmd* mi_cmds = 0; static int mi_cmds_no = 0; -static cJSON_Hooks sys_mem_hooks = { - .malloc_fn = malloc, - .free_fn = free, -}; -static cJSON_Hooks shm_mem_hooks = { - .malloc_fn = osips_shm_malloc, - .free_fn = osips_shm_free, -}; - void _init_mi_sys_mem_hooks(void) { cJSON_InitHooks(&sys_mem_hooks); diff --git a/modules/aaa_diameter/aaa_diameter.c b/modules/aaa_diameter/aaa_diameter.c index 381d8e618b6..86466b8068d 100644 --- a/modules/aaa_diameter/aaa_diameter.c +++ b/modules/aaa_diameter/aaa_diameter.c @@ -36,7 +36,7 @@ char *dm_conf_filename = "freeDiameter.conf"; char *extra_avps_file; static int dm_send_request(struct sip_msg *msg, int *app_id, int *cmd_code, - str *avp_json, pv_spec_t *res_code_pv); + str *avp_json, pv_spec_t *rpl_avps_pv); static int dm_bind_api(aaa_prot *api); int fd_log_level = FD_LOG_NOTICE; @@ -201,12 +201,13 @@ static int dm_bind_api(aaa_prot *api) static int dm_send_request(struct sip_msg *msg, int *app_id, int *cmd_code, - str *avp_json, pv_spec_t *res_code_pv) + str *avp_json, pv_spec_t *rpl_avps_pv) { aaa_message *dmsg = NULL; struct dict_object *req; cJSON *avps, *_avp; - int rc, res_code; + int rc; + char *rpl_avps; if (fd_dict_search(fd_g_config->cnf_dict, DICT_COMMAND, CMD_BY_CODE_R, cmd_code, &req, ENOENT) == ENOENT) { @@ -308,17 +309,16 @@ static int dm_send_request(struct sip_msg *msg, int *app_id, int *cmd_code, } } - rc = _dm_send_message(NULL, dmsg, NULL, &res_code); + rc = _dm_send_message(NULL, dmsg, NULL, &rpl_avps); - if (res_code_pv) { - pv_value_t val = {STR_NULL, 0, PV_VAL_INT|PV_TYPE_INT}; - val.ri = res_code; - if (pv_set_value(msg, res_code_pv, 0, &val) != 0) - LM_ERR("failed to set output res_code pv to %d\n", res_code); + if (rpl_avps_pv && rpl_avps) { + pv_value_t val = {(str){rpl_avps, strlen(rpl_avps)}, 0, PV_VAL_STR}; + if (pv_set_value(msg, rpl_avps_pv, 0, &val) != 0) + LM_ERR("failed to set output rpl_avps pv to: %s\n", rpl_avps); } if (rc != 0) { - LM_ERR("Diameter request failed, Result-Code: %d\n", res_code); + LM_ERR("Diameter request failed\n"); cJSON_Delete(avps); return rc; } @@ -327,11 +327,10 @@ static int dm_send_request(struct sip_msg *msg, int *app_id, int *cmd_code, return 1; error: - if (res_code_pv) { - pv_value_t val = {STR_NULL, 0, PV_VAL_INT|PV_TYPE_INT}; - val.ri = -1; - if (pv_set_value(msg, res_code_pv, 0, &val) != 0) - LM_ERR("failed to set output res_code pv to %d\n", res_code); + if (rpl_avps_pv) { + pv_value_t val = {STR_NULL, 0, PV_VAL_NULL}; + if (pv_set_value(msg, rpl_avps_pv, 0, &val) != 0) + LM_ERR("failed to set output rpl_avps pv to NULL\n"); } _dm_destroy_message(dmsg); diff --git a/modules/aaa_diameter/aaa_impl.c b/modules/aaa_diameter/aaa_impl.c index 9b0e31fff31..ba846189189 100644 --- a/modules/aaa_diameter/aaa_impl.c +++ b/modules/aaa_diameter/aaa_impl.c @@ -204,11 +204,12 @@ static int dm_auth_reply(struct msg **_msg, struct avp * avp, struct session * s static int dm_custom_cmd_reply(struct msg **_msg, struct avp * avp, struct session * sess, void * data, enum disp_action * act) { + cJSON *avps = NULL; struct msg_hdr *hdr = NULL; struct msg *msg = *_msg; - struct avp *a = NULL; + struct avp *a = NULL, *it = NULL; struct avp_hdr * h = NULL; - int rc; + int rc, i = 0; str tid; struct dm_cond **prpl_cond, *rpl_cond; @@ -219,20 +220,78 @@ static int dm_custom_cmd_reply(struct msg **_msg, struct avp * avp, struct sessi goto out; } - FD_CHECK(fd_msg_search_avp(msg, dm_dict.Result_Code, &a)); - FD_CHECK(fd_msg_avp_hdr(a, &h)); - rc = h->avp_value->u32; + cJSON_InitHooks(&shm_mem_hooks); + avps = cJSON_CreateArray(); + if (!avps) { + LM_ERR("oom 1\n"); + goto out; + } - FD_CHECK(fd_msg_search_avp(msg, dm_dict.Transaction_Id, &a)); - FD_CHECK(fd_msg_avp_hdr(a, &h)); + FD_CHECK_GT(fd_msg_browse(msg, MSG_BRW_FIRST_CHILD, &it, NULL)); + + LM_DBG("------------ AVP iteration ----------------\n"); + while (it) { + cJSON *item, *val; + struct dict_object *obj; + struct dict_avp_data dm_avp; + + FD_CHECK_GT(fd_msg_avp_hdr(it, &h)); + + FD_CHECK_GT(fd_dict_search(fd_g_config->cnf_dict, DICT_AVP, AVP_BY_CODE, + &h->avp_code, &obj, ENOENT)); + FD_CHECK_GT(fd_dict_getval(obj, &dm_avp)); + + item = cJSON_CreateObject(); + if (!item) { + LM_ERR("oom 2\n"); + goto out; + } + + /* FIXME: there has to be an API-ish way of detecting type, but this works for now */ + if (h->avp_len >= 12 && h->avp_value->os.len == 0) { + /* Integer AVP */ + LM_DBG("%d. got integer AVP %u, value: %d\n", i, h->avp_code, h->avp_value->i32); + + val = cJSON_CreateNumber((double)h->avp_value->i64); + if (!val) { + cJSON_Delete(item); + LM_ERR("oom 3\n"); + goto out; + } + } else { + /* Octetstring AVP */ + LM_DBG("%d. got string AVP %u, value: %.*s %s\n", i, h->avp_code, (int)h->avp_value->os.len, h->avp_value->os.data, h->avp_value->os.data); + + val = cJSON_CreateString((const char *)h->avp_value->os.data); + if (!val) { + cJSON_Delete(item); + LM_ERR("oom 3\n"); + goto out; + } + } + + cJSON_AddItemToObject(item, dm_avp.avp_name, val); + cJSON_AddItemToArray(avps, item); + + FD_CHECK_GT(fd_msg_browse(it, MSG_BRW_NEXT, &it, NULL)); + i++; + } + LM_DBG("------------ END AVP iteration ----------------\n"); + + rc = fd_msg_search_avp(msg, dm_dict.Transaction_Id, &a); + if (rc != 0) { + LM_WARN("Missing Transaction-Id AVP in Diameter Answer %d/%d\n", + hdr->msg_appl, hdr->msg_code); + goto out; + } + + FD_CHECK_GT(fd_msg_avp_hdr(a, &h)); tid.s = (char *)h->avp_value->os.data; tid.len = (int)h->avp_value->os.len; LM_DBG("%d/%d reply %d, Transaction-Id: %.*s\n", hdr->msg_appl, hdr->msg_code, rc, tid.len, tid.s); - FD_CHECK(fd_msg_search_avp(msg, dm_dict.Error_Message, &a)); - prpl_cond = (struct dm_cond **)hash_find_key(pending_replies, tid); if (!prpl_cond) { LM_ERR("failed to match Transaction_Id %.*s to a pending request\n", @@ -248,15 +307,23 @@ static int dm_custom_cmd_reply(struct msg **_msg, struct avp * avp, struct sessi goto out; } - rpl_cond->rc = rc; + if (rpl_cond->rpl_avps_json) + shm_free(rpl_cond->rpl_avps_json); + rpl_cond->rpl_avps_json = cJSON_PrintUnformatted(avps); hash_remove_key(pending_replies, tid); + fd_msg_search_avp(msg, dm_dict.Error_Message, &a); if (a) { rpl_cond->is_error = 1; - FD_CHECK(fd_msg_avp_hdr(a, &h)); - LM_DBG("transaction failed, rc: %d (%.*s)\n", - rc, (int)h->avp_value->os.len, h->avp_value->os.data); + rc = fd_msg_avp_hdr(a, &h); + if (rc != 0) { + pthread_mutex_unlock(&rpl_cond->mutex); + goto out; + } + + LM_DBG("transaction failed (%.*s)\n", + (int)h->avp_value->os.len, h->avp_value->os.data); } else { rpl_cond->is_error = 0; } @@ -266,6 +333,9 @@ static int dm_custom_cmd_reply(struct msg **_msg, struct avp * avp, struct sessi pthread_mutex_unlock(&rpl_cond->mutex); out: + cJSON_Delete(avps); + cJSON_InitHooks(NULL); + FD_CHECK(fd_msg_free(msg)); *_msg = NULL; return 0; @@ -1313,7 +1383,7 @@ int dm_avp_add(aaa_conn *_, aaa_message *msg, aaa_map *avp, void *val, int _dm_send_message(aaa_conn *_, aaa_message *req, aaa_message **reply, - int *res_code) + char **rpl_avps) { struct dm_message *dm; @@ -1361,8 +1431,8 @@ int _dm_send_message(aaa_conn *_, aaa_message *req, aaa_message **reply, "reply\n", rc, strerror(rc)); pthread_mutex_unlock(&my_reply_cond->mutex); - if (res_code) - *res_code = -1; + if (rpl_avps) + *rpl_avps = NULL; return -2; } @@ -1370,9 +1440,11 @@ int _dm_send_message(aaa_conn *_, aaa_message *req, aaa_message **reply, LM_DBG("reply received, Result-Code: %d (%s)\n", my_reply_cond->rc, my_reply_cond->is_error ? "FAILURE" : "SUCCESS"); + LM_DBG("AVPs: %s\n", my_reply_cond->rpl_avps_json); + + if (rpl_avps) + *rpl_avps = my_reply_cond->rpl_avps_json; - if (res_code) - *res_code = my_reply_cond->rc; if (my_reply_cond->is_error) return -1; } diff --git a/modules/aaa_diameter/aaa_impl.h b/modules/aaa_diameter/aaa_impl.h index 6d647438246..b927703a896 100644 --- a/modules/aaa_diameter/aaa_impl.h +++ b/modules/aaa_diameter/aaa_impl.h @@ -38,6 +38,21 @@ __FD_CHECK((__call__), (__retok__), __ret__) #define FD_CHECK(__call__) _FD_CHECK((__call__), 0) +#define __FD_CHECK_GT(__call__, __retok__, __goto__) \ + do { \ + int __ret__; \ + __ret__ = (__call__); \ + if (__ret__ > 0) \ + __ret__ = -__ret__; \ + if (__ret__ != (__retok__)) { \ + LM_ERR("error in %s: %d\n", #__call__, __ret__); \ + goto __goto__; \ + } \ + } while (0) +#define _FD_CHECK_GT(__call__, __retok__) \ + __FD_CHECK_GT((__call__), (__retok__), out) +#define FD_CHECK_GT(__call__) _FD_CHECK_GT((__call__), 0) + #define FD_CHECK_dict_new(type, data, parent, ref) \ FD_CHECK(fd_dict_new(fd_g_config->cnf_dict, (type), \ (data), (parent), (ref))) @@ -98,6 +113,7 @@ struct dm_cond { int rc; /* the Diameter Result-Code AVP value */ int is_error; + char *rpl_avps_json; /* JSON with all reply AVPs and their values */ }; int init_mutex_cond(pthread_mutex_t *mutex, pthread_cond_t *cond); @@ -122,7 +138,7 @@ int dm_avp_add(aaa_conn *_, aaa_message *msg, aaa_map *avp, void *val, int val_length, int vendor); int dm_send_message(aaa_conn *_, aaa_message *req, aaa_message **__); int _dm_send_message(aaa_conn *_, aaa_message *req, aaa_message **reply, - int *res_code); + char **rpl_avps); int dm_destroy_message(aaa_conn *con, aaa_message *msg); void _dm_destroy_message(aaa_message *msg); diff --git a/modules/aaa_diameter/app_opensips/app_opensips.c b/modules/aaa_diameter/app_opensips/app_opensips.c index 994e03d35a9..b7f5be2cbb6 100644 --- a/modules/aaa_diameter/app_opensips/app_opensips.c +++ b/modules/aaa_diameter/app_opensips/app_opensips.c @@ -249,16 +249,26 @@ static int acc_request( struct msg ** msg, struct avp * avp, struct session * se fd_log_debug("Session: %.*s", (int)sl, s); /* The AVPs that we copy in the answer */ + CHECK_FCT( fd_msg_search_avp ( qry, dm_dict.Transaction_Id, &a) ); + if (a) { + CHECK_FCT( fd_msg_avp_hdr( a, &h ) ); + fd_log_debug("[ACC] Transaction-Id: %.*s", + h->avp_value->os.len, h->avp_value->os.data); + CHECK_FCT( fd_msg_avp_new ( dm_dict.Transaction_Id, 0, &a ) ); + CHECK_FCT( fd_msg_avp_setvalue( a, h->avp_value ) ); + CHECK_FCT( fd_msg_avp_add( ans, MSG_BRW_LAST_CHILD, a ) ); + } + CHECK_FCT( fd_msg_search_avp ( qry, dm_dict.Accounting_Record_Type, &a) ); if (a) { CHECK_FCT( fd_msg_avp_hdr( a, &h ) ); - fd_log_debug("Accounting-Record-Type: %d (%s)", h->avp_value->u32, + fd_log_debug("Accounting-Record-Type: %d (%s) %d %p", h->avp_value->u32, /* it would be better to search this in the dictionary, but it is only for debug, so ok */ (h->avp_value->u32 == 1) ? "EVENT_RECORD" : (h->avp_value->u32 == 2) ? "START_RECORD" : (h->avp_value->u32 == 3) ? "INTERIM_RECORD" : (h->avp_value->u32 == 4) ? "STOP_RECORD" : - "" + "", h->avp_value->os.len, h->avp_value->os.data ); CHECK_FCT( fd_msg_avp_new ( dm_dict.Accounting_Record_Type, 0, &a ) ); CHECK_FCT( fd_msg_avp_setvalue( a, h->avp_value ) ); @@ -715,6 +725,7 @@ static int os_entry(char *confstring) CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Accounting-Record-Type", &dm_dict.Accounting_Record_Type, ENOENT) ); CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Acct-Session-Id", &dm_dict.Acct_Session_Id, ENOENT) ); CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Event-Timestamp", &dm_dict.Event_Timestamp, ENOENT) ); + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Transaction-Id", &dm_dict.Transaction_Id, ENOENT) ); CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Auth-Application-Id", &dm_dict.Auth_Application_Id, ENOENT) ); CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Auth-Session-State", &dm_dict.Auth_Session_State, ENOENT) ); diff --git a/modules/aaa_diameter/doc/aaa_diameter_admin.xml b/modules/aaa_diameter/doc/aaa_diameter_admin.xml index cd95e232ba1..e1900931d33 100644 --- a/modules/aaa_diameter/doc/aaa_diameter_admin.xml +++ b/modules/aaa_diameter/doc/aaa_diameter_admin.xml @@ -152,7 +152,7 @@ modparam("aaa_diameter", "answer_timeout", 5000) Exported Functions
- <function moreinfo="none">dm_send_request(app_id, cmd_code, avps_json, [res_code_pv])</function> + <function moreinfo="none">dm_send_request(app_id, cmd_code, avps_json, [rpl_avps_pv])</function> Perform a blocking Diameter request over to the interconnected peer @@ -176,10 +176,10 @@ modparam("aaa_diameter", "answer_timeout", 5000) the AVPs to include in the message payload. - res_code_pv (var, optional) - output variable which will - retain the Result-Code AVP value from the Diameter Answer. - A -2 result code means that - no reply arrived at all within the timeout window. + rpl_avps_pv (var, optional) - output variable which will + hold all AVP names from the Diameter Answer along with their values, packed + as a JSON Array string. The "json" module and its $json + variable could be used to iterate this array. @@ -221,18 +221,28 @@ APPLICATION 42 My Diameter Application REQUEST 92001 My-Custom-Request { - Session-Id | FIXED_HEAD | 1 Origin-Host | REQUIRED | 1 Origin-Realm | REQUIRED | 1 - Route-Record | REQUIRED | 1 - rated_duration | OPTIONAL | 1 + Destination-Realm | REQUIRED | 1 + Transaction-Id | REQUIRED | 1 + Sip-From-Tag | REQUIRED | 1 + Sip-To-Tag | REQUIRED | 1 + Acct-Session-Id | REQUIRED | 1 + Sip-Call-Duration | REQUIRED | 1 + Sip-Call-Setuptime | REQUIRED | 1 + Sip-Call-Created | REQUIRED | 1 + Sip-Call-MSDuration | REQUIRED | 1 + out_gw | REQUIRED | 1 + call_cost | REQUIRED | 1 } ANSWER 92001 My-Custom-Answer { Origin-Host | REQUIRED | 1 Origin-Realm | REQUIRED | 1 - Route-Record | REQUIRED | 1 + Destination-Realm | REQUIRED | 1 + Transaction-Id | REQUIRED | 1 + Result-Code | REQUIRED | 1 } @@ -242,8 +252,8 @@ ANSWER 92001 My-Custom-Answer <function moreinfo="none">dm_send_request</function> usage -# Building an sending an Accounting-Request (271) for the -# Diameter Accounting application (3) +# Building an sending an My-Custom-Request (92001) for the +# My Diameter Application (42) $var(payload) = "[ { \"Origin-Host\": \"client.diameter.test\" }, { \"Origin-Realm\": \"diameter.test\" }, @@ -259,8 +269,9 @@ $var(payload) = "[ { \"cost\": \"10.84\" } ]"; -$var(rc) = dm_send_request(3, 271, $var(payload), $var(res_code)); -xlog("rc: $var(rc), Result-Code: $var(res_code)\n"); +$var(rc) = dm_send_request(42, 92001, $var(payload), $var(rpl_avps)); +xlog("rc: $var(rc), AVPs: $var(rpl_avps)\n"); +$json(avps) := $var(rpl_avps);