pespin has uploaded this change for review. ( https://gerrit.osmocom.org/c/osmo-mgw/+/39723?usp=email )
Change subject: mgw: Split MGCP LocalConnectionOptions parsing from updating endpoint ...................................................................... mgw: Split MGCP LocalConnectionOptions parsing from updating endpoint Split current code into 2 steps, so first read-only parsing can be done, then when all parsing and checks have been done, finally can be applied o the object. Change-Id: If75731fb92329dca6d092ffc7ff17cb354d28c0d --- M include/osmocom/mgcp/mgcp_endp.h M include/osmocom/mgcp/mgcp_protocol.h M src/libosmo-mgcp/mgcp_endp.c M src/libosmo-mgcp/mgcp_msg.c M src/libosmo-mgcp/mgcp_protocol.c 5 files changed, 259 insertions(+), 244 deletions(-) git pull ssh://gerrit.osmocom.org:29418/osmo-mgw refs/changes/23/39723/1 diff --git a/include/osmocom/mgcp/mgcp_endp.h b/include/osmocom/mgcp/mgcp_endp.h index fbe0850..8a2c8a2 100644 --- a/include/osmocom/mgcp/mgcp_endp.h +++ b/include/osmocom/mgcp/mgcp_endp.h @@ -149,6 +149,7 @@ const struct mgcp_trunk *trunk); struct mgcp_endpoint *mgcp_endp_find_specific(const char *epname, const struct mgcp_trunk *trunk); +void mgcp_endp_update_lco(struct mgcp_endpoint *endp, const struct mgcp_lco *lco); void mgcp_endp_release(struct mgcp_endpoint *endp); struct mgcp_conn *mgcp_endp_get_conn(struct mgcp_endpoint *endp, const char *id); diff --git a/include/osmocom/mgcp/mgcp_protocol.h b/include/osmocom/mgcp/mgcp_protocol.h index 2ef4c3e..9f79334 100644 --- a/include/osmocom/mgcp/mgcp_protocol.h +++ b/include/osmocom/mgcp/mgcp_protocol.h @@ -42,12 +42,26 @@ mgcp_codecset_reset(&sdp->cset); } +/* Local connection options */ +struct mgcp_lco { + bool present; + char *codec; /* talloc-allocated to some parent */ + int pkt_period_min; /* time in ms */ + int pkt_period_max; /* time in ms */ +}; +static inline void mgcp_lco_init(struct mgcp_lco *lco) +{ + *lco = (struct mgcp_lco){}; +} +char *get_lco_identifier(const char *options); +int check_local_cx_options(void *ctx, const char *options); #define MGCP_PARSE_HDR_PARS_OSMUX_CID_UNSET (-2) #define MGCP_PARSE_HDR_PARS_OSMUX_CID_WILDCARD (-1) struct mgcp_parse_hdr_pars { - const char *local_options; + const char *lco_string; + struct mgcp_lco lco; const char *callid; const char *connid; enum mgcp_connection_mode mode; @@ -59,15 +73,14 @@ static inline void mgcp_parse_hdr_pars_init(struct mgcp_parse_hdr_pars *hpars) { - *hpars = (struct mgcp_parse_hdr_pars){ - .local_options = NULL, - .callid = NULL, - .connid = NULL, - .mode = MGCP_CONN_NONE, - .remote_osmux_cid = MGCP_PARSE_HDR_PARS_OSMUX_CID_UNSET, - .have_sdp = false, - .x_osmo_ign = 0, - }; + hpars->lco_string = NULL; + mgcp_lco_init(&hpars->lco); + hpars->callid = NULL; + hpars->connid = NULL; + hpars->mode = MGCP_CONN_NONE; + hpars->remote_osmux_cid = MGCP_PARSE_HDR_PARS_OSMUX_CID_UNSET; + hpars->have_sdp = false; + hpars->x_osmo_ign = 0; } /* Internal structure while parsing a request */ @@ -111,18 +124,8 @@ int mgcp_cause; }; -/* Local connection options */ -struct mgcp_lco { - char *string; - char *codec; - int pkt_period_min; /* time in ms */ - int pkt_period_max; /* time in ms */ -}; - char *mgcp_debug_get_last_endpoint_name(void); -char *get_lco_identifier(const char *options); -int check_local_cx_options(void *ctx, const char *options); struct mgcp_rtp_end; struct mgcp_endpoint; diff --git a/src/libosmo-mgcp/mgcp_endp.c b/src/libosmo-mgcp/mgcp_endp.c index 1d2287c..a4ab860 100644 --- a/src/libosmo-mgcp/mgcp_endp.c +++ b/src/libosmo-mgcp/mgcp_endp.c @@ -764,6 +764,22 @@ return NULL; } +/* Helps assigning a new lco structure, since "codec" is talloc allocated. */ +void mgcp_endp_update_lco(struct mgcp_endpoint *endp, const struct mgcp_lco *lco) +{ + /* First free old talloc allocated codec string: */ + talloc_free(endp->local_options.codec); + endp->local_options.codec = NULL; + + if (lco) { + endp->local_options = *lco; + if (lco->codec) + endp->local_options.codec = talloc_strdup(endp, lco->codec); + } else { + endp->local_options = (struct mgcp_lco){0}; + } +} + /*! release endpoint, all open connections are closed. * \param[in] endp endpoint to release */ void mgcp_endp_release(struct mgcp_endpoint *endp) @@ -785,10 +801,7 @@ /* Reset endpoint parameters and states */ talloc_free(endp->callid); endp->callid = NULL; - talloc_free(endp->local_options.string); - endp->local_options.string = NULL; - talloc_free(endp->local_options.codec); - endp->local_options.codec = NULL; + mgcp_endp_update_lco(endp, NULL); if (endp->trunk->trunk_type == MGCP_TRUNK_E1) { uint8_t ts = e1_ts_nr_from_epname(endp->name); diff --git a/src/libosmo-mgcp/mgcp_msg.c b/src/libosmo-mgcp/mgcp_msg.c index af4f9d0..afeadd5 100644 --- a/src/libosmo-mgcp/mgcp_msg.c +++ b/src/libosmo-mgcp/mgcp_msg.c @@ -101,6 +101,197 @@ return MGCP_CONN_NONE; } +/*! Helper function for check_local_cx_options() to get a pointer of the next + * lco option identifier + * \param[in] lco string + * \returns pointer to the beginning of the LCO identifier, NULL on failure */ +char *get_lco_identifier(const char *options) +{ + char *ptr; + unsigned int count = 0; + + /* Jump to the end of the lco identifier */ + ptr = strstr(options, ":"); + if (!ptr) + return NULL; + + /* Walk backwards until the pointer points to the beginning of the + * lco identifier. We know that we stand at the beginning when we + * are either at the beginning of the memory or see a space or + * comma. (this is tolerant, it will accept a:10, b:11 as well as + * a:10,b:11) */ + while (1) { + /* Endless loop protection */ + if (count > 10000) + return NULL; + else if (ptr < options || *ptr == ' ' || *ptr == ',') { + ptr++; + break; + } + ptr--; + count++; + } + + /* Check if we got any result */ + if (*ptr == ':') + return NULL; + + return ptr; +} + +/*! Check the LCO option. This function checks for multiple appearance of LCO +* options, which is illegal +* \param[in] ctx talloc context +* \param[in] lco string +* \returns 0 on success, -1 on failure */ +int check_local_cx_options(void *ctx, const char *options) +{ + int i; + char *options_copy; + char *lco_identifier; + char *lco_identifier_end; + char *next_lco_identifier; + + char **lco_seen; + unsigned int lco_seen_n = 0; + + if (!options) + return -1; + + lco_seen = + (char **)talloc_zero_size(ctx, strlen(options) * sizeof(char *)); + options_copy = talloc_strdup(ctx, options); + lco_identifier = options_copy; + + do { + /* Move the lco_identifier pointer to the beginning of the + * current lco option identifier */ + lco_identifier = get_lco_identifier(lco_identifier); + if (!lco_identifier) + goto error; + + /* Look ahead to the next LCO option early, since we + * will parse destructively */ + next_lco_identifier = strstr(lco_identifier + 1, ","); + + /* Pinch off the end of the lco field identifier name + * and see if we still got something, also check if + * there is some value after the colon. */ + lco_identifier_end = strstr(lco_identifier, ":"); + if (!lco_identifier_end) + goto error; + if (*(lco_identifier_end + 1) == ' ' + || *(lco_identifier_end + 1) == ',' + || *(lco_identifier_end + 1) == '\0') + goto error; + *lco_identifier_end = '\0'; + if (strlen(lco_identifier) == 0) + goto error; + + /* Check if we have already seen the current field identifier + * before. If yes, we must bail, an LCO must only appear once + * in the LCO string */ + for (i = 0; i < lco_seen_n; i++) { + if (strcasecmp(lco_seen[i], lco_identifier) == 0) + goto error; + } + lco_seen[lco_seen_n] = lco_identifier; + lco_seen_n++; + + /* The first identifier must always be found at the beginnning + * of the LCO string */ + if (lco_seen[0] != options_copy) + goto error; + + /* Go to the next lco option */ + lco_identifier = next_lco_identifier; + } while (lco_identifier); + + talloc_free(lco_seen); + talloc_free(options_copy); + return 0; +error: + talloc_free(lco_seen); + talloc_free(options_copy); + return -1; +} + +/* Set the LCO from a string (see RFC 3435). +* The string is stored in the 'string' field. A NULL string is handled exactly +* like an empty string, the 'string' field is never NULL after this function +* has been called. */ +static int mgcp_parse_lco(void *ctx, struct mgcp_lco *lco, const char *options) +{ + const char *lco_id; + char codec[17]; + char nt[17]; + int len; + + if (!options) + return 0; + if (strlen(options) == 0) + return 0; + + lco->present = true; + + /* Make sure the encoding of the LCO is consistent before we proceed */ + if (check_local_cx_options(ctx, options) != 0) { + LOGP(DLMGCP, LOGL_ERROR, + "local CX options: Internal inconsistency in Local Connection Options!\n"); + return -524; + } + + lco_id = options; + while ((lco_id = get_lco_identifier(lco_id))) { + switch (tolower(lco_id[0])) { + case 'p': + if (sscanf(lco_id + 1, ":%d-%d", + &lco->pkt_period_min, &lco->pkt_period_max) == 1) + lco->pkt_period_max = lco->pkt_period_min; + break; + case 'a': + /* FIXME: LCO also supports the negotiation of more than one codec. + * (e.g. a:PCMU;G726-32) But this implementation only supports a single + * codec only. Ignoring all but the first codec. */ + if (sscanf(lco_id + 1, ":%16[^,;]", codec) == 1) { + talloc_free(lco->codec); + /* MGCP header is case insensive, and we'll need + codec in uppercase when using it later: */ + len = strlen(codec); + lco->codec = talloc_size(ctx, len + 1); + osmo_str_toupper_buf(lco->codec, len + 1, codec); + } + break; + case 'n': + if (lco_id[1] == 't' && sscanf(lco_id + 2, ":%16[^,]", nt) == 1) + break; + /* else: fall through to print notice log */ + default: + LOGP(DLMGCP, LOGL_NOTICE, + "LCO: unhandled option: '%c'/%d in \"%s\"\n", + *lco_id, *lco_id, options); + break; + } + + lco_id = strchr(lco_id, ','); + if (!lco_id) + break; + } + + LOGP(DLMGCP, LOGL_DEBUG, + "local CX options: lco->pkt_period_max: %d, lco->codec: %s\n", + lco->pkt_period_max, lco->codec); + + /* Check if the packetization fits the 20ms raster */ + if (lco->pkt_period_min % 20 && lco->pkt_period_max % 20) { + LOGP(DLMGCP, LOGL_ERROR, + "local CX options: packetization interval is not a multiple of 20ms!\n"); + return -535; + } + + return 0; +} + /*! Analyze and parse the the hader of an MGCP messeage string. * \param[out] pdata caller provided memory to store the parsing results. * \param[in] data mgcp message string. @@ -177,6 +368,7 @@ { struct mgcp_parse_hdr_pars *hp = &pdata->hpars; char *line; + int rc; mgcp_parse_hdr_pars_init(hp); @@ -188,7 +380,24 @@ switch (toupper(line[0])) { case 'L': - hp->local_options = (const char *)line + 3; + hp->lco_string = (const char *)line + 3; + rc = mgcp_parse_lco(pdata, &hp->lco, hp->lco_string); + if (rc < 0) { + LOG_MGCP_PDATA(pdata, LOGL_NOTICE, "Invalid LocalConnectionOptions line: '%s'", line); + switch (pdata->rq->verb) { + case MGCP_VERB_CRCX: + rate_ctr_inc(rate_ctr_group_get_ctr(pdata->rq->trunk->ratectr.mgcp_crcx_ctr_group, + MGCP_CRCX_FAIL_INVALID_CONN_OPTIONS)); + break; + case MGCP_VERB_MDCX: + rate_ctr_inc(rate_ctr_group_get_ctr(pdata->rq->trunk->ratectr.mgcp_mdcx_ctr_group, + MGCP_MDCX_FAIL_INVALID_CONN_OPTIONS)); + break; + default: + break; + } + return rc; + } break; case 'C': hp->callid = (const char *)line + 3; diff --git a/src/libosmo-mgcp/mgcp_protocol.c b/src/libosmo-mgcp/mgcp_protocol.c index 06da91a..49a4e83 100644 --- a/src/libosmo-mgcp/mgcp_protocol.c +++ b/src/libosmo-mgcp/mgcp_protocol.c @@ -455,199 +455,6 @@ return create_ok_response(rq->trunk, rq->endp, 200, "AUEP", rq->pdata->trans); } -/*! Helper function for check_local_cx_options() to get a pointer of the next - * lco option identifier - * \param[in] lco string - * \returns pointer to the beginning of the LCO identifier, NULL on failure */ -char *get_lco_identifier(const char *options) -{ - char *ptr; - unsigned int count = 0; - - /* Jump to the end of the lco identifier */ - ptr = strstr(options, ":"); - if (!ptr) - return NULL; - - /* Walk backwards until the pointer points to the beginning of the - * lco identifier. We know that we stand at the beginning when we - * are either at the beginning of the memory or see a space or - * comma. (this is tolerant, it will accept a:10, b:11 as well as - * a:10,b:11) */ - while (1) { - /* Endless loop protection */ - if (count > 10000) - return NULL; - else if (ptr < options || *ptr == ' ' || *ptr == ',') { - ptr++; - break; - } - ptr--; - count++; - } - - /* Check if we got any result */ - if (*ptr == ':') - return NULL; - - return ptr; -} - -/*! Check the LCO option. This function checks for multiple appearence of LCO - * options, which is illegal - * \param[in] ctx talloc context - * \param[in] lco string - * \returns 0 on success, -1 on failure */ -int check_local_cx_options(void *ctx, const char *options) -{ - int i; - char *options_copy; - char *lco_identifier; - char *lco_identifier_end; - char *next_lco_identifier; - - char **lco_seen; - unsigned int lco_seen_n = 0; - - if (!options) - return -1; - - lco_seen = - (char **)talloc_zero_size(ctx, strlen(options) * sizeof(char *)); - options_copy = talloc_strdup(ctx, options); - lco_identifier = options_copy; - - do { - /* Move the lco_identifier pointer to the beginning of the - * current lco option identifier */ - lco_identifier = get_lco_identifier(lco_identifier); - if (!lco_identifier) - goto error; - - /* Look ahead to the next LCO option early, since we - * will parse destructively */ - next_lco_identifier = strstr(lco_identifier + 1, ","); - - /* Pinch off the end of the lco field identifier name - * and see if we still got something, also check if - * there is some value after the colon. */ - lco_identifier_end = strstr(lco_identifier, ":"); - if (!lco_identifier_end) - goto error; - if (*(lco_identifier_end + 1) == ' ' - || *(lco_identifier_end + 1) == ',' - || *(lco_identifier_end + 1) == '\0') - goto error; - *lco_identifier_end = '\0'; - if (strlen(lco_identifier) == 0) - goto error; - - /* Check if we have already seen the current field identifier - * before. If yes, we must bail, an LCO must only appear once - * in the LCO string */ - for (i = 0; i < lco_seen_n; i++) { - if (strcasecmp(lco_seen[i], lco_identifier) == 0) - goto error; - } - lco_seen[lco_seen_n] = lco_identifier; - lco_seen_n++; - - /* The first identifier must always be found at the beginnning - * of the LCO string */ - if (lco_seen[0] != options_copy) - goto error; - - /* Go to the next lco option */ - lco_identifier = next_lco_identifier; - } while (lco_identifier); - - talloc_free(lco_seen); - talloc_free(options_copy); - return 0; -error: - talloc_free(lco_seen); - talloc_free(options_copy); - return -1; -} - -/* Set the LCO from a string (see RFC 3435). - * The string is stored in the 'string' field. A NULL string is handled exactly - * like an empty string, the 'string' field is never NULL after this function - * has been called. */ -static int set_local_cx_options(void *ctx, struct mgcp_lco *lco, - const char *options) -{ - char *lco_id; - char codec[17]; - char nt[17]; - int len; - - if (!options) - return 0; - if (strlen(options) == 0) - return 0; - - /* Make sure the encoding of the LCO is consistant before we proceed */ - if (check_local_cx_options(ctx, options) != 0) { - LOGP(DLMGCP, LOGL_ERROR, - "local CX options: Internal inconsistency in Local Connection Options!\n"); - return 524; - } - - talloc_free(lco->string); - lco->string = talloc_strdup(ctx, options); - - lco_id = lco->string; - while ((lco_id = get_lco_identifier(lco_id))) { - switch (tolower(lco_id[0])) { - case 'p': - if (sscanf(lco_id + 1, ":%d-%d", - &lco->pkt_period_min, &lco->pkt_period_max) == 1) - lco->pkt_period_max = lco->pkt_period_min; - break; - case 'a': - /* FIXME: LCO also supports the negotiation of more than one codec. - * (e.g. a:PCMU;G726-32) But this implementation only supports a single - * codec only. Ignoring all but the first codec. */ - if (sscanf(lco_id + 1, ":%16[^,;]", codec) == 1) { - talloc_free(lco->codec); - /* MGCP header is case insensive, and we'll need - codec in uppercase when using it later: */ - len = strlen(codec); - lco->codec = talloc_size(ctx, len + 1); - osmo_str_toupper_buf(lco->codec, len + 1, codec); - } - break; - case 'n': - if (lco_id[1] == 't' && sscanf(lco_id + 2, ":%16[^,]", nt) == 1) - break; - /* else: fall throught to print notice log */ - default: - LOGP(DLMGCP, LOGL_NOTICE, - "LCO: unhandled option: '%c'/%d in \"%s\"\n", - *lco_id, *lco_id, lco->string); - break; - } - - lco_id = strchr(lco_id, ','); - if (!lco_id) - break; - } - - LOGP(DLMGCP, LOGL_DEBUG, - "local CX options: lco->pkt_period_max: %i, lco->codec: %s\n", - lco->pkt_period_max, lco->codec); - - /* Check if the packetization fits the 20ms raster */ - if (lco->pkt_period_min % 20 && lco->pkt_period_max % 20) { - LOGP(DLMGCP, LOGL_ERROR, - "local CX options: packetization interval is not a multiple of 20ms!\n"); - return 535; - } - - return 0; -} - uint32_t mgcp_rtp_packet_duration(const struct mgcp_endpoint *endp, const struct mgcp_rtp_end *rtp) { @@ -907,6 +714,10 @@ /* Update endp->x_osmo_ign: */ endp->x_osmo_ign |= hpars->x_osmo_ign; + /* Set local connection options, if present */ + if (hpars->lco.present) + mgcp_endp_update_lco(endp, &hpars->lco); + if (!endp->callid) { /* Claim endpoint resources. This will also set the callid, * creating additional connections will only be possible if @@ -953,19 +764,6 @@ } /* else: -1 (wildcard) */ } - /* Set local connection options, if present */ - if (hpars->local_options) { - rc = set_local_cx_options(trunk->endpoints, - &endp->local_options, hpars->local_options); - if (rc != 0) { - LOGPCONN(conn, DLMGCP, LOGL_ERROR, - "CRCX: invalid local connection options!\n"); - error_code = rc; - rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_INVALID_CONN_OPTIONS)); - goto error2; - } - } - /* Handle codec information and decide for a suitable codec */ rc = handle_codec_info(conn_rtp, rq); mgcp_codecset_summary(&conn_rtp->end.cset, mgcp_conn_dump(conn)); @@ -1120,6 +918,10 @@ return create_err_response(endp, endp, 400, "MDCX", pdata->trans); } + /* Set local connection options, if present */ + if (hpars->lco.present) + mgcp_endp_update_lco(endp, &hpars->lco); + mgcp_conn_watchdog_kick(conn); if (hpars->mode == MGCP_CONN_NONE) { @@ -1130,19 +932,6 @@ return create_err_response(endp, endp, 517, "MDCX", pdata->trans); } - /* Set local connection options, if present */ - if (hpars->local_options) { - rc = set_local_cx_options(trunk->endpoints, - &endp->local_options, hpars->local_options); - if (rc != 0) { - LOGPCONN(conn, DLMGCP, LOGL_ERROR, - "MDCX: invalid local connection options!\n"); - error_code = rc; - rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_INVALID_CONN_OPTIONS)); - goto error3; - } - } - conn_rtp = mgcp_conn_get_conn_rtp(conn); OSMO_ASSERT(conn_rtp); -- To view, visit https://gerrit.osmocom.org/c/osmo-mgw/+/39723?usp=email To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings?usp=email Gerrit-MessageType: newchange Gerrit-Project: osmo-mgw Gerrit-Branch: master Gerrit-Change-Id: If75731fb92329dca6d092ffc7ff17cb354d28c0d Gerrit-Change-Number: 39723 Gerrit-PatchSet: 1 Gerrit-Owner: pespin <pes...@sysmocom.de>