diff --git a/aaa/aaa.h b/aaa/aaa.h index 338736be08d..3c35f011a45 100644 --- a/aaa/aaa.h +++ b/aaa/aaa.h @@ -47,11 +47,15 @@ #define AAA_DICT_FIND_VAL 1 #define AAA_DICT_FIND_ATTR 2 #define AAA_DICT_FIND_VEND 3 + +/* message types */ #define AAA_AUTH 4 #define AAA_ACCT 5 #define AAA_RECV 6 -#define AAA_GET_FROM_START 7 -#define AAA_GET_FROM_CURRENT 8 +#define AAA_CUSTOM 7 + +#define AAA_GET_FROM_START 8 +#define AAA_GET_FROM_CURRENT 9 #define AAA_APP_ACCOUNTING 3 #define AAA_APP_SIP 6 diff --git a/etc/dictionary.opensips b/etc/dictionary.opensips index 75fdeba3227..89aee3e5543 100644 --- a/etc/dictionary.opensips +++ b/etc/dictionary.opensips @@ -23,6 +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 ### Acct-Status-Type Values ### VALUE Acct-Status-Type Start 1 # RFC2866, RADIUS acc diff --git a/modules/aaa_diameter/aaa_diameter.c b/modules/aaa_diameter/aaa_diameter.c index 689e6cc5021..aeca90ac62b 100644 --- a/modules/aaa_diameter/aaa_diameter.c +++ b/modules/aaa_diameter/aaa_diameter.c @@ -22,6 +22,7 @@ #include "../../sr_module.h" #include "../../lib/list.h" +#include "../../ut.h" #include "aaa_impl.h" #include "peer.h" @@ -34,14 +35,22 @@ static void mod_destroy(void); char *dm_conf_filename = "freeDiameter.conf"; char *extra_avps_file; -int aaa_diameter_bind_api(aaa_prot *api); +static int dm_send_request(struct sip_msg *msg, int *app_id, int *cmd_code, + str *avp_json); +static int dm_bind_api(aaa_prot *api); int fd_log_level = FD_LOG_NOTICE; str dm_realm = str_init("diameter.test"); str dm_peer_identity = str_init("server"); /* a.k.a. server.diameter.test */ static cmd_export_t cmds[]= { - {"aaa_bind_api", (cmd_function) aaa_diameter_bind_api, {{0, 0, 0}}, 0}, + {"dm_send_request", (cmd_function)dm_send_request, { + {CMD_PARAM_INT,0,0}, + {CMD_PARAM_INT,0,0}, + {CMD_PARAM_STR,0,0}, {0,0,0}}, + ALL_ROUTES}, + + {"aaa_bind_api", (cmd_function) dm_bind_api, {{0, 0, 0}}, 0}, {0,0,{{0,0,0}},0} }; @@ -169,7 +178,7 @@ static void mod_destroy(void) } -int aaa_diameter_bind_api(aaa_prot *api) +static int dm_bind_api(aaa_prot *api) { if (!api) return -1; @@ -186,3 +195,120 @@ int aaa_diameter_bind_api(aaa_prot *api) return 0; } + + +static int dm_send_request(struct sip_msg *msg, int *app_id, int *cmd_code, + str *avp_json) +{ + aaa_message *dmsg; + struct dict_object *req; + cJSON *avps, *_avp; + + if (fd_dict_search(fd_g_config->cnf_dict, DICT_COMMAND, CMD_BY_CODE_R, + cmd_code, &req, ENOENT) == ENOENT) { + LM_ERR("unrecognized Request command code: %d\n", *cmd_code); + LM_ERR("to fix this, you can define the Request/Answer format in the " + "'extra-avps-file' config file\n"); + return -1; + } + + LM_DBG("found a matching dict entry for command code %d\n", *cmd_code); + + if (!avp_json || !avp_json->s) { + LM_ERR("NULL JSON input\n"); + return -1; + } + + avps = cJSON_Parse(avp_json->s); + if (!avps) { + LM_ERR("failed to parse input JSON ('%.*s' ..., total: %d)\n", + avp_json->len > 512 ? 512 : avp_json->len, avp_json->s, avp_json->len); + return -1; + } + + if (avps->type != cJSON_Array) { + LM_ERR("bad JSON type: must be Array ('%.*s' ..., total: %d)\n", + avp_json->len > 512 ? 512 : avp_json->len, avp_json->s, avp_json->len); + return -1; + } + + dmsg = _dm_create_message(NULL, AAA_CUSTOM, *app_id, *cmd_code); + if (!dmsg) { + LM_ERR("oom\n"); + return -1; + } + + for (_avp = avps->child; _avp; _avp = _avp->next) { + if (_avp->type != cJSON_Object) { + LM_ERR("bad JSON type in Array: AVPs must be Objects ('%.*s' " + "..., total: %d)\n", avp_json->len > 512 ? 512 : avp_json->len, + avp_json->s, avp_json->len); + return -1; + } + + cJSON *avp = _avp->child; // only work with child #0 + struct dict_avp_data dm_avp; + struct dict_object *obj; + char *name; + unsigned int code; + + // TODO: allow dict too + if (!(avp->type & (cJSON_String|cJSON_Number))) { + LM_ERR("bad AVP value: only String allowed ('%.*s' ..., key: %s)\n", + avp_json->len > 512 ? 512 : avp_json->len, avp_json->s, avp->string); + return -1; + } + + if (_isdigit(avp->string[0])) { + str st; + + init_str(&st, avp->string); + if (str2int(&st, &code) != 0) { + LM_ERR("bad AVP key: cannot start with a digit ('%.*s' ..., key: %s)\n", + avp_json->len > 512 ? 512 : avp_json->len, avp_json->s, avp->string); + return -1; + } + + LM_DBG("AVP:: searching AVP by int: %d\n", code); + FD_CHECK(fd_dict_search(fd_g_config->cnf_dict, DICT_AVP, AVP_BY_CODE, + &code, &obj, ENOENT)); + FD_CHECK(fd_dict_getval(obj, &dm_avp)); + + name = dm_avp.avp_name; + } else { + LM_DBG("AVP:: searching AVP by string: %s\n", avp->string); + + FD_CHECK(fd_dict_search(fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, + avp->string, &obj, ENOENT)); + FD_CHECK(fd_dict_getval(obj, &dm_avp)); + + name = avp->string; + code = dm_avp.avp_code; + } + + aaa_map my_avp = {.name = name}; + + if (avp->type & cJSON_String) { + LM_DBG("dbg::: AVP %d (name: '%s', str-val: %s)\n", code, name, avp->valuestring); + if (dm_avp_add(NULL, dmsg, &my_avp, avp->valuestring, + strlen(avp->valuestring), 0) != 0) { + LM_ERR("failed to add AVP %d, aborting request\n", code); + return -1; + } + } else { + LM_DBG("dbg::: AVP %d (name: '%s', int-val: %d)\n", code, name, avp->valueint); + if (dm_avp_add(NULL, dmsg, &my_avp, &avp->valueint, -1, 0) != 0) { + LM_ERR("failed to add AVP %d, aborting request\n", code); + return -1; + } + } + } + + if (dm_send_message(NULL, dmsg, NULL) != 0) { + LM_ERR("failed to queue Diameter request for sending\n"); + return -1; + } + + cJSON_Delete(avps); + return 1; +} diff --git a/modules/aaa_diameter/aaa_impl.c b/modules/aaa_diameter/aaa_impl.c index 6dcee000232..5a342a2690f 100644 --- a/modules/aaa_diameter/aaa_impl.c +++ b/modules/aaa_diameter/aaa_impl.c @@ -204,6 +204,70 @@ 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) +{ + struct msg_hdr *hdr = NULL; + struct msg *msg = *_msg; + struct avp *a = NULL; + struct avp_hdr * h = NULL; + int rc; + str tid; + struct dm_cond **prpl_cond, *rpl_cond; + + FD_CHECK(fd_msg_hdr(msg, &hdr)); + + if (hdr->msg_flags & CMD_FLAG_REQUEST) { + LM_INFO("received a request?! discarding...\n"); + 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; + + FD_CHECK(fd_msg_search_avp(msg, dm_dict.Transaction_Id, &a)); + FD_CHECK(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); + + 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", + tid.len, tid.s); + goto out; + } + rpl_cond = *prpl_cond; + rpl_cond->rc = rc; + + hash_remove_key(pending_replies, tid); + + FD_CHECK(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); + } else { + rpl_cond->is_error = 0; + } + + /* signal the blocked SIP worker that the auth result is available! */ + pthread_mutex_lock(&rpl_cond->mutex); + if (rpl_cond->count == 0) + pthread_cond_signal(&rpl_cond->cond); + rpl_cond->count += 1; + pthread_mutex_unlock(&rpl_cond->mutex); + +out: + FD_CHECK(fd_msg_free(msg)); + *_msg = NULL; + return 0; +} + + int dm_register_callbacks(void) { struct disp_when data; @@ -240,6 +304,28 @@ int dm_register_callbacks(void) FD_CHECK(fd_disp_app_support(data.app, NULL, 0, 1 )); } + /* custom commands */ + { + int i; + + memset(&data, 0, sizeof data); + + for (i = 0; i < n_app_ids; i++) { + /* Initialize the dictionary objects we use */ + FD_CHECK_dict_search(DICT_APPLICATION, APPLICATION_BY_ID, + &app_ids[i], &data.app); + + /* Register the dispatch callback */ + FD_CHECK(fd_disp_register(dm_custom_cmd_reply, + DISP_HOW_APPID, &data, NULL, NULL)); + + /* Advertise support for the respective app */ + FD_CHECK(fd_disp_app_support(data.app, NULL, 0, 1 )); + + LM_DBG("registered a reply callback for App ID %d ...\n", app_ids[i]); + } + } + return 0; } @@ -1074,12 +1160,12 @@ int freeDiameter_init(void) } -int dm_find(aaa_conn *con, aaa_map *map, int op) +int dm_find(aaa_conn *_, aaa_map *map, int op) { struct dict_object *obj; - if (!con || !map) { - LM_ERR("invalid arguments (%p %p)\n", con, map); + if (!map) { + LM_ERR("NULL map argument\n"); return -1; } @@ -1137,7 +1223,8 @@ int dm_find(aaa_conn *con, aaa_map *map, int op) } -aaa_message *dm_create_message(aaa_conn *con, int msg_type) +aaa_message *_dm_create_message(aaa_conn *_, int msg_type, + unsigned int app_id, unsigned int cmd_code) { aaa_message *m; struct dm_message *dm; @@ -1162,12 +1249,20 @@ aaa_message *dm_create_message(aaa_conn *con, int msg_type) memset(dm, 0, sizeof *dm); INIT_LIST_HEAD(&dm->avps); dm->am = m; + dm->app_id = app_id; + dm->cmd_code = cmd_code; return m; } -int dm_avp_add(aaa_conn *con, aaa_message *msg, aaa_map *avp, void *val, +aaa_message *dm_create_message(aaa_conn *_, int msg_type) +{ + return _dm_create_message(_, msg_type, 0, 0); +} + + +int dm_avp_add(aaa_conn *_, aaa_message *msg, aaa_map *avp, void *val, int val_length, int vendor) { struct { @@ -1213,11 +1308,11 @@ int dm_avp_add(aaa_conn *con, aaa_message *msg, aaa_map *avp, void *val, } -int dm_send_message(aaa_conn *con, aaa_message *req, aaa_message **reply) +int dm_send_message(aaa_conn *_, aaa_message *req, aaa_message **reply) { struct dm_message *dm; - if (!con || !req || !my_reply_cond) + if (!req || !my_reply_cond) return -1; dm = (struct dm_message *)(req->avpair); @@ -1238,7 +1333,7 @@ int dm_send_message(aaa_conn *con, aaa_message *req, aaa_message **reply) LM_DBG("message queued for sending\n"); - if (req->type == AAA_AUTH) { + if (req->type == AAA_AUTH || req->type == AAA_CUSTOM) { LM_DBG("awaiting auth reply...\n"); pthread_mutex_lock(&my_reply_cond->mutex); @@ -1275,7 +1370,7 @@ void _dm_destroy_message(aaa_message *msg) shm_free(msg); } -int dm_destroy_message(aaa_conn *conn, aaa_message *msg) +int dm_destroy_message(aaa_conn *_, aaa_message *msg) { if (!msg) return 0; diff --git a/modules/aaa_diameter/aaa_impl.h b/modules/aaa_diameter/aaa_impl.h index fb6fe02bd6e..9763a9ac0aa 100644 --- a/modules/aaa_diameter/aaa_impl.h +++ b/modules/aaa_diameter/aaa_impl.h @@ -63,6 +63,7 @@ struct _dm_dict { struct dict_object *SIP_AOR; struct dict_object *SIP_Method; + struct dict_object *Transaction_Id; struct dict_object *Event_Timestamp; struct dict_object *Route_Record; }; @@ -70,6 +71,9 @@ struct _dm_dict { struct dm_message { aaa_message *am; /* back-reference, useful during cleanup */ + unsigned int app_id; /* these are used when sending */ + unsigned int cmd_code; /* custom Diameter requests */ + str sip_method; struct dm_cond *reply_cond; /* the cond to signal on reply arrival */ @@ -110,11 +114,13 @@ int dm_init_sip_application(void); int dm_register_osips_avps(void); int dm_register_callbacks(void); -int dm_find(aaa_conn *con, aaa_map *map, int op); -aaa_message *dm_create_message(aaa_conn *con, int msg_type); -int dm_avp_add(aaa_conn *con, aaa_message *msg, aaa_map *avp, void *val, +int dm_find(aaa_conn *_, aaa_map *map, int op); +aaa_message *dm_create_message(aaa_conn *_, int msg_type); +aaa_message *_dm_create_message(aaa_conn *_, int msg_type, + unsigned int app_id, unsigned int cmd_code); +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 *con, aaa_message *req, aaa_message **_); +int dm_send_message(aaa_conn *_, aaa_message *req, aaa_message **__); 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 f43918ee511..994e03d35a9 100644 --- a/modules/aaa_diameter/app_opensips/app_opensips.c +++ b/modules/aaa_diameter/app_opensips/app_opensips.c @@ -68,6 +68,7 @@ static struct { struct dict_object * Accounting_Record_Type; struct dict_object * Acct_Session_Id; struct dict_object * Event_Timestamp; + struct dict_object * Transaction_Id; struct dict_object * Auth_Application_Id; struct dict_object * Auth_Session_State; @@ -700,6 +701,8 @@ static int os_entry(char *confstring) char *extra_avps_file; int lib_mode; + CHECK_FCT(register_osips_avps()); + CHECK_FCT(parse_conf_string(confstring, &extra_avps_file, &lib_mode)); CHECK_FCT(parse_extra_avps(extra_avps_file)); free(extra_avps_file); @@ -707,8 +710,6 @@ static int os_entry(char *confstring) if (lib_mode) return 0; - CHECK_FCT(register_osips_avps()); - /* Initialize the dictionary objects we use */ CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Accounting-Record-Number", &dm_dict.Accounting_Record_Number, ENOENT) ); CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Accounting-Record-Type", &dm_dict.Accounting_Record_Type, ENOENT) ); diff --git a/modules/aaa_diameter/app_opensips/avps.c b/modules/aaa_diameter/app_opensips/avps.c index f8724fd188e..ea6b7f86f5f 100644 --- a/modules/aaa_diameter/app_opensips/avps.c +++ b/modules/aaa_diameter/app_opensips/avps.c @@ -312,6 +312,24 @@ static int dm_register_custom_sip_avps(void) FD_CHECK_dict_new(DICT_AVP, &data, UTF8String_type, NULL); } + /* Transaction-Id */ + { + /* + The Transaction-Id AVP (AVP Code 231) is of type UTF8String + and represents a unique ID of the transaction, to facilitate + reply matching + */ + struct dict_avp_data data = { + 231, /* Code */ + 0, /* Vendor */ + "Transaction-Id", /* Name */ + AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, /* Fixed flags */ + AVP_FLAG_MANDATORY, /* Fixed flag values */ + AVP_TYPE_OCTETSTRING /* base type of data */ + }; + FD_CHECK_dict_new(DICT_AVP, &data, NULL, NULL); + } + return 0; } @@ -439,7 +457,7 @@ int parse_attr_line(char *line, ssize_t len) enum dict_avp_basetype avp_type; if (len < attr_len || strncasecmp(p, "ATTRIBUTE", attr_len)) - goto error; + return 1; p += attr_len; len -= attr_len; @@ -508,12 +526,289 @@ int parse_attr_line(char *line, ssize_t len) } +unsigned int app_ids[64], n_app_ids; +int parse_app_def(char *line, FILE *fp) +{ + unsigned int app_id = -1; + int i, len = strlen(line); + char *p = line, *newp, *app_name; + + if (n_app_ids >= 64) { + printf("ERROR: max allowed Applications reached (64)\n"); + return -1; + } + + if (len < strlen("APPLICATION") || memcmp(p, "APPLICATION", 11)) + return 1; + + p += 11; + len -= 11; + + while (isspace(*p)) { p++; len--; } + + app_id = (unsigned int)strtoul(p, &newp, 10); + if (app_id < 0) { + printf("ERROR: bad Application ID: '... | %s'\n", p); + return -1; + } + + len -= newp - p; + p = newp; + + while (isspace(*p)) { p++; len--; } + + if (len <= 0) { + printf("ERROR: empty Application Name not allowed\n"); + return -1; + } + + app_name = p; + p += len - 1; + + while (p > app_name && isspace(*p)) { p--; } + *(++p) = '\0'; + + struct dict_application_data app_reg = {app_id, app_name}; + FD_CHECK_dict_new(DICT_APPLICATION, &app_reg, NULL, NULL); + + LOG_DBG("registered Application %d (%s)\n", app_id, app_name); + + /* store the App ID so OpenSIPS can register a reply cb later */ + for (i = 0; i < n_app_ids; i++) + if (app_ids[i] == app_id) + return 1; + + app_ids[n_app_ids++] = app_id; + return 1; +} + + +#define CMD_REQUEST 1 +#define CMD_ANSWER 2 +int parse_command_def(char *line, FILE *fp, int cmd_type) +{ + struct dict_object *cmd = NULL; + unsigned int cmd_code = -1; + char *p = line, cmd_name[128 + 1], *avp_name, *bkp, *newp; + size_t buflen = strlen(line); + int i, len = buflen, cmd_name_len = -1, avp_count = 0; + struct { + char name[64 + 1]; + int name_len; + enum rule_position pos; + int max_repeats; + } avps[128]; + + switch (cmd_type) { + case CMD_REQUEST: + if (len < strlen("REQUEST") || memcmp(p, "REQUEST", 7)) + return 1; + + p += 7; + len -= 7; + break; + + case CMD_ANSWER: + if (len < strlen("ANSWER") || memcmp(p, "ANSWER", 6)) + return 1; + + p += 6; + len -= 6; + break; + } + + cmd_code = (unsigned int)strtoul(p, &newp, 10); + if (cmd_code < 0) { + printf("ERROR: bad AVP cmd code: '... | %s'\n", p); + return -1; + } + + len -= newp - p; + p = newp; + + while (isspace(*p)) { p++; len--; } + + bkp = p; + p += len - 1; + + while (p > bkp && isspace(*p)) { p--; } + p++; + + cmd_name_len = p - bkp; + if (cmd_name_len > 128) { + printf("ERROR: max Command Name length exceeded (128)\n"); + return -1; + } + + memcpy(cmd_name, bkp, cmd_name_len); + cmd_name[cmd_name_len] = '\0'; + + LOG_DBG("parsed Cmd-Code %d (%s)\n", cmd_code, cmd_name); + + while (getline(&line, &buflen, fp) >= 0) { + p = line; + len = strlen(p); + + while (isspace(*p)) { p++; len--; } + + if (*p == '{') + continue; + + if (*p == '}' || !strlen(p)) + goto define_req; + + if (avp_count >= 128) { + printf("ERROR: max AVP count exceeded (128)\n"); + return -1; + } + + avp_name = p; + while (*p && !isspace(*p)) { p++; len--; } + avps[avp_count].name_len = p - avp_name; + + if (avps[avp_count].name_len > 64) { + printf("ERROR: AVP max name length exceeded (64)\n"); + return -1; + } + + memcpy(&avps[avp_count].name, avp_name, avps[avp_count].name_len); + avps[avp_count].name[avps[avp_count].name_len] = '\0'; + + while (isspace(*p)) { p++; len--; } + + if (*p != '|') + goto error; + + p++; len--; + while (isspace(*p)) { p++; len--; } + + switch (*p) { + case 'F': + if (len < strlen("FIXED_HEAD") || memcmp(p, "FIXED_HEAD", 10)) + goto error; + + avps[avp_count].pos = RULE_FIXED_HEAD; + p += 10; + len -= 10; + break; + + case 'R': + if (len < strlen("REQUIRED") || memcmp(p, "REQUIRED", 8)) + goto error; + + avps[avp_count].pos = RULE_REQUIRED; + p += 8; + len -= 8; + break; + + case 'O': + if (len < strlen("OPTIONAL") || memcmp(p, "OPTIONAL", 8)) + goto error; + + avps[avp_count].pos = RULE_OPTIONAL; + p += 8; + len -= 8; + break; + + default: + printf("ERROR: bad AVP flag in: '... | %s'\n", p); + goto error; + } + + while (isspace(*p)) { p++; len--; } + + if (*p != '|') + goto error; + + p++; len--; + while (isspace(*p)) { p++; len--; } + + avps[avp_count].max_repeats = (int)strtol(p, NULL, 10); + if (avps[avp_count].max_repeats < 0) { + printf("ERROR: bad AVP max count: '... | %s'\n", p); + goto error; + } + + LOG_DBG("AVP def: %.*s | %d | %d\n", avps[avp_count].name_len, + avps[avp_count].name, avps[avp_count].pos, + avps[avp_count].max_repeats); + avp_count++; + } + +define_req: + LOG_DBG("defining request (%d AVPs in total)...\n", avp_count); + + struct dict_cmd_data req_data = { + cmd_code, + cmd_name, + CMD_FLAG_REQUEST | CMD_FLAG_PROXIABLE + | (cmd_type == CMD_REQUEST ? CMD_FLAG_ERROR : 0), /* Fixed flags */ + (cmd_type == CMD_REQUEST ? CMD_FLAG_REQUEST : 0) + | CMD_FLAG_PROXIABLE /* Fixed flag values */ + }; + + FD_CHECK(fd_dict_new(fd_g_config->cnf_dict, DICT_COMMAND, &req_data, NULL, &cmd)); + + for (i = 0; i < avp_count; i++) { + struct dict_rule_data data = {NULL, avps[i].pos, + (avps[i].pos == RULE_FIXED_HEAD), -1, avps[i].max_repeats}; + + FD_CHECK(fd_dict_search(fd_g_config->cnf_dict, + DICT_AVP, AVP_BY_NAME, avps[i].name, &data.rule_avp, 0)); + + if (!data.rule_avp) { + printf("ERROR: failed to locate AVP: %s\n", avps[i].name); + return -1; + } + + FD_CHECK_dict_new(DICT_RULE, &data, cmd, NULL); + } + + { + /* all custom requests and replies MUST include Transaction-Id */ + struct dict_rule_data data = {NULL, RULE_REQUIRED, 0, -1, 1}; + + FD_CHECK(fd_dict_search(fd_g_config->cnf_dict, + DICT_AVP, AVP_BY_NAME, "Transaction-Id", &data.rule_avp, 0)); + + if (!data.rule_avp) { + printf("ERROR: failed to locate Transaction-Id AVP\n"); + return -1; + } + + FD_CHECK_dict_new(DICT_RULE, &data, cmd, NULL); + } + + /* all replies MUST include a Result-Code */ + if (cmd_type == CMD_ANSWER) { + struct dict_rule_data data = {NULL, RULE_REQUIRED, 0, -1, 1}; + + FD_CHECK(fd_dict_search(fd_g_config->cnf_dict, + DICT_AVP, AVP_BY_NAME, "Result-Code", &data.rule_avp, 0)); + + if (!data.rule_avp) { + printf("ERROR: failed to locate Result-Code AVP\n"); + return -1; + } + + FD_CHECK_dict_new(DICT_RULE, &data, cmd, NULL); + } + + return 0; + +error: + printf("ERROR-2: failed to parse line: %s\n", line); + return -1; +} + + int parse_extra_avps(const char *extra_avps_file) { FILE *fp; char *line = NULL; size_t len = 0; ssize_t read; + int answers_needed = 0, rc, ret = 0; if (!extra_avps_file) return 0; @@ -532,14 +827,52 @@ int parse_extra_avps(const char *extra_avps_file) if (*p == '#' || p - line >= read) continue; - if (parse_attr_line(p, read - (p - line)) == 0) + rc = parse_attr_line(p, read - (p - line)); + if (rc < 0) { + ret = -1; + goto out; + } else if (rc == 0) { + continue; + } + + rc = parse_app_def(p, fp); + if (rc < 0) { + ret = -1; + goto out; + } else if (rc == 0) { + continue; + } + + rc = parse_command_def(p, fp, CMD_REQUEST); + if (rc < 0) { + ret = -1; + goto out; + } else if (rc == 0) { + answers_needed++; + continue; + } + + rc = parse_command_def(p, fp, CMD_ANSWER); + if (rc < 0) { + ret = -1; + goto out; + } else if (rc == 0) { + answers_needed--; continue; + } // unknown line... ignoring } + if (answers_needed > 0) { + printf("ERROR: bad config file, at least one Diameter Answer " + "definition is missing\n"); + ret = -1; + } + +out: fclose(fp); free(line); - return 0; + return ret; } diff --git a/modules/aaa_diameter/app_opensips/avps.h b/modules/aaa_diameter/app_opensips/avps.h index 8a43d49ec23..bfa93b9609b 100644 --- a/modules/aaa_diameter/app_opensips/avps.h +++ b/modules/aaa_diameter/app_opensips/avps.h @@ -68,4 +68,7 @@ int register_osips_avps(void); int parse_extra_avps(const char *extra_avps_file); +extern unsigned int app_ids[]; +extern unsigned int n_app_ids; + #endif /* _APP_OPENSIPS_AVPS_H */ diff --git a/modules/aaa_diameter/peer.c b/modules/aaa_diameter/peer.c index 6a3ef7f2f8e..15d9f88c6b3 100644 --- a/modules/aaa_diameter/peer.c +++ b/modules/aaa_diameter/peer.c @@ -397,6 +397,77 @@ static int dm_acct(struct dm_message *msg) } +static int dm_custom_req(struct dm_message *msg) +{ + struct msg *dmsg; + struct avp *avp; + struct dm_avp *dm_avp; + struct list_head *it; + union avp_value val; + struct dict_object *req; /* a custom Diameter request */ + + FD_CHECK(fd_dict_search(fd_g_config->cnf_dict, DICT_COMMAND, CMD_BY_CODE_R, + &msg->cmd_code, &req, ENOENT)); + + FD_CHECK(fd_msg_new(req, MSGFL_ALLOC_ETEID, &dmsg)); + + /* App id */ + { + struct msg_hdr *h; + FD_CHECK(fd_msg_hdr(dmsg, &h)); + h->msg_appl = msg->app_id; + } + + list_for_each (it, &msg->avps) { + struct dict_object *obj; + + dm_avp = list_entry(it, struct dm_avp, list); + + /* each AVP must be recognized, otherwise we abort the request */ + FD_CHECK_dict_search(DICT_AVP, AVP_BY_NAME, dm_avp->name.s, &obj); + FD_CHECK(fd_msg_avp_new(obj, 0, &avp)); + + memset(&val, 0, sizeof val); + if (dm_avp->value.len < 0) { + val.u32 = (unsigned int)(unsigned long)dm_avp->value.s; + LM_DBG("appending AVP: %s: int(%lu)\n", + dm_avp->name.s, (unsigned long)dm_avp->value.s); + } else { + val.os.data = (unsigned char *)dm_avp->value.s; + val.os.len = dm_avp->value.len; + LM_DBG("appending AVP: %s: str(%.*s)\n", + dm_avp->name.s, dm_avp->value.len, dm_avp->value.s); + } + FD_CHECK(fd_msg_avp_setvalue(avp, &val)); + FD_CHECK(fd_msg_avp_add(dmsg, MSG_BRW_LAST_CHILD, avp)); + } + + /* Transaction-Id */ + { + struct timeval now; + char tid[16 + 1]; + str tid_str; + + FD_CHECK(fd_msg_avp_new(dm_dict.Transaction_Id, 0, &avp)); + + gettimeofday(&now, NULL); + sprintf(tid, "%ld%ld", now.tv_sec, now.tv_usec); + + memset(&val, 0, sizeof val); + val.os.data = (unsigned char *)tid; + val.os.len = strlen(tid); + FD_CHECK(fd_msg_avp_setvalue(avp, &val)); + FD_CHECK(fd_msg_avp_add(dmsg, MSG_BRW_LAST_CHILD, avp)); + + tid_str = (str){(char *)val.os.data, val.os.len}; + FD_CHECK(dm_add_pending_reply(&tid_str, msg->reply_cond)); + } + + FD_CHECK(fd_msg_send(&dmsg, NULL, NULL)); + return 0; +} + + static inline int diameter_send_msg(struct dm_message *msg) { aaa_message *am = msg->am; @@ -406,6 +477,8 @@ static inline int diameter_send_msg(struct dm_message *msg) return dm_auth(msg); case AAA_ACCT: return dm_acct(msg); + case AAA_CUSTOM: + return dm_custom_req(msg); default: LM_ERR("unsupported AAA message type (%d), skipping\n", am->type); } @@ -445,6 +518,8 @@ static int dm_prepare_globals(void) FD_CHECK(fd_dict_search(fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "SIP-Method", &dm_dict.SIP_Method, ENOENT)); + FD_CHECK(fd_dict_search(fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, + "Transaction-Id", &dm_dict.Transaction_Id, ENOENT)); FD_CHECK(fd_dict_search(fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Route-Record", &dm_dict.Route_Record, ENOENT));