2016-05-20 19:59:55 +00:00
|
|
|
/* mgcp_utils - common functions to setup an MGCP connection
|
|
|
|
*/
|
|
|
|
/* (C) 2016 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
|
|
|
|
* All Rights Reserved
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
2018-02-04 08:04:16 +00:00
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation; either version 2 of the License, or
|
2016-05-20 19:59:55 +00:00
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
2018-02-04 08:04:16 +00:00
|
|
|
* GNU General Public License for more details.
|
2016-05-20 19:59:55 +00:00
|
|
|
*
|
2018-02-04 08:04:16 +00:00
|
|
|
* You should have received a copy of the GNU General Public License
|
2016-05-20 19:59:55 +00:00
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2017-07-10 13:07:22 +00:00
|
|
|
#include <osmocom/core/linuxlist.h>
|
2016-05-20 19:59:55 +00:00
|
|
|
#include <osmocom/core/select.h>
|
|
|
|
#include <osmocom/core/write_queue.h>
|
|
|
|
#include <osmocom/core/msgb.h>
|
|
|
|
#include <osmocom/core/logging.h>
|
2017-10-05 16:25:37 +00:00
|
|
|
#include <osmocom/core/byteswap.h>
|
2017-11-17 14:09:30 +00:00
|
|
|
#include <osmocom/core/socket.h>
|
2020-08-28 18:21:55 +00:00
|
|
|
#include <osmocom/core/sockaddr_str.h>
|
2016-05-20 19:59:55 +00:00
|
|
|
|
2017-09-03 23:02:56 +00:00
|
|
|
#include <osmocom/mgcp_client/mgcp_client.h>
|
|
|
|
#include <osmocom/mgcp_client/mgcp_client_internal.h>
|
2016-05-20 19:59:55 +00:00
|
|
|
|
2020-11-26 21:21:11 +00:00
|
|
|
#include <osmocom/abis/e1_input.h>
|
|
|
|
|
2016-05-20 19:59:55 +00:00
|
|
|
#include <netinet/in.h>
|
|
|
|
#include <arpa/inet.h>
|
|
|
|
|
|
|
|
#include <errno.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <string.h>
|
2019-06-26 10:21:38 +00:00
|
|
|
#include <ctype.h>
|
2019-07-23 14:19:41 +00:00
|
|
|
#include <stdlib.h>
|
|
|
|
#include <limits.h>
|
2016-05-20 19:59:55 +00:00
|
|
|
|
2019-05-23 14:48:24 +00:00
|
|
|
#ifndef OSMUX_CID_MAX
|
|
|
|
#define OSMUX_CID_MAX 255 /* FIXME: use OSMUX_CID_MAX from libosmo-netif? */
|
|
|
|
#endif
|
|
|
|
|
2021-09-03 15:50:51 +00:00
|
|
|
#define LOGPMGW(mgcp, level, fmt, args...) \
|
|
|
|
LOGP(DLMGCP, level, "MGW(%s) " fmt, mgcp_client_name(mgcp), ## args)
|
|
|
|
|
2018-06-07 16:51:31 +00:00
|
|
|
/* Codec descripton for dynamic payload types (SDP) */
|
2019-02-27 04:56:53 +00:00
|
|
|
const struct value_string osmo_mgcpc_codec_names[] = {
|
2018-06-07 16:51:31 +00:00
|
|
|
{ CODEC_PCMU_8000_1, "PCMU/8000/1" },
|
|
|
|
{ CODEC_GSM_8000_1, "GSM/8000/1" },
|
|
|
|
{ CODEC_PCMA_8000_1, "PCMA/8000/1" },
|
|
|
|
{ CODEC_G729_8000_1, "G729/8000/1" },
|
|
|
|
{ CODEC_GSMEFR_8000_1, "GSM-EFR/8000/1" },
|
|
|
|
{ CODEC_GSMHR_8000_1, "GSM-HR-08/8000/1" },
|
|
|
|
{ CODEC_AMR_8000_1, "AMR/8000/1" },
|
|
|
|
{ CODEC_AMRWB_16000_1, "AMR-WB/16000/1" },
|
2021-12-21 13:38:14 +00:00
|
|
|
{ CODEC_IUFP, "VND.3GPP.IUFP/16000" },
|
2023-01-24 12:04:47 +00:00
|
|
|
{ CODEC_CLEARMODE, "CLEARMODE/8000" },
|
2018-06-07 16:51:31 +00:00
|
|
|
{ 0, NULL },
|
|
|
|
};
|
|
|
|
|
|
|
|
/* Get encoding name from a full codec string e,g.
|
|
|
|
* ("CODEC/8000/2" => returns "CODEC") */
|
|
|
|
static char *extract_codec_name(const char *str)
|
|
|
|
{
|
|
|
|
static char buf[64];
|
2023-06-30 03:09:53 +00:00
|
|
|
char *pos;
|
2018-06-07 16:51:31 +00:00
|
|
|
|
|
|
|
if (!str)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
osmo_strlcpy(buf, str, sizeof(buf));
|
|
|
|
|
2023-06-30 03:09:53 +00:00
|
|
|
pos = strchr(buf, '/');
|
|
|
|
if (pos)
|
|
|
|
*pos = '\0';
|
2018-06-07 16:51:31 +00:00
|
|
|
|
|
|
|
return buf;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*! Map a string to a codec.
|
|
|
|
* \ptmap[in] str input string (e.g "GSM/8000/1", "GSM/8000" or "GSM")
|
|
|
|
* \returns codec that corresponds to the given string representation. */
|
|
|
|
enum mgcp_codecs map_str_to_codec(const char *str)
|
|
|
|
{
|
|
|
|
unsigned int i;
|
|
|
|
char *codec_name;
|
|
|
|
char str_buf[64];
|
|
|
|
|
|
|
|
osmo_strlcpy(str_buf, extract_codec_name(str), sizeof(str_buf));
|
|
|
|
|
2019-02-27 04:56:53 +00:00
|
|
|
for (i = 0; i < ARRAY_SIZE(osmo_mgcpc_codec_names); i++) {
|
|
|
|
codec_name = extract_codec_name(osmo_mgcpc_codec_names[i].str);
|
2018-06-07 16:51:31 +00:00
|
|
|
if (!codec_name)
|
|
|
|
continue;
|
|
|
|
if (strcmp(codec_name, str_buf) == 0)
|
2019-02-27 04:56:53 +00:00
|
|
|
return osmo_mgcpc_codec_names[i].value;
|
2018-06-07 16:51:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check the ptmap for illegal mappings */
|
2019-04-27 17:08:36 +00:00
|
|
|
static int check_ptmap(const struct ptmap *ptmap)
|
2018-06-07 16:51:31 +00:00
|
|
|
{
|
|
|
|
/* Check if there are mappings that leave the IANA assigned dynamic
|
|
|
|
* payload type range. Under normal conditions such mappings should
|
|
|
|
* not occur */
|
|
|
|
|
|
|
|
/* Its ok to have a 1:1 mapping in the statically defined
|
|
|
|
* range, this won't hurt */
|
|
|
|
if (ptmap->codec == ptmap->pt)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (ptmap->codec < 96 || ptmap->codec > 127)
|
|
|
|
goto error;
|
|
|
|
if (ptmap->pt < 96 || ptmap->pt > 127)
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
error:
|
|
|
|
LOGP(DLMGCP, LOGL_ERROR,
|
|
|
|
"ptmap contains illegal mapping: codec=%u maps to pt=%u\n",
|
|
|
|
ptmap->codec, ptmap->pt);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*! Map a codec to a payload type.
|
|
|
|
* \ptmap[in] payload pointer to payload type map with specified payload types.
|
|
|
|
* \ptmap[in] ptmap_len length of the payload type map.
|
|
|
|
* \ptmap[in] codec the codec for which the payload type should be looked up.
|
|
|
|
* \returns assigned payload type */
|
2019-04-27 17:08:36 +00:00
|
|
|
unsigned int map_codec_to_pt(const struct ptmap *ptmap, unsigned int ptmap_len,
|
2018-06-07 16:51:31 +00:00
|
|
|
enum mgcp_codecs codec)
|
|
|
|
{
|
|
|
|
unsigned int i;
|
|
|
|
|
|
|
|
/*! Note: If the payload type map is empty or the codec is not found
|
|
|
|
* in the map, then a 1:1 mapping is performed. If the codec falls
|
|
|
|
* into the statically defined range or if the mapping table isself
|
|
|
|
* tries to map to the statically defined range, then the mapping
|
|
|
|
* is also ignored and a 1:1 mapping is performed instead. */
|
|
|
|
|
|
|
|
/* we may return the codec directly since enum mgcp_codecs directly
|
2023-06-28 08:30:25 +00:00
|
|
|
* corresponds to the statically assigned payload types */
|
2018-06-07 16:51:31 +00:00
|
|
|
if (codec < 96 || codec > 127)
|
|
|
|
return codec;
|
|
|
|
|
|
|
|
for (i = 0; i < ptmap_len; i++) {
|
|
|
|
/* Skip illegal map entries */
|
|
|
|
if (check_ptmap(ptmap) == 0 && ptmap->codec == codec)
|
|
|
|
return ptmap->pt;
|
|
|
|
ptmap++;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* If nothing is found, do not perform any mapping */
|
|
|
|
return codec;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*! Map a payload type to a codec.
|
|
|
|
* \ptmap[in] payload pointer to payload type map with specified payload types.
|
|
|
|
* \ptmap[in] ptmap_len length of the payload type map.
|
|
|
|
* \ptmap[in] payload type for which the codec should be looked up.
|
|
|
|
* \returns codec that corresponds to the specified payload type */
|
|
|
|
enum mgcp_codecs map_pt_to_codec(struct ptmap *ptmap, unsigned int ptmap_len,
|
|
|
|
unsigned int pt)
|
|
|
|
{
|
|
|
|
unsigned int i;
|
|
|
|
|
|
|
|
/*! Note: If the payload type map is empty or the payload type is not
|
|
|
|
* found in the map, then a 1:1 mapping is performed. If the payload
|
|
|
|
* type falls into the statically defined range or if the mapping
|
|
|
|
* table isself tries to map to the statically defined range, then
|
|
|
|
* the mapping is also ignored and a 1:1 mapping is performed
|
|
|
|
* instead. */
|
|
|
|
|
|
|
|
/* See also note in map_codec_to_pt() */
|
|
|
|
if (pt < 96 || pt > 127)
|
|
|
|
return pt;
|
|
|
|
|
|
|
|
for (i = 0; i < ptmap_len; i++) {
|
|
|
|
if (check_ptmap(ptmap) == 0 && ptmap->pt == pt)
|
|
|
|
return ptmap->codec;
|
|
|
|
ptmap++;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* If nothing is found, do not perform any mapping */
|
|
|
|
return pt;
|
|
|
|
}
|
|
|
|
|
mgcp_client: Introduce mgcp_client_conf_alloc(), deprecate mgcp_client_conf_init()
So far, the users of the old non-pooled API were in charge of allocating
the struct mgcp_client_conf by themselves, then init them using
mgcp_client_conf_init(). This causes a major problem, since it makes it
difficult to extend the struct mgcp_client_conf structure to add new
features, which may happen frequently.
The MGW pool API doesn't have this problem, because the struct
mgcp_client_conf is allocated as parts/fields of private structs defined
in internal headers. Only pointers to it are used in public headers.
Since it still has to internally initialize the conf fields, we still
need the API to initialize it internally, and hence why is it marked as
DEPRECTED_OUTSIDE instead of DEPRECATED.
While some programs already moved to the new MGW pool infrastructure,
they still use the old APIs to accomodate for old config files in order
to be back-compatible, hence most users of libosmo-mgcp-client are
affected.
Introduce an API to allocate the conf struct internally, which, while
still breaking the ABI, allows for a more relaxed update path where it's
possible to extend the struct mgcp_client_conf at the end.
Eventually the non pooled API should be gone and the struct
mgcp_client_conf can then be moved to a private header, but for now
let's add this small feature to avoid major ABI breakage.
Change-Id: Iba0853ed099a32cf1dde78c17e1b34343db41cfc
2023-06-13 17:41:44 +00:00
|
|
|
static void _mgcp_client_conf_init(struct mgcp_client_conf *conf)
|
2016-05-20 19:59:55 +00:00
|
|
|
{
|
2017-09-03 23:02:56 +00:00
|
|
|
/* NULL and -1 default to MGCP_CLIENT_*_DEFAULT values */
|
|
|
|
*conf = (struct mgcp_client_conf){
|
2016-05-20 19:59:55 +00:00
|
|
|
.local_addr = NULL,
|
|
|
|
.local_port = -1,
|
|
|
|
.remote_addr = NULL,
|
|
|
|
.remote_port = -1,
|
2023-06-14 10:20:49 +00:00
|
|
|
.keepalive = {
|
|
|
|
.timeout_sec = 0, /* disabled */
|
|
|
|
.req_interval_sec = 0, /* disabled */
|
|
|
|
.req_endpoint_name = MGCP_CLIENT_KEEPALIVE_DEFAULT_ENDP,
|
|
|
|
},
|
2016-05-20 19:59:55 +00:00
|
|
|
};
|
2021-07-22 09:53:07 +00:00
|
|
|
|
|
|
|
INIT_LLIST_HEAD(&conf->reset_epnames);
|
2016-05-20 19:59:55 +00:00
|
|
|
}
|
|
|
|
|
mgcp_client: Introduce mgcp_client_conf_alloc(), deprecate mgcp_client_conf_init()
So far, the users of the old non-pooled API were in charge of allocating
the struct mgcp_client_conf by themselves, then init them using
mgcp_client_conf_init(). This causes a major problem, since it makes it
difficult to extend the struct mgcp_client_conf structure to add new
features, which may happen frequently.
The MGW pool API doesn't have this problem, because the struct
mgcp_client_conf is allocated as parts/fields of private structs defined
in internal headers. Only pointers to it are used in public headers.
Since it still has to internally initialize the conf fields, we still
need the API to initialize it internally, and hence why is it marked as
DEPRECTED_OUTSIDE instead of DEPRECATED.
While some programs already moved to the new MGW pool infrastructure,
they still use the old APIs to accomodate for old config files in order
to be back-compatible, hence most users of libosmo-mgcp-client are
affected.
Introduce an API to allocate the conf struct internally, which, while
still breaking the ABI, allows for a more relaxed update path where it's
possible to extend the struct mgcp_client_conf at the end.
Eventually the non pooled API should be gone and the struct
mgcp_client_conf can then be moved to a private header, but for now
let's add this small feature to avoid major ABI breakage.
Change-Id: Iba0853ed099a32cf1dde78c17e1b34343db41cfc
2023-06-13 17:41:44 +00:00
|
|
|
/*! Allocate and initialize MGCP client configuration struct with default values.
|
|
|
|
* \param[in] ctx talloc context to use as a parent during allocation.
|
|
|
|
*
|
|
|
|
* The returned struct can be freed using talloc_free().
|
|
|
|
*/
|
|
|
|
struct mgcp_client_conf *mgcp_client_conf_alloc(void *ctx)
|
|
|
|
{
|
|
|
|
struct mgcp_client_conf *conf = talloc(ctx, struct mgcp_client_conf);
|
|
|
|
_mgcp_client_conf_init(conf);
|
|
|
|
return conf;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*! Initialize MGCP client configuration struct with default values.
|
|
|
|
* \param[out] conf Client configuration.
|
|
|
|
*
|
|
|
|
* This function is deprecated and should not be used, as it may break if size
|
|
|
|
* of struct mgcp_client_conf changes in the future!
|
|
|
|
*/
|
|
|
|
void mgcp_client_conf_init(struct mgcp_client_conf *conf)
|
|
|
|
{
|
|
|
|
_mgcp_client_conf_init(conf);
|
|
|
|
}
|
|
|
|
|
2017-09-03 23:02:56 +00:00
|
|
|
static void mgcp_client_handle_response(struct mgcp_client *mgcp,
|
|
|
|
struct mgcp_response_pending *pending,
|
|
|
|
struct mgcp_response *response)
|
2016-05-20 19:59:55 +00:00
|
|
|
{
|
|
|
|
if (!pending) {
|
2021-09-03 15:50:51 +00:00
|
|
|
LOGPMGW(mgcp, LOGL_ERROR, "Cannot handle NULL response\n");
|
2016-05-20 19:59:55 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (pending->response_cb)
|
|
|
|
pending->response_cb(response, pending->priv);
|
|
|
|
else
|
2021-09-03 15:50:51 +00:00
|
|
|
LOGPMGW(mgcp, LOGL_DEBUG, "MGCP response ignored (NULL cb)\n");
|
2016-05-20 19:59:55 +00:00
|
|
|
talloc_free(pending);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int mgcp_response_parse_head(struct mgcp_response *r, struct msgb *msg)
|
|
|
|
{
|
|
|
|
int comment_pos;
|
|
|
|
char *end;
|
|
|
|
|
|
|
|
if (mgcp_msg_terminate_nul(msg))
|
|
|
|
goto response_parse_failure;
|
|
|
|
|
|
|
|
r->body = (char *)msg->data;
|
|
|
|
|
2017-11-17 13:14:31 +00:00
|
|
|
if (sscanf(r->body, "%3d %u %n",
|
2016-05-20 19:59:55 +00:00
|
|
|
&r->head.response_code, &r->head.trans_id,
|
|
|
|
&comment_pos) != 2)
|
|
|
|
goto response_parse_failure;
|
|
|
|
|
2018-01-19 23:15:12 +00:00
|
|
|
osmo_strlcpy(r->head.comment, r->body + comment_pos, sizeof(r->head.comment));
|
2016-05-20 19:59:55 +00:00
|
|
|
end = strchr(r->head.comment, '\r');
|
|
|
|
if (!end)
|
|
|
|
goto response_parse_failure;
|
|
|
|
/* Mark the end of the comment */
|
|
|
|
*end = '\0';
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
response_parse_failure:
|
2017-07-10 13:07:22 +00:00
|
|
|
LOGP(DLMGCP, LOGL_ERROR,
|
2016-05-20 19:59:55 +00:00
|
|
|
"Failed to parse MGCP response header\n");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* TODO undup against mgcp_protocol.c:mgcp_check_param() */
|
|
|
|
static bool mgcp_line_is_valid(const char *line)
|
|
|
|
{
|
|
|
|
const size_t line_len = strlen(line);
|
|
|
|
if (line[0] == '\0')
|
|
|
|
return true;
|
|
|
|
|
|
|
|
if (line_len < 2
|
|
|
|
|| line[1] != '=') {
|
2017-07-10 13:07:22 +00:00
|
|
|
LOGP(DLMGCP, LOGL_ERROR,
|
2016-05-20 19:59:55 +00:00
|
|
|
"Wrong MGCP option format: '%s'\n",
|
|
|
|
line);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2018-06-07 16:51:31 +00:00
|
|
|
/* Parse a line like "m=audio 16002 RTP/AVP 98", extract port and payload types */
|
|
|
|
static int mgcp_parse_audio_port_pt(struct mgcp_response *r, char *line)
|
2016-05-20 19:59:55 +00:00
|
|
|
{
|
2018-06-07 16:51:31 +00:00
|
|
|
char *pt_str;
|
2019-07-23 14:19:41 +00:00
|
|
|
char *pt_end;
|
2019-07-26 12:13:14 +00:00
|
|
|
unsigned long int pt;
|
2018-06-07 16:51:31 +00:00
|
|
|
unsigned int count = 0;
|
|
|
|
unsigned int i;
|
2016-05-20 19:59:55 +00:00
|
|
|
|
2018-06-07 16:51:31 +00:00
|
|
|
/* Extract port information */
|
|
|
|
if (sscanf(line, "m=audio %hu", &r->audio_port) != 1)
|
|
|
|
goto response_parse_failure_port;
|
2017-12-13 11:34:34 +00:00
|
|
|
if (r->audio_port == 0)
|
2018-06-07 16:51:31 +00:00
|
|
|
goto response_parse_failure_port;
|
|
|
|
|
|
|
|
/* Extract payload types */
|
|
|
|
line = strstr(line, "RTP/AVP ");
|
|
|
|
if (!line)
|
|
|
|
goto exit;
|
|
|
|
|
|
|
|
pt_str = strtok(line, " ");
|
|
|
|
while (1) {
|
|
|
|
/* Do not allow excessive payload types */
|
2023-10-25 19:53:47 +00:00
|
|
|
if (count >= ARRAY_SIZE(r->codecs))
|
2018-06-07 16:51:31 +00:00
|
|
|
goto response_parse_failure_pt;
|
2017-12-13 11:34:34 +00:00
|
|
|
|
2018-06-07 16:51:31 +00:00
|
|
|
pt_str = strtok(NULL, " ");
|
|
|
|
if (!pt_str)
|
|
|
|
break;
|
2019-07-23 14:19:41 +00:00
|
|
|
errno = 0;
|
|
|
|
pt = strtoul(pt_str, &pt_end, 0);
|
|
|
|
if ((errno == ERANGE && pt == ULONG_MAX) || (errno && !pt) ||
|
|
|
|
pt_str == pt_end)
|
|
|
|
goto response_parse_failure_pt;
|
2018-06-07 16:51:31 +00:00
|
|
|
|
2019-07-26 12:13:14 +00:00
|
|
|
if (pt >> 7) /* PT is 7 bit field, higher values not allowed */
|
|
|
|
goto response_parse_failure_pt;
|
|
|
|
|
2018-06-07 16:51:31 +00:00
|
|
|
/* Do not allow duplicate payload types */
|
|
|
|
for (i = 0; i < count; i++)
|
|
|
|
if (r->codecs[i] == pt)
|
|
|
|
goto response_parse_failure_pt;
|
|
|
|
|
|
|
|
/* Note: The payload type we store may not necessarly match
|
|
|
|
* the codec types we have defined in enum mgcp_codecs. To
|
|
|
|
* ensure that the end result only contains codec types which
|
|
|
|
* match enum mgcp_codecs, we will go through afterwards and
|
|
|
|
* remap the affected entries with the inrofmation we learn
|
|
|
|
* from rtpmap */
|
|
|
|
r->codecs[count] = pt;
|
|
|
|
count++;
|
|
|
|
}
|
|
|
|
|
|
|
|
r->codecs_len = count;
|
|
|
|
|
|
|
|
exit:
|
2016-05-20 19:59:55 +00:00
|
|
|
return 0;
|
|
|
|
|
2018-06-07 16:51:31 +00:00
|
|
|
response_parse_failure_port:
|
|
|
|
LOGP(DLMGCP, LOGL_ERROR,
|
|
|
|
"Failed to parse SDP parameter port (%s)\n", line);
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
response_parse_failure_pt:
|
2017-07-10 13:07:22 +00:00
|
|
|
LOGP(DLMGCP, LOGL_ERROR,
|
2018-06-07 16:51:31 +00:00
|
|
|
"Failed to parse SDP parameter payload types (%s)\n", line);
|
2017-10-05 16:49:24 +00:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2018-06-07 16:51:31 +00:00
|
|
|
/* Parse a line like "m=audio 16002 RTP/AVP 98", extract port and payload types */
|
|
|
|
static int mgcp_parse_audio_ptime_rtpmap(struct mgcp_response *r, const char *line)
|
|
|
|
{
|
|
|
|
unsigned int pt;
|
|
|
|
char codec_resp[64];
|
2023-06-28 11:28:57 +00:00
|
|
|
int rc;
|
2019-04-16 15:23:09 +00:00
|
|
|
|
2019-08-08 02:03:42 +00:00
|
|
|
#define A_PTIME "a=ptime:"
|
|
|
|
#define A_RTPMAP "a=rtpmap:"
|
|
|
|
|
|
|
|
if (osmo_str_startswith(line, A_PTIME)) {
|
|
|
|
if (sscanf(line, A_PTIME "%u", &r->ptime) != 1) {
|
|
|
|
LOGP(DLMGCP, LOGL_ERROR,
|
|
|
|
"Failed to parse SDP parameter, invalid ptime (%s)\n", line);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
} else if (osmo_str_startswith(line, A_RTPMAP)) {
|
|
|
|
if (sscanf(line, A_RTPMAP "%d %63s", &pt, codec_resp) != 2) {
|
|
|
|
LOGP(DLMGCP, LOGL_ERROR,
|
|
|
|
"Failed to parse SDP parameter, invalid rtpmap: %s\n", osmo_quote_str(line, -1));
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
2019-08-08 02:03:42 +00:00
|
|
|
if (r->ptmap_len >= ARRAY_SIZE(r->ptmap)) {
|
|
|
|
LOGP(DLMGCP, LOGL_ERROR, "No more space in ptmap array (len=%u)\n", r->ptmap_len);
|
|
|
|
return -ENOSPC;
|
2019-08-08 02:03:42 +00:00
|
|
|
}
|
2023-06-28 11:28:57 +00:00
|
|
|
rc = map_str_to_codec(codec_resp);
|
|
|
|
if (rc < 0) {
|
|
|
|
LOGP(DLMGCP, LOGL_ERROR,
|
|
|
|
"Failed to parse SDP parameter, can't parse codec in rtpmap: %s\n", osmo_quote_str(line, -1));
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
2019-08-08 02:03:42 +00:00
|
|
|
r->ptmap[r->ptmap_len].pt = pt;
|
2023-06-28 11:28:57 +00:00
|
|
|
r->ptmap[r->ptmap_len].codec = rc;
|
2019-08-08 02:03:42 +00:00
|
|
|
r->ptmap_len++;
|
2018-06-07 16:51:31 +00:00
|
|
|
}
|
2019-04-16 15:23:09 +00:00
|
|
|
|
2018-06-07 16:51:31 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-10-05 16:49:24 +00:00
|
|
|
/* Parse a line like "c=IN IP4 10.11.12.13" */
|
|
|
|
static int mgcp_parse_audio_ip(struct mgcp_response *r, const char *line)
|
|
|
|
{
|
2020-08-28 18:21:55 +00:00
|
|
|
struct in6_addr ip_test;
|
|
|
|
bool is_ipv6;
|
2017-10-05 16:49:24 +00:00
|
|
|
|
2020-08-28 18:21:55 +00:00
|
|
|
if (strncmp("c=IN IP", line, 7) != 0)
|
2017-10-05 16:49:24 +00:00
|
|
|
goto response_parse_failure;
|
2020-08-28 18:21:55 +00:00
|
|
|
line += 7;
|
|
|
|
if (*line == '6')
|
|
|
|
is_ipv6 = true;
|
|
|
|
else if (*line == '4')
|
|
|
|
is_ipv6 = false;
|
|
|
|
else
|
2017-10-05 16:49:24 +00:00
|
|
|
goto response_parse_failure;
|
2020-08-28 18:21:55 +00:00
|
|
|
line++;
|
|
|
|
if (*line != ' ')
|
2017-10-05 16:49:24 +00:00
|
|
|
goto response_parse_failure;
|
2020-08-28 18:21:55 +00:00
|
|
|
line++;
|
|
|
|
|
|
|
|
/* Extract and check IP-Address */
|
|
|
|
if (is_ipv6) {
|
|
|
|
/* 45 = INET6_ADDRSTRLEN -1 */
|
|
|
|
if (sscanf(line, "%45s", r->audio_ip) != 1)
|
|
|
|
goto response_parse_failure;
|
|
|
|
if (inet_pton(AF_INET6, r->audio_ip, &ip_test) != 1)
|
|
|
|
goto response_parse_failure;
|
|
|
|
} else {
|
|
|
|
/* 15 = INET_ADDRSTRLEN -1 */
|
|
|
|
if (sscanf(line, "%15s", r->audio_ip) != 1)
|
|
|
|
goto response_parse_failure;
|
|
|
|
if (inet_pton(AF_INET, r->audio_ip, &ip_test) != 1)
|
|
|
|
goto response_parse_failure;
|
|
|
|
}
|
2017-10-05 16:49:24 +00:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
response_parse_failure:
|
|
|
|
LOGP(DLMGCP, LOGL_ERROR,
|
|
|
|
"Failed to parse MGCP response header (audio ip)\n");
|
2016-05-20 19:59:55 +00:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2019-04-24 19:02:40 +00:00
|
|
|
/*! Extract OSMUX CID from an MGCP parameter line (string).
|
|
|
|
* \param[in] line single parameter line from the MGCP message
|
|
|
|
* \returns OSMUX CID, -1 wildcard, -2 on error
|
|
|
|
* FIXME: This is a copy of function in mgcp_msg.c. Have some common.c file between both libs?
|
|
|
|
*/
|
|
|
|
static int mgcp_parse_osmux_cid(const char *line)
|
|
|
|
{
|
|
|
|
int osmux_cid;
|
|
|
|
|
|
|
|
|
2019-05-14 14:23:24 +00:00
|
|
|
if (strcasecmp(line + 2, "Osmux: *") == 0) {
|
2019-04-24 19:02:40 +00:00
|
|
|
LOGP(DLMGCP, LOGL_DEBUG, "Parsed wilcard Osmux CID\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2019-05-14 14:23:24 +00:00
|
|
|
if (sscanf(line + 2 + 7, "%u", &osmux_cid) != 1) {
|
2019-04-24 19:02:40 +00:00
|
|
|
LOGP(DLMGCP, LOGL_ERROR, "Failed parsing Osmux in MGCP msg line: %s\n",
|
|
|
|
line);
|
|
|
|
return -2;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (osmux_cid > OSMUX_CID_MAX) { /* OSMUX_CID_MAX from libosmo-netif */
|
|
|
|
LOGP(DLMGCP, LOGL_ERROR, "Osmux ID too large: %u > %u\n",
|
|
|
|
osmux_cid, OSMUX_CID_MAX);
|
|
|
|
return -2;
|
|
|
|
}
|
2022-10-06 16:24:21 +00:00
|
|
|
LOGP(DLMGCP, LOGL_DEBUG, "MGW offered Osmux CID %u\n", osmux_cid);
|
2019-04-24 19:02:40 +00:00
|
|
|
|
|
|
|
return osmux_cid;
|
|
|
|
}
|
|
|
|
|
2018-01-18 14:16:13 +00:00
|
|
|
/* A new section is marked by a double line break, check a few more
|
|
|
|
* patterns as there may be variants */
|
|
|
|
static char *mgcp_find_section_end(char *string)
|
|
|
|
{
|
|
|
|
char *rc;
|
|
|
|
|
|
|
|
rc = strstr(string, "\n\n");
|
|
|
|
if (rc)
|
2023-03-30 14:51:40 +00:00
|
|
|
return rc + 2;
|
2018-01-18 14:16:13 +00:00
|
|
|
|
|
|
|
rc = strstr(string, "\n\r\n\r");
|
|
|
|
if (rc)
|
2023-03-30 14:51:40 +00:00
|
|
|
return rc + 4;
|
2018-01-18 14:16:13 +00:00
|
|
|
|
|
|
|
rc = strstr(string, "\r\n\r\n");
|
|
|
|
if (rc)
|
2023-03-30 14:51:40 +00:00
|
|
|
return rc + 4;
|
2018-01-18 14:16:13 +00:00
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2018-01-19 23:58:16 +00:00
|
|
|
/*! Parse body (SDP) parameters of the MGCP response
|
|
|
|
* \param[in,out] r Response data
|
|
|
|
* \returns 0 on success, -EINVAL on error. */
|
2016-05-20 19:59:55 +00:00
|
|
|
int mgcp_response_parse_params(struct mgcp_response *r)
|
|
|
|
{
|
|
|
|
char *line;
|
|
|
|
int rc;
|
2018-02-21 13:55:34 +00:00
|
|
|
char *data;
|
2018-01-19 22:54:08 +00:00
|
|
|
char *data_ptr;
|
2018-06-07 16:51:31 +00:00
|
|
|
int i;
|
2016-05-20 19:59:55 +00:00
|
|
|
|
2018-01-19 22:54:08 +00:00
|
|
|
/* Since this functions performs a destructive parsing, we create a
|
|
|
|
* local copy of the body data */
|
2018-02-21 13:55:34 +00:00
|
|
|
OSMO_ASSERT(r->body);
|
|
|
|
data = talloc_strdup(r, r->body);
|
2018-01-19 22:54:08 +00:00
|
|
|
OSMO_ASSERT(data);
|
2018-01-15 13:00:28 +00:00
|
|
|
|
2018-01-19 22:54:08 +00:00
|
|
|
/* Find beginning of the parameter (SDP) section */
|
|
|
|
data_ptr = mgcp_find_section_end(data);
|
2018-02-21 13:55:34 +00:00
|
|
|
if (!data_ptr) {
|
2019-09-19 01:06:46 +00:00
|
|
|
LOGP(DLMGCP, LOGL_DEBUG, "MGCP response contains no SDP parameters\n");
|
|
|
|
rc = 0;
|
2018-01-19 22:54:08 +00:00
|
|
|
goto exit;
|
2016-05-20 19:59:55 +00:00
|
|
|
}
|
|
|
|
|
2018-02-21 13:55:34 +00:00
|
|
|
/* data_ptr now points to the beginning of the section-end-marker; for_each_non_empty_line()
|
|
|
|
* skips any \r and \n characters for free, so we don't need to skip the marker. */
|
|
|
|
|
2018-01-19 22:54:08 +00:00
|
|
|
for_each_non_empty_line(line, data_ptr) {
|
2016-05-20 19:59:55 +00:00
|
|
|
if (!mgcp_line_is_valid(line))
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
switch (line[0]) {
|
2018-06-07 16:51:31 +00:00
|
|
|
case 'a':
|
|
|
|
rc = mgcp_parse_audio_ptime_rtpmap(r, line);
|
|
|
|
if (rc)
|
|
|
|
goto exit;
|
|
|
|
break;
|
2016-05-20 19:59:55 +00:00
|
|
|
case 'm':
|
2018-06-07 16:51:31 +00:00
|
|
|
rc = mgcp_parse_audio_port_pt(r, line);
|
2017-10-05 16:49:24 +00:00
|
|
|
if (rc)
|
2018-01-19 22:54:08 +00:00
|
|
|
goto exit;
|
2017-10-05 16:49:24 +00:00
|
|
|
break;
|
|
|
|
case 'c':
|
|
|
|
rc = mgcp_parse_audio_ip(r, line);
|
2016-05-20 19:59:55 +00:00
|
|
|
if (rc)
|
2018-01-19 22:54:08 +00:00
|
|
|
goto exit;
|
2016-05-20 19:59:55 +00:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
/* skip unhandled parameters */
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2018-01-19 22:54:08 +00:00
|
|
|
|
2018-06-07 16:51:31 +00:00
|
|
|
/* See also note in mgcp_parse_audio_port_pt() */
|
|
|
|
for (i = 0; i < r->codecs_len; i++)
|
|
|
|
r->codecs[i] = map_pt_to_codec(r->ptmap, r->ptmap_len, r->codecs[i]);
|
|
|
|
|
2018-01-19 22:54:08 +00:00
|
|
|
rc = 0;
|
|
|
|
exit:
|
|
|
|
talloc_free(data);
|
|
|
|
return rc;
|
2016-05-20 19:59:55 +00:00
|
|
|
}
|
|
|
|
|
2018-01-15 13:00:28 +00:00
|
|
|
/* Parse a line like "X: something" */
|
|
|
|
static int mgcp_parse_head_param(char *result, unsigned int result_len,
|
|
|
|
char label, const char *line)
|
2017-11-22 10:44:50 +00:00
|
|
|
{
|
2018-01-15 13:00:28 +00:00
|
|
|
char label_string[4];
|
2018-09-03 19:26:22 +00:00
|
|
|
size_t rc;
|
2018-01-15 13:00:28 +00:00
|
|
|
|
|
|
|
/* Detect empty parameters */
|
2017-11-22 10:44:50 +00:00
|
|
|
if (strlen(line) < 4)
|
|
|
|
goto response_parse_failure;
|
|
|
|
|
2018-01-15 13:00:28 +00:00
|
|
|
/* Check if the label matches */
|
|
|
|
snprintf(label_string, sizeof(label_string), "%c: ", label);
|
|
|
|
if (memcmp(label_string, line, 3) != 0)
|
2017-11-22 10:44:50 +00:00
|
|
|
goto response_parse_failure;
|
|
|
|
|
2018-01-15 13:00:28 +00:00
|
|
|
/* Copy payload part of the string to destinations (the label string
|
|
|
|
* is always 3 chars long) */
|
2018-09-03 19:26:22 +00:00
|
|
|
rc = osmo_strlcpy(result, line + 3, result_len);
|
|
|
|
if (rc >= result_len) {
|
|
|
|
LOGP(DLMGCP, LOGL_ERROR,
|
|
|
|
"Failed to parse MGCP response (parameter label: %c):"
|
|
|
|
" the received conn ID is too long: %zu, maximum is %u characters\n",
|
|
|
|
label, rc, result_len - 1);
|
|
|
|
return -ENOSPC;
|
|
|
|
}
|
2017-11-22 10:44:50 +00:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
response_parse_failure:
|
|
|
|
LOGP(DLMGCP, LOGL_ERROR,
|
2018-01-15 13:00:28 +00:00
|
|
|
"Failed to parse MGCP response (parameter label: %c)\n", label);
|
2017-11-22 10:44:50 +00:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Parse MGCP parameters of the response */
|
|
|
|
static int parse_head_params(struct mgcp_response *r)
|
|
|
|
{
|
|
|
|
char *line;
|
|
|
|
int rc = 0;
|
|
|
|
OSMO_ASSERT(r->body);
|
2018-01-15 13:00:28 +00:00
|
|
|
char *data;
|
|
|
|
char *data_ptr;
|
|
|
|
char *data_end;
|
|
|
|
|
|
|
|
/* Since this functions performs a destructive parsing, we create a
|
|
|
|
* local copy of the body data */
|
2018-01-31 14:32:55 +00:00
|
|
|
data = talloc_zero_size(r, strlen(r->body)+1);
|
2018-01-15 13:00:28 +00:00
|
|
|
OSMO_ASSERT(data);
|
|
|
|
data_ptr = data;
|
|
|
|
osmo_strlcpy(data, r->body, strlen(r->body));
|
|
|
|
|
|
|
|
/* If there is an SDP body attached, prevent for_each_non_empty_line()
|
|
|
|
* into running in there, we are not yet interested in the parameters
|
|
|
|
* stored there. */
|
2019-04-16 15:23:09 +00:00
|
|
|
data_end = mgcp_find_section_end(data);
|
2017-11-22 10:44:50 +00:00
|
|
|
if (data_end)
|
|
|
|
*data_end = '\0';
|
|
|
|
|
2018-01-15 13:00:28 +00:00
|
|
|
for_each_non_empty_line(line, data_ptr) {
|
2019-06-26 10:21:38 +00:00
|
|
|
switch (toupper(line[0])) {
|
2018-01-15 13:00:28 +00:00
|
|
|
case 'Z':
|
|
|
|
rc = mgcp_parse_head_param(r->head.endpoint,
|
|
|
|
sizeof(r->head.endpoint),
|
|
|
|
'Z', line);
|
|
|
|
if (rc)
|
|
|
|
goto exit;
|
2018-01-31 12:53:11 +00:00
|
|
|
|
|
|
|
/* A specific endpoint identifier returned by the MGW
|
|
|
|
* must not contain any wildcard characters */
|
|
|
|
if (strstr(r->head.endpoint, "*") != NULL) {
|
|
|
|
rc = -EINVAL;
|
|
|
|
goto exit;
|
|
|
|
}
|
2018-01-31 13:03:13 +00:00
|
|
|
|
|
|
|
/* A specific endpoint identifier returned by the MGW
|
|
|
|
* must contain an @ character */
|
|
|
|
if (strstr(r->head.endpoint, "@") == NULL) {
|
|
|
|
rc = -EINVAL;
|
|
|
|
goto exit;
|
|
|
|
}
|
2018-01-15 13:00:28 +00:00
|
|
|
break;
|
2017-11-22 10:44:50 +00:00
|
|
|
case 'I':
|
2018-01-15 13:00:28 +00:00
|
|
|
rc = mgcp_parse_head_param(r->head.conn_id,
|
|
|
|
sizeof(r->head.conn_id),
|
|
|
|
'I', line);
|
2017-11-22 10:44:50 +00:00
|
|
|
if (rc)
|
|
|
|
goto exit;
|
|
|
|
break;
|
2019-04-24 19:02:40 +00:00
|
|
|
case 'X':
|
2019-05-14 14:23:24 +00:00
|
|
|
if (strncasecmp("Osmux: ", line + 2, strlen("Osmux: ")) == 0) {
|
2019-04-24 19:02:40 +00:00
|
|
|
rc = mgcp_parse_osmux_cid(line);
|
|
|
|
if (rc < 0) {
|
|
|
|
/* -1: we don't want wildcards in response. -2: error */
|
|
|
|
rc = -EINVAL;
|
|
|
|
goto exit;
|
|
|
|
}
|
|
|
|
r->head.x_osmo_osmux_use = true;
|
|
|
|
r->head.x_osmo_osmux_cid = (uint8_t) rc;
|
|
|
|
rc = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
/* Ignore unknown X-headers */
|
|
|
|
break;
|
2017-11-22 10:44:50 +00:00
|
|
|
default:
|
|
|
|
/* skip unhandled parameters */
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
exit:
|
2018-01-15 13:00:28 +00:00
|
|
|
talloc_free(data);
|
2017-11-22 10:44:50 +00:00
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2017-09-03 23:02:56 +00:00
|
|
|
static struct mgcp_response_pending *mgcp_client_response_pending_get(
|
|
|
|
struct mgcp_client *mgcp,
|
mgcp_client: add transaction cleanup
So far, if an MGCP message is sent, the transaction gets enqueued, but there is
no way to end the transaction other than receiving a valid reply. So, if the
caller decides that the transaction timed out and tears down the priv pointer
passed to mgcp_client_tx, and if then a late reply arrives, the callback will
dereference the invalid priv pointer and cause a segfault. Hence it is possible
to crash an mgcp_client program by sending a late response.
Furthermore, if no reply ever arrives, we would keep the pending response in
the list forever, amounting to a "memory leak".
Add mgcp_client_cancel() to discard a pending transaction. The caller can now
decide to discard a pending response when it sees fit (e.g. the caller's
timeout expired). This needs to be added to OsmoMSC and OsmoBSC.
Add mgcp_msg_trans_id() to provide an obvious way to obtain the transaction id
from a generated MGCP message.
No public API is broken; but refine the negative return code from
mgcp_client_rx(): return -ENOENT if no such transaction ID is found, and still
-1 if decoding failed. This is mainly for mgcp_client_test.
Implement a test for mgcp_client_cancel() in mgcp_client_test.c.
Tweak internal mgcp_client_response_pending_get() to take only the transaction
id as argument instead of the entire mgcp message struct.
Found-by: dexter
Related: OS#2695 OS#2696
Change-Id: I16811e168a46a82a05943252a737b3434143f4bd
2017-11-30 12:43:11 +00:00
|
|
|
mgcp_trans_id_t trans_id)
|
2016-05-20 19:59:55 +00:00
|
|
|
{
|
|
|
|
struct mgcp_response_pending *pending;
|
|
|
|
llist_for_each_entry(pending, &mgcp->responses_pending, entry) {
|
mgcp_client: add transaction cleanup
So far, if an MGCP message is sent, the transaction gets enqueued, but there is
no way to end the transaction other than receiving a valid reply. So, if the
caller decides that the transaction timed out and tears down the priv pointer
passed to mgcp_client_tx, and if then a late reply arrives, the callback will
dereference the invalid priv pointer and cause a segfault. Hence it is possible
to crash an mgcp_client program by sending a late response.
Furthermore, if no reply ever arrives, we would keep the pending response in
the list forever, amounting to a "memory leak".
Add mgcp_client_cancel() to discard a pending transaction. The caller can now
decide to discard a pending response when it sees fit (e.g. the caller's
timeout expired). This needs to be added to OsmoMSC and OsmoBSC.
Add mgcp_msg_trans_id() to provide an obvious way to obtain the transaction id
from a generated MGCP message.
No public API is broken; but refine the negative return code from
mgcp_client_rx(): return -ENOENT if no such transaction ID is found, and still
-1 if decoding failed. This is mainly for mgcp_client_test.
Implement a test for mgcp_client_cancel() in mgcp_client_test.c.
Tweak internal mgcp_client_response_pending_get() to take only the transaction
id as argument instead of the entire mgcp message struct.
Found-by: dexter
Related: OS#2695 OS#2696
Change-Id: I16811e168a46a82a05943252a737b3434143f4bd
2017-11-30 12:43:11 +00:00
|
|
|
if (pending->trans_id == trans_id) {
|
2016-05-20 19:59:55 +00:00
|
|
|
llist_del(&pending->entry);
|
|
|
|
return pending;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Feed an MGCP message into the receive processing.
|
|
|
|
* Parse the head and call any callback registered for the transaction id found
|
|
|
|
* in the MGCP message. This is normally called directly from the internal
|
|
|
|
* mgcp_do_read that reads from the socket connected to the MGCP gateway. This
|
|
|
|
* function is published mainly to be able to feed data from the test suite.
|
|
|
|
*/
|
2017-09-03 23:02:56 +00:00
|
|
|
int mgcp_client_rx(struct mgcp_client *mgcp, struct msgb *msg)
|
2016-05-20 19:59:55 +00:00
|
|
|
{
|
2018-01-31 14:32:55 +00:00
|
|
|
struct mgcp_response *r;
|
2016-05-20 19:59:55 +00:00
|
|
|
struct mgcp_response_pending *pending;
|
|
|
|
int rc;
|
|
|
|
|
2018-01-31 14:32:55 +00:00
|
|
|
r = talloc_zero(mgcp, struct mgcp_response);
|
|
|
|
OSMO_ASSERT(r);
|
|
|
|
|
2023-06-14 10:20:49 +00:00
|
|
|
/* Re-arm keepalive timer if enabled */
|
|
|
|
if (OSMO_UNLIKELY(mgcp->conn_up == false)) {
|
|
|
|
LOGPMGW(mgcp, LOGL_NOTICE, "MGCP link to MGW now considered UP\n");
|
|
|
|
mgcp->conn_up = true;
|
|
|
|
}
|
|
|
|
if (mgcp->actual.keepalive.timeout_sec > 0)
|
|
|
|
osmo_timer_schedule(&mgcp->keepalive_rx_timer, mgcp->actual.keepalive.timeout_sec, 0);
|
|
|
|
|
2018-01-31 14:32:55 +00:00
|
|
|
rc = mgcp_response_parse_head(r, msg);
|
2016-05-20 19:59:55 +00:00
|
|
|
if (rc) {
|
2021-09-03 15:50:51 +00:00
|
|
|
LOGPMGW(mgcp, LOGL_ERROR, "Cannot parse MGCP response (head)\n");
|
2018-01-31 14:32:55 +00:00
|
|
|
rc = 1;
|
|
|
|
goto error;
|
2017-11-22 10:44:50 +00:00
|
|
|
}
|
|
|
|
|
2021-09-03 15:50:51 +00:00
|
|
|
LOGPMGW(mgcp, LOGL_DEBUG, "MGCP client: Rx %d %u %s\n",
|
2021-07-14 23:36:47 +00:00
|
|
|
r->head.response_code, r->head.trans_id, r->head.comment);
|
|
|
|
|
2018-01-31 14:32:55 +00:00
|
|
|
rc = parse_head_params(r);
|
2017-11-22 10:44:50 +00:00
|
|
|
if (rc) {
|
2021-09-03 15:50:51 +00:00
|
|
|
LOGPMGW(mgcp, LOGL_ERROR, "Cannot parse MGCP response (head parameters)\n");
|
2018-01-31 14:32:55 +00:00
|
|
|
rc = 1;
|
|
|
|
goto error;
|
2016-05-20 19:59:55 +00:00
|
|
|
}
|
|
|
|
|
2018-01-31 14:32:55 +00:00
|
|
|
pending = mgcp_client_response_pending_get(mgcp, r->head.trans_id);
|
2016-05-20 19:59:55 +00:00
|
|
|
if (!pending) {
|
2021-09-03 15:50:51 +00:00
|
|
|
LOGPMGW(mgcp, LOGL_ERROR, "Cannot find matching MGCP transaction for trans_id %d\n",
|
|
|
|
r->head.trans_id);
|
2018-01-31 14:32:55 +00:00
|
|
|
rc = -ENOENT;
|
|
|
|
goto error;
|
2016-05-20 19:59:55 +00:00
|
|
|
}
|
|
|
|
|
2018-01-31 14:32:55 +00:00
|
|
|
mgcp_client_handle_response(mgcp, pending, r);
|
|
|
|
rc = 0;
|
|
|
|
|
|
|
|
error:
|
|
|
|
talloc_free(r);
|
|
|
|
return rc;
|
2016-05-20 19:59:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int mgcp_do_read(struct osmo_fd *fd)
|
|
|
|
{
|
2017-09-03 23:02:56 +00:00
|
|
|
struct mgcp_client *mgcp = fd->data;
|
2016-05-20 19:59:55 +00:00
|
|
|
struct msgb *msg;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
msg = msgb_alloc_headroom(4096, 128, "mgcp_from_gw");
|
|
|
|
if (!msg) {
|
2021-09-03 15:50:51 +00:00
|
|
|
LOGPMGW(mgcp, LOGL_ERROR, "Failed to allocate MGCP message.\n");
|
2016-05-20 19:59:55 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2023-12-11 14:03:26 +00:00
|
|
|
/* msgb_tailroom() is basically (4096 - 128); -1 is for '\0' */
|
|
|
|
ret = read(fd->fd, msg->data, msgb_tailroom(msg) - 1);
|
2016-05-20 19:59:55 +00:00
|
|
|
if (ret <= 0) {
|
2021-09-03 15:50:51 +00:00
|
|
|
LOGPMGW(mgcp, LOGL_ERROR, "Failed to read: %s: %d='%s'\n",
|
|
|
|
osmo_sock_get_name2(fd->fd), errno, strerror(errno));
|
2018-12-12 02:10:18 +00:00
|
|
|
|
2016-05-20 19:59:55 +00:00
|
|
|
msgb_free(msg);
|
|
|
|
return -1;
|
2017-11-17 13:14:31 +00:00
|
|
|
}
|
2016-05-20 19:59:55 +00:00
|
|
|
|
|
|
|
msg->l2h = msgb_put(msg, ret);
|
2017-09-03 23:02:56 +00:00
|
|
|
ret = mgcp_client_rx(mgcp, msg);
|
2016-05-20 19:59:55 +00:00
|
|
|
talloc_free(msg);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int mgcp_do_write(struct osmo_fd *fd, struct msgb *msg)
|
|
|
|
{
|
|
|
|
int ret;
|
2021-09-03 15:50:51 +00:00
|
|
|
struct mgcp_client *mgcp = fd->data;
|
2016-05-20 19:59:55 +00:00
|
|
|
|
2021-09-03 15:50:51 +00:00
|
|
|
LOGPMGW(mgcp, LOGL_DEBUG, "Tx MGCP: %s: len=%u '%s'...\n",
|
|
|
|
osmo_sock_get_name2(fd->fd), msg->len,
|
|
|
|
osmo_escape_str((const char *)msg->data, OSMO_MIN(42, msg->len)));
|
2016-05-20 19:59:55 +00:00
|
|
|
|
|
|
|
ret = write(fd->fd, msg->data, msg->len);
|
2023-06-14 10:20:49 +00:00
|
|
|
if (OSMO_UNLIKELY(ret != msg->len))
|
2021-09-03 15:50:51 +00:00
|
|
|
LOGPMGW(mgcp, LOGL_ERROR, "Failed to Tx MGCP: %s: %d='%s'; msg: len=%u '%s'...\n",
|
|
|
|
osmo_sock_get_name2(fd->fd), errno, strerror(errno),
|
|
|
|
msg->len, osmo_escape_str((const char *)msg->data, OSMO_MIN(42, msg->len)));
|
2023-06-14 10:20:49 +00:00
|
|
|
|
|
|
|
/* Re-arm the keepalive Tx timer: */
|
|
|
|
if (mgcp->actual.keepalive.req_interval_sec > 0)
|
|
|
|
osmo_timer_schedule(&mgcp->keepalive_tx_timer, mgcp->actual.keepalive.req_interval_sec, 0);
|
2016-05-20 19:59:55 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2023-06-14 09:52:04 +00:00
|
|
|
static const char *_mgcp_client_name_append_domain(const struct mgcp_client *mgcp, const char *name)
|
|
|
|
{
|
|
|
|
static char endpoint[MGCP_ENDPOINT_MAXLEN];
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
rc = snprintf(endpoint, sizeof(endpoint), "%s@%s", name, mgcp_client_endpoint_domain(mgcp));
|
|
|
|
if (rc > sizeof(endpoint) - 1) {
|
|
|
|
LOGPMGW(mgcp, LOGL_ERROR, "MGCP endpoint exceeds maximum length of %zu: '%s@%s'\n",
|
|
|
|
sizeof(endpoint) - 1, name, mgcp_client_endpoint_domain(mgcp));
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
if (rc < 1) {
|
|
|
|
LOGPMGW(mgcp, LOGL_ERROR, "Cannot compose MGCP endpoint name\n");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
return endpoint;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Safely ignore the MGCP response to the DLCX sent via _mgcp_client_send_dlcx() */
|
|
|
|
static void _ignore_mgcp_response(struct mgcp_response *response, void *priv) { }
|
|
|
|
|
|
|
|
/* Format DLCX message (fire and forget) and send it off to the MGW */
|
|
|
|
static void _mgcp_client_send_dlcx(struct mgcp_client *mgcp, const char *epname)
|
|
|
|
{
|
|
|
|
struct msgb *msgb_dlcx;
|
|
|
|
struct mgcp_msg mgcp_msg_dlcx = {
|
|
|
|
.verb = MGCP_VERB_DLCX,
|
|
|
|
.presence = MGCP_MSG_PRESENCE_ENDPOINT,
|
|
|
|
};
|
|
|
|
osmo_strlcpy(mgcp_msg_dlcx.endpoint, epname, sizeof(mgcp_msg_dlcx.endpoint));
|
|
|
|
msgb_dlcx = mgcp_msg_gen(mgcp, &mgcp_msg_dlcx);
|
|
|
|
mgcp_client_tx(mgcp, msgb_dlcx, &_ignore_mgcp_response, NULL);
|
|
|
|
}
|
|
|
|
|
2023-06-14 10:20:49 +00:00
|
|
|
/* Format AuditEndpoint message (fire and forget) and send it off to the MGW */
|
|
|
|
static void _mgcp_client_send_auep(struct mgcp_client *mgcp, const char *epname)
|
|
|
|
{
|
|
|
|
struct msgb *msgb_auep;
|
|
|
|
struct mgcp_msg mgcp_msg_auep = {
|
|
|
|
.verb = MGCP_VERB_AUEP,
|
|
|
|
.presence = MGCP_MSG_PRESENCE_ENDPOINT,
|
|
|
|
};
|
|
|
|
OSMO_STRLCPY_ARRAY(mgcp_msg_auep.endpoint, epname);
|
|
|
|
msgb_auep = mgcp_msg_gen(mgcp, &mgcp_msg_auep);
|
|
|
|
mgcp_client_tx(mgcp, msgb_auep, &_ignore_mgcp_response, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void mgcp_client_keepalive_tx_timer_cb(void *data)
|
|
|
|
{
|
|
|
|
struct mgcp_client *mgcp = (struct mgcp_client *)data;
|
|
|
|
LOGPMGW(mgcp, LOGL_INFO, "Triggering keepalive MGCP request\n");
|
|
|
|
const char *epname = _mgcp_client_name_append_domain(mgcp, mgcp->actual.keepalive.req_endpoint_name);
|
|
|
|
_mgcp_client_send_auep(mgcp, epname);
|
|
|
|
|
|
|
|
/* Re-arm the timer: */
|
|
|
|
osmo_timer_schedule(&mgcp->keepalive_tx_timer, mgcp->actual.keepalive.req_interval_sec, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void mgcp_client_keepalive_rx_timer_cb(void *data)
|
|
|
|
{
|
|
|
|
struct mgcp_client *mgcp = (struct mgcp_client *)data;
|
|
|
|
LOGPMGW(mgcp, LOGL_ERROR, "MGCP link to MGW now considered DOWN (keepalive timeout, more than %u seconds with no answer from MGW)\n",
|
|
|
|
mgcp->actual.keepalive.timeout_sec);
|
|
|
|
mgcp->conn_up = false;
|
|
|
|
/* TODO: Potentially time out all ongoing transactions for that MGW. Maybe based on VTY cfg? */
|
|
|
|
}
|
|
|
|
|
2017-09-03 23:02:56 +00:00
|
|
|
struct mgcp_client *mgcp_client_init(void *ctx,
|
|
|
|
struct mgcp_client_conf *conf)
|
2016-05-20 19:59:55 +00:00
|
|
|
{
|
2017-09-03 23:02:56 +00:00
|
|
|
struct mgcp_client *mgcp;
|
2021-07-22 09:53:07 +00:00
|
|
|
struct reset_ep *reset_ep;
|
|
|
|
struct reset_ep *actual_reset_ep;
|
2016-05-20 19:59:55 +00:00
|
|
|
|
2017-09-03 23:02:56 +00:00
|
|
|
mgcp = talloc_zero(ctx, struct mgcp_client);
|
2020-07-15 08:56:45 +00:00
|
|
|
if (!mgcp)
|
|
|
|
return NULL;
|
2016-05-20 19:59:55 +00:00
|
|
|
|
|
|
|
INIT_LLIST_HEAD(&mgcp->responses_pending);
|
|
|
|
|
|
|
|
mgcp->next_trans_id = 1;
|
|
|
|
|
|
|
|
mgcp->actual.local_addr = conf->local_addr ? conf->local_addr :
|
2017-09-03 23:02:56 +00:00
|
|
|
MGCP_CLIENT_LOCAL_ADDR_DEFAULT;
|
2022-10-17 17:20:45 +00:00
|
|
|
mgcp->actual.local_port = conf->local_port > 0 ? (uint16_t)conf->local_port :
|
2017-09-03 23:02:56 +00:00
|
|
|
MGCP_CLIENT_LOCAL_PORT_DEFAULT;
|
2016-05-20 19:59:55 +00:00
|
|
|
|
|
|
|
mgcp->actual.remote_addr = conf->remote_addr ? conf->remote_addr :
|
2017-09-03 23:02:56 +00:00
|
|
|
MGCP_CLIENT_REMOTE_ADDR_DEFAULT;
|
2016-05-20 19:59:55 +00:00
|
|
|
mgcp->actual.remote_port = conf->remote_port >= 0 ? (uint16_t)conf->remote_port :
|
2017-09-03 23:02:56 +00:00
|
|
|
MGCP_CLIENT_REMOTE_PORT_DEFAULT;
|
2016-05-20 19:59:55 +00:00
|
|
|
|
2018-12-18 23:27:50 +00:00
|
|
|
if (osmo_strlcpy(mgcp->actual.endpoint_domain_name, conf->endpoint_domain_name,
|
|
|
|
sizeof(mgcp->actual.endpoint_domain_name))
|
|
|
|
>= sizeof(mgcp->actual.endpoint_domain_name)) {
|
2021-09-03 15:50:51 +00:00
|
|
|
LOGPMGW(mgcp, LOGL_ERROR, "MGCP client: endpoint domain name is too long, max length is %zu: '%s'\n",
|
|
|
|
sizeof(mgcp->actual.endpoint_domain_name) - 1, conf->endpoint_domain_name);
|
2018-12-18 23:27:50 +00:00
|
|
|
talloc_free(mgcp);
|
|
|
|
return NULL;
|
|
|
|
}
|
2021-09-03 15:50:51 +00:00
|
|
|
LOGPMGW(mgcp, LOGL_NOTICE, "MGCP client: using endpoint domain '@%s'\n", mgcp_client_endpoint_domain(mgcp));
|
2018-12-18 23:27:50 +00:00
|
|
|
|
2021-07-22 09:53:07 +00:00
|
|
|
INIT_LLIST_HEAD(&mgcp->actual.reset_epnames);
|
|
|
|
llist_for_each_entry(reset_ep, &conf->reset_epnames, list) {
|
|
|
|
actual_reset_ep = talloc_memdup(mgcp, reset_ep, sizeof(*reset_ep));
|
|
|
|
llist_add_tail(&actual_reset_ep->list, &mgcp->actual.reset_epnames);
|
|
|
|
}
|
|
|
|
|
2021-09-03 15:50:51 +00:00
|
|
|
if (conf->description)
|
|
|
|
mgcp->actual.description = talloc_strdup(mgcp, conf->description);
|
|
|
|
|
2023-06-14 10:20:49 +00:00
|
|
|
osmo_wqueue_init(&mgcp->wq, 1024);
|
|
|
|
mgcp->wq.read_cb = mgcp_do_read;
|
|
|
|
mgcp->wq.write_cb = mgcp_do_write;
|
|
|
|
osmo_fd_setup(&mgcp->wq.bfd, -1, OSMO_FD_READ, osmo_wqueue_bfd_cb, mgcp, 0);
|
|
|
|
|
|
|
|
memcpy(&mgcp->actual.keepalive, &conf->keepalive, sizeof(conf->keepalive));
|
|
|
|
osmo_timer_setup(&mgcp->keepalive_tx_timer, mgcp_client_keepalive_tx_timer_cb, mgcp);
|
|
|
|
osmo_timer_setup(&mgcp->keepalive_rx_timer, mgcp_client_keepalive_rx_timer_cb, mgcp);
|
|
|
|
|
2016-05-20 19:59:55 +00:00
|
|
|
return mgcp;
|
|
|
|
}
|
|
|
|
|
2021-07-22 09:53:07 +00:00
|
|
|
/*! Initialize client connection (opens socket)
|
2018-01-19 23:58:16 +00:00
|
|
|
* \param[in,out] mgcp MGCP client descriptor.
|
|
|
|
* \returns 0 on success, -EINVAL on error. */
|
2022-10-17 17:20:45 +00:00
|
|
|
int mgcp_client_connect(struct mgcp_client *mgcp)
|
2016-05-20 19:59:55 +00:00
|
|
|
{
|
|
|
|
int rc;
|
2021-07-22 09:53:07 +00:00
|
|
|
struct reset_ep *reset_ep;
|
|
|
|
const char *epname;
|
2023-06-14 10:20:49 +00:00
|
|
|
bool some_dlcx_sent = false;
|
2016-05-20 19:59:55 +00:00
|
|
|
|
|
|
|
if (!mgcp) {
|
2021-09-03 15:50:51 +00:00
|
|
|
LOGPMGW(mgcp, LOGL_FATAL, "Client not initialized properly\n");
|
2016-05-20 19:59:55 +00:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2023-06-14 10:20:49 +00:00
|
|
|
rc = osmo_sock_init2_ofd(&mgcp->wq.bfd, AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP, mgcp->actual.local_addr,
|
2022-10-17 17:20:45 +00:00
|
|
|
mgcp->actual.local_port, mgcp->actual.remote_addr, mgcp->actual.remote_port,
|
|
|
|
OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT);
|
2017-11-17 14:09:30 +00:00
|
|
|
if (rc < 0) {
|
2021-09-03 15:50:51 +00:00
|
|
|
LOGPMGW(mgcp, LOGL_FATAL,
|
|
|
|
"Failed to initialize socket %s:%u -> %s:%u for MGW: %s\n",
|
2021-07-29 09:55:14 +00:00
|
|
|
mgcp->actual.local_addr ? mgcp->actual.local_addr : "(any)", mgcp->actual.local_port,
|
|
|
|
mgcp->actual.remote_addr ? mgcp->actual.local_addr : "(any)", mgcp->actual.remote_port,
|
|
|
|
strerror(errno));
|
2016-05-20 19:59:55 +00:00
|
|
|
goto error_close_fd;
|
|
|
|
}
|
|
|
|
|
2023-06-14 10:20:49 +00:00
|
|
|
LOGPMGW(mgcp, LOGL_INFO, "MGW connection: %s\n", osmo_sock_get_name2(mgcp->wq.bfd.fd));
|
2016-05-20 19:59:55 +00:00
|
|
|
|
2021-07-22 09:53:07 +00:00
|
|
|
/* If configured, send a DLCX message to the endpoints that are configured to
|
|
|
|
* be reset on startup. Usually this is a wildcarded endpoint. */
|
|
|
|
llist_for_each_entry(reset_ep, &mgcp->actual.reset_epnames, list) {
|
|
|
|
epname = _mgcp_client_name_append_domain(mgcp, reset_ep->name);
|
2021-09-03 15:50:51 +00:00
|
|
|
LOGPMGW(mgcp, LOGL_INFO, "Sending DLCX to: %s\n", epname);
|
2021-07-22 09:53:07 +00:00
|
|
|
_mgcp_client_send_dlcx(mgcp, epname);
|
2023-06-14 10:20:49 +00:00
|
|
|
some_dlcx_sent = true;
|
2021-07-22 09:53:07 +00:00
|
|
|
}
|
2023-06-14 10:20:49 +00:00
|
|
|
|
2023-06-26 16:44:44 +00:00
|
|
|
if (mgcp->actual.keepalive.req_interval_sec > 0) {
|
|
|
|
if (!some_dlcx_sent) {
|
2023-06-14 10:20:49 +00:00
|
|
|
/* Attempt an immediate probe to find out if link is UP or DOWN: */
|
|
|
|
osmo_timer_schedule(&mgcp->keepalive_tx_timer, 0, 0);
|
|
|
|
}
|
2023-06-26 16:44:44 +00:00
|
|
|
/* else: keepalive_tx_timer was already scheduled (if needed) down in the stack during Tx DLCX above */
|
|
|
|
} else {
|
|
|
|
/* Assume link is UP by default, so that this MGW can be selected: */
|
|
|
|
mgcp->conn_up = true;
|
2023-06-14 10:20:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (mgcp->actual.keepalive.timeout_sec > 0)
|
|
|
|
osmo_timer_schedule(&mgcp->keepalive_rx_timer, mgcp->actual.keepalive.timeout_sec, 0);
|
|
|
|
|
2016-05-20 19:59:55 +00:00
|
|
|
return 0;
|
|
|
|
error_close_fd:
|
2023-06-14 10:20:49 +00:00
|
|
|
close(mgcp->wq.bfd.fd);
|
|
|
|
mgcp->wq.bfd.fd = -1;
|
2016-05-20 19:59:55 +00:00
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2022-10-17 17:20:45 +00:00
|
|
|
/*! DEPRECATED: Initialize client connection (opens socket)
|
2021-07-26 11:20:05 +00:00
|
|
|
* \param[in,out] mgcp MGCP client descriptor.
|
|
|
|
* \returns 0 on success, -EINVAL on error. */
|
2022-10-17 17:20:45 +00:00
|
|
|
int mgcp_client_connect2(struct mgcp_client *mgcp, unsigned int retry_n_ports)
|
2021-07-26 11:20:05 +00:00
|
|
|
{
|
2022-10-17 17:20:45 +00:00
|
|
|
return mgcp_client_connect(mgcp);
|
2021-07-26 11:20:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*! Terminate client connection
|
|
|
|
* \param[in,out] mgcp MGCP client descriptor.
|
|
|
|
* \returns 0 on success, -EINVAL on error. */
|
|
|
|
void mgcp_client_disconnect(struct mgcp_client *mgcp)
|
|
|
|
{
|
|
|
|
struct osmo_wqueue *wq;
|
|
|
|
|
|
|
|
if (!mgcp) {
|
2021-09-03 15:50:51 +00:00
|
|
|
LOGP(DLMGCP, LOGL_FATAL, "MGCP client not initialized properly\n");
|
2021-07-26 11:20:05 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-06-14 10:20:49 +00:00
|
|
|
/* Disarm keepalive Tx/Rx timer until next connect() */
|
|
|
|
osmo_timer_del(&mgcp->keepalive_rx_timer);
|
|
|
|
osmo_timer_del(&mgcp->keepalive_tx_timer);
|
|
|
|
mgcp->conn_up = false;
|
|
|
|
|
2021-07-26 11:20:05 +00:00
|
|
|
wq = &mgcp->wq;
|
|
|
|
osmo_wqueue_clear(wq);
|
2021-09-03 15:50:51 +00:00
|
|
|
LOGPMGW(mgcp, LOGL_INFO, "MGCP association: %s -- closed!\n", osmo_sock_get_name2(wq->bfd.fd));
|
2021-07-26 11:20:05 +00:00
|
|
|
if (osmo_fd_is_registered(&wq->bfd))
|
|
|
|
osmo_fd_unregister(&wq->bfd);
|
2023-03-14 10:48:27 +00:00
|
|
|
close(wq->bfd.fd);
|
|
|
|
wq->bfd.fd = -1;
|
2021-07-26 11:20:05 +00:00
|
|
|
}
|
|
|
|
|
2018-01-19 23:58:16 +00:00
|
|
|
/*! Get the IP-Aaddress of the associated MGW as string.
|
|
|
|
* \param[in] mgcp MGCP client descriptor.
|
|
|
|
* \returns a pointer to the address string. */
|
2017-09-03 23:02:56 +00:00
|
|
|
const char *mgcp_client_remote_addr_str(struct mgcp_client *mgcp)
|
2016-05-20 19:59:55 +00:00
|
|
|
{
|
|
|
|
return mgcp->actual.remote_addr;
|
|
|
|
}
|
|
|
|
|
2018-01-19 23:58:16 +00:00
|
|
|
/*! Get the IP-Port of the associated MGW.
|
|
|
|
* \param[in] mgcp MGCP client descriptor.
|
|
|
|
* \returns port number. */
|
2017-09-03 23:02:56 +00:00
|
|
|
uint16_t mgcp_client_remote_port(struct mgcp_client *mgcp)
|
2016-05-20 19:59:55 +00:00
|
|
|
{
|
|
|
|
return mgcp->actual.remote_port;
|
|
|
|
}
|
|
|
|
|
2020-09-02 15:19:20 +00:00
|
|
|
/*! Get the IP-Address of the associated MGW as its numeric representation.
|
|
|
|
* DEPRECATED, DON'T USE.
|
2018-01-19 23:58:16 +00:00
|
|
|
* \param[in] mgcp MGCP client descriptor.
|
|
|
|
* \returns IP-Address as 32 bit integer (network byte order) */
|
2017-09-03 23:02:56 +00:00
|
|
|
uint32_t mgcp_client_remote_addr_n(struct mgcp_client *mgcp)
|
2016-05-20 19:59:55 +00:00
|
|
|
{
|
2020-09-02 15:19:20 +00:00
|
|
|
return 0;
|
2016-05-20 19:59:55 +00:00
|
|
|
}
|
|
|
|
|
2018-12-18 23:27:50 +00:00
|
|
|
/* To compose endpoint names, usually for CRCX, use this as domain name.
|
|
|
|
* For example, snprintf("rtpbridge\*@%s", mgcp_client_endpoint_domain(mgcp)). */
|
|
|
|
const char *mgcp_client_endpoint_domain(const struct mgcp_client *mgcp)
|
|
|
|
{
|
|
|
|
return mgcp->actual.endpoint_domain_name[0] ? mgcp->actual.endpoint_domain_name : "mgw";
|
|
|
|
}
|
|
|
|
|
2020-06-25 14:11:04 +00:00
|
|
|
/*! Compose endpoint name for a wildcarded request to the virtual trunk
|
|
|
|
* \param[in] mgcp MGCP client descriptor.
|
|
|
|
* \returns string containing the endpoint name (e.g. rtpbridge\*@mgw) */
|
2019-04-16 15:23:38 +00:00
|
|
|
const char *mgcp_client_rtpbridge_wildcard(const struct mgcp_client *mgcp)
|
|
|
|
{
|
|
|
|
return _mgcp_client_name_append_domain(mgcp, "rtpbridge/*");
|
|
|
|
}
|
|
|
|
|
2020-06-25 18:16:22 +00:00
|
|
|
/*! Compose endpoint name for an E1 endpoint.
|
|
|
|
* \param[in] ctx talloc context.
|
|
|
|
* \param[in] mgcp MGCP client descriptor.
|
|
|
|
* \param[in] trunk_id id number of the E1 trunk (1-64).
|
|
|
|
* \param[in] ts timeslot on the E1 trunk (1-31).
|
|
|
|
* \param[in] rate bitrate used on the E1 trunk (e.g 16 for 16kbit).
|
|
|
|
* \param[in] offset bit offset of the E1 subslot (e.g. 4 for the third 16k subslot).
|
|
|
|
* \returns string containing the endpoint name (e.g. ds/e1-1/s-1/su16-4). */
|
|
|
|
const char *mgcp_client_e1_epname(void *ctx, const struct mgcp_client *mgcp, uint8_t trunk_id, uint8_t ts,
|
|
|
|
uint8_t rate, uint8_t offset)
|
|
|
|
{
|
|
|
|
/* See also comment in libosmo-mgcp, mgcp_client.c, gen_e1_epname() */
|
|
|
|
const uint8_t valid_rates[] = { 64, 32, 32, 16, 16, 16, 16, 8, 8, 8, 8, 8, 8, 8, 8 };
|
|
|
|
const uint8_t valid_offsets[] = { 0, 0, 4, 0, 2, 4, 6, 0, 1, 2, 3, 4, 5, 6, 7 };
|
|
|
|
|
|
|
|
uint8_t i;
|
|
|
|
bool rate_offs_valid = false;
|
|
|
|
char *epname;
|
|
|
|
|
|
|
|
epname =
|
|
|
|
talloc_asprintf(ctx, "ds/e1-%u/s-%u/su%u-%u@%s", trunk_id, ts, rate, offset,
|
|
|
|
mgcp_client_endpoint_domain(mgcp));
|
|
|
|
if (!epname) {
|
2021-09-03 15:50:51 +00:00
|
|
|
LOGPMGW(mgcp, LOGL_ERROR, "Cannot compose MGCP e1-endpoint name!\n");
|
2020-06-25 18:16:22 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check if the supplied rate/offset pair resembles a valid combination */
|
|
|
|
for (i = 0; i < sizeof(valid_rates); i++) {
|
|
|
|
if (valid_rates[i] == rate && valid_offsets[i] == offset)
|
|
|
|
rate_offs_valid = true;
|
|
|
|
}
|
|
|
|
if (!rate_offs_valid) {
|
2021-09-03 15:50:51 +00:00
|
|
|
LOGPMGW(mgcp, LOGL_ERROR,
|
|
|
|
"Cannot compose MGCP e1-endpoint name (%s), rate(%u)/offset(%u) combination is invalid!\n",
|
|
|
|
epname, rate, offset);
|
2020-06-25 18:16:22 +00:00
|
|
|
talloc_free(epname);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* An E1 line has a maximum of 32 timeslots, while the first (ts=0) is
|
|
|
|
* reserverd for framing and alignment, so we can not use it here. */
|
2020-11-26 21:21:11 +00:00
|
|
|
if (ts == 0 || ts > NUM_E1_TS-1) {
|
2021-09-03 15:50:51 +00:00
|
|
|
LOGPMGW(mgcp, LOGL_ERROR,
|
|
|
|
"Cannot compose MGCP e1-endpoint name (%s), E1-timeslot number (%u) is invalid!\n",
|
|
|
|
epname, ts);
|
2020-06-25 18:16:22 +00:00
|
|
|
talloc_free(epname);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return epname;
|
|
|
|
}
|
|
|
|
|
2023-02-20 11:40:46 +00:00
|
|
|
struct mgcp_response_pending *mgcp_client_pending_add(struct mgcp_client *mgcp, mgcp_trans_id_t trans_id,
|
|
|
|
mgcp_response_cb_t response_cb, void *priv)
|
2016-05-20 19:59:55 +00:00
|
|
|
{
|
|
|
|
struct mgcp_response_pending *pending;
|
|
|
|
|
|
|
|
pending = talloc_zero(mgcp, struct mgcp_response_pending);
|
2020-07-15 08:57:08 +00:00
|
|
|
if (!pending)
|
|
|
|
return NULL;
|
|
|
|
|
2016-05-20 19:59:55 +00:00
|
|
|
pending->trans_id = trans_id;
|
|
|
|
pending->response_cb = response_cb;
|
|
|
|
pending->priv = priv;
|
|
|
|
llist_add_tail(&pending->entry, &mgcp->responses_pending);
|
|
|
|
|
|
|
|
return pending;
|
|
|
|
}
|
|
|
|
|
2021-09-03 15:50:51 +00:00
|
|
|
/* Send the MGCP message in msg to the MGW and handle a response with
|
2016-05-20 19:59:55 +00:00
|
|
|
* response_cb. NOTE: the response_cb still needs to call
|
|
|
|
* mgcp_response_parse_params(response) to get the parsed parameters -- to
|
|
|
|
* potentially save some CPU cycles, only the head line has been parsed when
|
mgcp_client: add transaction cleanup
So far, if an MGCP message is sent, the transaction gets enqueued, but there is
no way to end the transaction other than receiving a valid reply. So, if the
caller decides that the transaction timed out and tears down the priv pointer
passed to mgcp_client_tx, and if then a late reply arrives, the callback will
dereference the invalid priv pointer and cause a segfault. Hence it is possible
to crash an mgcp_client program by sending a late response.
Furthermore, if no reply ever arrives, we would keep the pending response in
the list forever, amounting to a "memory leak".
Add mgcp_client_cancel() to discard a pending transaction. The caller can now
decide to discard a pending response when it sees fit (e.g. the caller's
timeout expired). This needs to be added to OsmoMSC and OsmoBSC.
Add mgcp_msg_trans_id() to provide an obvious way to obtain the transaction id
from a generated MGCP message.
No public API is broken; but refine the negative return code from
mgcp_client_rx(): return -ENOENT if no such transaction ID is found, and still
-1 if decoding failed. This is mainly for mgcp_client_test.
Implement a test for mgcp_client_cancel() in mgcp_client_test.c.
Tweak internal mgcp_client_response_pending_get() to take only the transaction
id as argument instead of the entire mgcp message struct.
Found-by: dexter
Related: OS#2695 OS#2696
Change-Id: I16811e168a46a82a05943252a737b3434143f4bd
2017-11-30 12:43:11 +00:00
|
|
|
* the response_cb is invoked.
|
|
|
|
* Before the priv pointer becomes invalid, e.g. due to transaction timeout,
|
|
|
|
* mgcp_client_cancel() needs to be called for this transaction.
|
|
|
|
*/
|
2017-09-03 23:02:56 +00:00
|
|
|
int mgcp_client_tx(struct mgcp_client *mgcp, struct msgb *msg,
|
|
|
|
mgcp_response_cb_t response_cb, void *priv)
|
2016-05-20 19:59:55 +00:00
|
|
|
{
|
2020-06-17 14:54:13 +00:00
|
|
|
struct mgcp_response_pending *pending = NULL;
|
2016-05-20 19:59:55 +00:00
|
|
|
mgcp_trans_id_t trans_id;
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
trans_id = msg->cb[MSGB_CB_MGCP_TRANS_ID];
|
|
|
|
if (!trans_id) {
|
2021-09-03 15:50:51 +00:00
|
|
|
LOGPMGW(mgcp, LOGL_ERROR,
|
|
|
|
"Unset transaction id in mgcp send request\n");
|
2016-05-20 19:59:55 +00:00
|
|
|
talloc_free(msg);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2020-06-17 14:54:13 +00:00
|
|
|
/* Do not allocate a dummy 'mgcp_response_pending' entry */
|
|
|
|
if (response_cb != NULL) {
|
|
|
|
pending = mgcp_client_pending_add(mgcp, trans_id, response_cb, priv);
|
|
|
|
if (!pending) {
|
|
|
|
talloc_free(msg);
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
2020-07-15 08:57:08 +00:00
|
|
|
}
|
2016-05-20 19:59:55 +00:00
|
|
|
|
|
|
|
if (msgb_l2len(msg) > 4096) {
|
2021-09-03 15:50:51 +00:00
|
|
|
LOGPMGW(mgcp, LOGL_ERROR,
|
|
|
|
"Cannot send, MGCP message too large: %u\n",
|
|
|
|
msgb_l2len(msg));
|
2016-05-20 19:59:55 +00:00
|
|
|
msgb_free(msg);
|
|
|
|
rc = -EINVAL;
|
|
|
|
goto mgcp_tx_error;
|
|
|
|
}
|
|
|
|
|
|
|
|
rc = osmo_wqueue_enqueue(&mgcp->wq, msg);
|
|
|
|
if (rc) {
|
2021-09-03 15:50:51 +00:00
|
|
|
LOGPMGW(mgcp, LOGL_FATAL, "Could not queue message to MGW\n");
|
2016-05-20 19:59:55 +00:00
|
|
|
msgb_free(msg);
|
|
|
|
goto mgcp_tx_error;
|
|
|
|
} else
|
2021-09-03 15:50:51 +00:00
|
|
|
LOGPMGW(mgcp, LOGL_DEBUG, "Queued %u bytes for MGW\n",
|
|
|
|
msgb_l2len(msg));
|
2016-05-20 19:59:55 +00:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
mgcp_tx_error:
|
2020-06-17 14:54:13 +00:00
|
|
|
if (!pending)
|
2020-07-16 08:48:13 +00:00
|
|
|
return rc;
|
2020-06-17 13:50:17 +00:00
|
|
|
/* Dequeue pending response, it's going to be free()d */
|
|
|
|
llist_del(&pending->entry);
|
2016-05-20 19:59:55 +00:00
|
|
|
/* Pass NULL to response cb to indicate an error */
|
2017-09-03 23:02:56 +00:00
|
|
|
mgcp_client_handle_response(mgcp, pending, NULL);
|
2020-07-16 08:48:13 +00:00
|
|
|
return rc;
|
2016-05-20 19:59:55 +00:00
|
|
|
}
|
|
|
|
|
2018-01-19 23:58:16 +00:00
|
|
|
/*! Cancel a pending transaction.
|
|
|
|
* \param[in] mgcp MGCP client descriptor.
|
|
|
|
* \param[in,out] trans_id Transaction id.
|
|
|
|
* \returns 0 on success, -ENOENT on error.
|
|
|
|
*
|
mgcp_client: add transaction cleanup
So far, if an MGCP message is sent, the transaction gets enqueued, but there is
no way to end the transaction other than receiving a valid reply. So, if the
caller decides that the transaction timed out and tears down the priv pointer
passed to mgcp_client_tx, and if then a late reply arrives, the callback will
dereference the invalid priv pointer and cause a segfault. Hence it is possible
to crash an mgcp_client program by sending a late response.
Furthermore, if no reply ever arrives, we would keep the pending response in
the list forever, amounting to a "memory leak".
Add mgcp_client_cancel() to discard a pending transaction. The caller can now
decide to discard a pending response when it sees fit (e.g. the caller's
timeout expired). This needs to be added to OsmoMSC and OsmoBSC.
Add mgcp_msg_trans_id() to provide an obvious way to obtain the transaction id
from a generated MGCP message.
No public API is broken; but refine the negative return code from
mgcp_client_rx(): return -ENOENT if no such transaction ID is found, and still
-1 if decoding failed. This is mainly for mgcp_client_test.
Implement a test for mgcp_client_cancel() in mgcp_client_test.c.
Tweak internal mgcp_client_response_pending_get() to take only the transaction
id as argument instead of the entire mgcp message struct.
Found-by: dexter
Related: OS#2695 OS#2696
Change-Id: I16811e168a46a82a05943252a737b3434143f4bd
2017-11-30 12:43:11 +00:00
|
|
|
* Should a priv pointer passed to mgcp_client_tx() become invalid, this function must be called. In
|
|
|
|
* practical terms, if the caller of mgcp_client_tx() wishes to tear down a transaction without having
|
|
|
|
* received a response this function must be called. The trans_id can be obtained by calling
|
2018-01-19 23:58:16 +00:00
|
|
|
* mgcp_msg_trans_id() on the msgb produced by mgcp_msg_gen(). */
|
mgcp_client: add transaction cleanup
So far, if an MGCP message is sent, the transaction gets enqueued, but there is
no way to end the transaction other than receiving a valid reply. So, if the
caller decides that the transaction timed out and tears down the priv pointer
passed to mgcp_client_tx, and if then a late reply arrives, the callback will
dereference the invalid priv pointer and cause a segfault. Hence it is possible
to crash an mgcp_client program by sending a late response.
Furthermore, if no reply ever arrives, we would keep the pending response in
the list forever, amounting to a "memory leak".
Add mgcp_client_cancel() to discard a pending transaction. The caller can now
decide to discard a pending response when it sees fit (e.g. the caller's
timeout expired). This needs to be added to OsmoMSC and OsmoBSC.
Add mgcp_msg_trans_id() to provide an obvious way to obtain the transaction id
from a generated MGCP message.
No public API is broken; but refine the negative return code from
mgcp_client_rx(): return -ENOENT if no such transaction ID is found, and still
-1 if decoding failed. This is mainly for mgcp_client_test.
Implement a test for mgcp_client_cancel() in mgcp_client_test.c.
Tweak internal mgcp_client_response_pending_get() to take only the transaction
id as argument instead of the entire mgcp message struct.
Found-by: dexter
Related: OS#2695 OS#2696
Change-Id: I16811e168a46a82a05943252a737b3434143f4bd
2017-11-30 12:43:11 +00:00
|
|
|
int mgcp_client_cancel(struct mgcp_client *mgcp, mgcp_trans_id_t trans_id)
|
|
|
|
{
|
|
|
|
struct mgcp_response_pending *pending = mgcp_client_response_pending_get(mgcp, trans_id);
|
|
|
|
if (!pending) {
|
2018-01-19 23:58:16 +00:00
|
|
|
/*! Note: it is not harmful to cancel a transaction twice. */
|
2021-09-03 15:50:51 +00:00
|
|
|
LOGPMGW(mgcp, LOGL_ERROR, "Cannot cancel, no such transaction: %u\n", trans_id);
|
mgcp_client: add transaction cleanup
So far, if an MGCP message is sent, the transaction gets enqueued, but there is
no way to end the transaction other than receiving a valid reply. So, if the
caller decides that the transaction timed out and tears down the priv pointer
passed to mgcp_client_tx, and if then a late reply arrives, the callback will
dereference the invalid priv pointer and cause a segfault. Hence it is possible
to crash an mgcp_client program by sending a late response.
Furthermore, if no reply ever arrives, we would keep the pending response in
the list forever, amounting to a "memory leak".
Add mgcp_client_cancel() to discard a pending transaction. The caller can now
decide to discard a pending response when it sees fit (e.g. the caller's
timeout expired). This needs to be added to OsmoMSC and OsmoBSC.
Add mgcp_msg_trans_id() to provide an obvious way to obtain the transaction id
from a generated MGCP message.
No public API is broken; but refine the negative return code from
mgcp_client_rx(): return -ENOENT if no such transaction ID is found, and still
-1 if decoding failed. This is mainly for mgcp_client_test.
Implement a test for mgcp_client_cancel() in mgcp_client_test.c.
Tweak internal mgcp_client_response_pending_get() to take only the transaction
id as argument instead of the entire mgcp message struct.
Found-by: dexter
Related: OS#2695 OS#2696
Change-Id: I16811e168a46a82a05943252a737b3434143f4bd
2017-11-30 12:43:11 +00:00
|
|
|
return -ENOENT;
|
|
|
|
}
|
2021-09-03 15:50:51 +00:00
|
|
|
LOGPMGW(mgcp, LOGL_DEBUG, "Canceled transaction %u\n", trans_id);
|
mgcp_client: add transaction cleanup
So far, if an MGCP message is sent, the transaction gets enqueued, but there is
no way to end the transaction other than receiving a valid reply. So, if the
caller decides that the transaction timed out and tears down the priv pointer
passed to mgcp_client_tx, and if then a late reply arrives, the callback will
dereference the invalid priv pointer and cause a segfault. Hence it is possible
to crash an mgcp_client program by sending a late response.
Furthermore, if no reply ever arrives, we would keep the pending response in
the list forever, amounting to a "memory leak".
Add mgcp_client_cancel() to discard a pending transaction. The caller can now
decide to discard a pending response when it sees fit (e.g. the caller's
timeout expired). This needs to be added to OsmoMSC and OsmoBSC.
Add mgcp_msg_trans_id() to provide an obvious way to obtain the transaction id
from a generated MGCP message.
No public API is broken; but refine the negative return code from
mgcp_client_rx(): return -ENOENT if no such transaction ID is found, and still
-1 if decoding failed. This is mainly for mgcp_client_test.
Implement a test for mgcp_client_cancel() in mgcp_client_test.c.
Tweak internal mgcp_client_response_pending_get() to take only the transaction
id as argument instead of the entire mgcp message struct.
Found-by: dexter
Related: OS#2695 OS#2696
Change-Id: I16811e168a46a82a05943252a737b3434143f4bd
2017-11-30 12:43:11 +00:00
|
|
|
talloc_free(pending);
|
|
|
|
return 0;
|
2018-01-19 23:58:16 +00:00
|
|
|
/*! We don't really need to clean up the wqueue: In all sane cases, the msgb has already been sent
|
|
|
|
* out and is no longer in the wqueue. If it still is in the wqueue, then sending MGCP messages
|
|
|
|
* per se is broken and the program should notice so by a full wqueue. Even if this was called
|
|
|
|
* before we had a chance to send out the message and it is still going to be sent, we will just
|
|
|
|
* ignore the reply to it later. Removing a msgb from the wqueue here would just introduce more
|
|
|
|
* bug surface in terms of failing to update wqueue API's counters or some such.
|
mgcp_client: add transaction cleanup
So far, if an MGCP message is sent, the transaction gets enqueued, but there is
no way to end the transaction other than receiving a valid reply. So, if the
caller decides that the transaction timed out and tears down the priv pointer
passed to mgcp_client_tx, and if then a late reply arrives, the callback will
dereference the invalid priv pointer and cause a segfault. Hence it is possible
to crash an mgcp_client program by sending a late response.
Furthermore, if no reply ever arrives, we would keep the pending response in
the list forever, amounting to a "memory leak".
Add mgcp_client_cancel() to discard a pending transaction. The caller can now
decide to discard a pending response when it sees fit (e.g. the caller's
timeout expired). This needs to be added to OsmoMSC and OsmoBSC.
Add mgcp_msg_trans_id() to provide an obvious way to obtain the transaction id
from a generated MGCP message.
No public API is broken; but refine the negative return code from
mgcp_client_rx(): return -ENOENT if no such transaction ID is found, and still
-1 if decoding failed. This is mainly for mgcp_client_test.
Implement a test for mgcp_client_cancel() in mgcp_client_test.c.
Tweak internal mgcp_client_response_pending_get() to take only the transaction
id as argument instead of the entire mgcp message struct.
Found-by: dexter
Related: OS#2695 OS#2696
Change-Id: I16811e168a46a82a05943252a737b3434143f4bd
2017-11-30 12:43:11 +00:00
|
|
|
*/
|
|
|
|
}
|
|
|
|
|
2017-09-03 23:02:56 +00:00
|
|
|
static mgcp_trans_id_t mgcp_client_next_trans_id(struct mgcp_client *mgcp)
|
2016-05-20 19:59:55 +00:00
|
|
|
{
|
|
|
|
/* avoid zero trans_id to distinguish from unset trans_id */
|
|
|
|
if (!mgcp->next_trans_id)
|
|
|
|
mgcp->next_trans_id ++;
|
|
|
|
return mgcp->next_trans_id ++;
|
|
|
|
}
|
|
|
|
|
2017-10-05 16:25:37 +00:00
|
|
|
#define MGCP_CRCX_MANDATORY (MGCP_MSG_PRESENCE_ENDPOINT | \
|
|
|
|
MGCP_MSG_PRESENCE_CALL_ID | \
|
|
|
|
MGCP_MSG_PRESENCE_CONN_MODE)
|
|
|
|
#define MGCP_MDCX_MANDATORY (MGCP_MSG_PRESENCE_ENDPOINT | \
|
2018-01-22 16:32:38 +00:00
|
|
|
MGCP_MSG_PRESENCE_CALL_ID | \
|
2017-10-05 16:25:37 +00:00
|
|
|
MGCP_MSG_PRESENCE_CONN_ID)
|
|
|
|
#define MGCP_DLCX_MANDATORY (MGCP_MSG_PRESENCE_ENDPOINT)
|
|
|
|
#define MGCP_AUEP_MANDATORY (MGCP_MSG_PRESENCE_ENDPOINT)
|
|
|
|
#define MGCP_RSIP_MANDATORY 0 /* none */
|
|
|
|
|
2018-06-07 16:51:31 +00:00
|
|
|
/* Helper function for mgcp_msg_gen(): Add LCO information to MGCP message */
|
|
|
|
static int add_lco(struct msgb *msg, struct mgcp_msg *mgcp_msg)
|
|
|
|
{
|
|
|
|
unsigned int i;
|
|
|
|
const char *codec;
|
|
|
|
unsigned int pt;
|
|
|
|
|
2023-03-30 14:53:13 +00:00
|
|
|
#define MSGB_PRINTF_OR_RET(FMT, ARGS...) do { \
|
|
|
|
if (msgb_printf(msg, FMT, ##ARGS) != 0) { \
|
|
|
|
LOGP(DLMGCP, LOGL_ERROR, "Message buffer too small, can not generate MGCP/SDP message\n"); \
|
|
|
|
return -ENOBUFS; \
|
|
|
|
} \
|
|
|
|
} while (0)
|
|
|
|
|
|
|
|
MSGB_PRINTF_OR_RET("L:");
|
2018-06-07 16:51:31 +00:00
|
|
|
|
|
|
|
if (mgcp_msg->ptime)
|
2023-03-30 14:53:13 +00:00
|
|
|
MSGB_PRINTF_OR_RET(" p:%u,", mgcp_msg->ptime);
|
2018-06-07 16:51:31 +00:00
|
|
|
|
|
|
|
if (mgcp_msg->codecs_len) {
|
2023-03-30 14:53:13 +00:00
|
|
|
MSGB_PRINTF_OR_RET(" a:");
|
2018-06-07 16:51:31 +00:00
|
|
|
for (i = 0; i < mgcp_msg->codecs_len; i++) {
|
|
|
|
pt = mgcp_msg->codecs[i];
|
2019-02-27 04:56:53 +00:00
|
|
|
codec = get_value_string_or_null(osmo_mgcpc_codec_names, pt);
|
2019-04-16 15:23:09 +00:00
|
|
|
|
2018-06-07 16:51:31 +00:00
|
|
|
/* Note: Use codec descriptors from enum mgcp_codecs
|
|
|
|
* in mgcp_client only! */
|
2023-03-30 14:53:13 +00:00
|
|
|
if (!codec)
|
2021-06-11 13:16:13 +00:00
|
|
|
return -EINVAL;
|
|
|
|
|
2023-03-30 14:53:13 +00:00
|
|
|
MSGB_PRINTF_OR_RET("%s", extract_codec_name(codec));
|
2018-06-07 16:51:31 +00:00
|
|
|
if (i < mgcp_msg->codecs_len - 1)
|
2023-03-30 14:53:13 +00:00
|
|
|
MSGB_PRINTF_OR_RET(";");
|
2018-06-07 16:51:31 +00:00
|
|
|
}
|
2023-03-30 14:53:13 +00:00
|
|
|
MSGB_PRINTF_OR_RET(",");
|
2018-06-07 16:51:31 +00:00
|
|
|
}
|
|
|
|
|
2023-03-30 14:53:13 +00:00
|
|
|
MSGB_PRINTF_OR_RET(" nt:IN\r\n");
|
2021-06-11 13:16:13 +00:00
|
|
|
return 0;
|
2023-03-30 14:53:13 +00:00
|
|
|
|
|
|
|
#undef MSGB_PRINTF_OR_RET
|
2018-06-07 16:51:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Helper function for mgcp_msg_gen(): Add SDP information to MGCP message */
|
|
|
|
static int add_sdp(struct msgb *msg, struct mgcp_msg *mgcp_msg, struct mgcp_client *mgcp)
|
|
|
|
{
|
|
|
|
unsigned int i;
|
2020-08-28 17:37:08 +00:00
|
|
|
char local_ip[INET6_ADDRSTRLEN];
|
2020-08-28 18:21:55 +00:00
|
|
|
int local_ip_family, audio_ip_family;
|
2018-06-07 16:51:31 +00:00
|
|
|
const char *codec;
|
|
|
|
unsigned int pt;
|
|
|
|
|
2023-03-30 14:53:13 +00:00
|
|
|
#define MSGB_PRINTF_OR_RET(FMT, ARGS...) do { \
|
|
|
|
if (msgb_printf(msg, FMT, ##ARGS) != 0) { \
|
|
|
|
LOGPMGW(mgcp, LOGL_ERROR, "Message buffer too small, can not generate MGCP message (SDP)\n"); \
|
|
|
|
return -ENOBUFS; \
|
|
|
|
} \
|
|
|
|
} while (0)
|
|
|
|
|
2018-06-07 16:51:31 +00:00
|
|
|
/* Add separator to mark the beginning of the SDP block */
|
2023-03-30 14:53:13 +00:00
|
|
|
MSGB_PRINTF_OR_RET("\r\n");
|
2018-06-07 16:51:31 +00:00
|
|
|
|
|
|
|
/* Add SDP protocol version */
|
2023-03-30 14:53:13 +00:00
|
|
|
MSGB_PRINTF_OR_RET("v=0\r\n");
|
2018-06-07 16:51:31 +00:00
|
|
|
|
|
|
|
/* Determine local IP-Address */
|
|
|
|
if (osmo_sock_local_ip(local_ip, mgcp->actual.remote_addr) < 0) {
|
2021-09-03 15:50:51 +00:00
|
|
|
LOGPMGW(mgcp, LOGL_ERROR,
|
|
|
|
"Could not determine local IP-Address!\n");
|
2021-06-11 13:16:13 +00:00
|
|
|
return -EINVAL;
|
2018-06-07 16:51:31 +00:00
|
|
|
}
|
2020-08-28 18:21:55 +00:00
|
|
|
local_ip_family = osmo_ip_str_type(local_ip);
|
2023-03-30 14:53:13 +00:00
|
|
|
if (local_ip_family == AF_UNSPEC)
|
2021-06-11 13:16:13 +00:00
|
|
|
return -EINVAL;
|
2018-06-07 16:51:31 +00:00
|
|
|
|
|
|
|
/* Add owner/creator (SDP) */
|
2023-03-30 14:53:13 +00:00
|
|
|
MSGB_PRINTF_OR_RET("o=- %x 23 IN IP%c %s\r\n", mgcp_msg->call_id,
|
2020-08-28 18:21:55 +00:00
|
|
|
local_ip_family == AF_INET6 ? '6' : '4',
|
|
|
|
local_ip);
|
2018-06-07 16:51:31 +00:00
|
|
|
|
|
|
|
/* Add session name (none) */
|
2023-03-30 14:53:13 +00:00
|
|
|
MSGB_PRINTF_OR_RET("s=-\r\n");
|
2018-06-07 16:51:31 +00:00
|
|
|
|
2023-11-28 17:28:44 +00:00
|
|
|
/* Add RTP address */
|
|
|
|
if (mgcp_msg->presence & MGCP_MSG_PRESENCE_AUDIO_IP) {
|
|
|
|
audio_ip_family = osmo_ip_str_type(mgcp_msg->audio_ip);
|
|
|
|
if (audio_ip_family == AF_UNSPEC)
|
|
|
|
return -EINVAL;
|
|
|
|
if (strlen(mgcp_msg->audio_ip) <= 0) {
|
|
|
|
LOGPMGW(mgcp, LOGL_ERROR,
|
|
|
|
"Empty ip address, can not generate MGCP message\n");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
MSGB_PRINTF_OR_RET("c=IN IP%c %s\r\n",
|
|
|
|
audio_ip_family == AF_INET6 ? '6' : '4',
|
|
|
|
mgcp_msg->audio_ip);
|
2018-06-07 16:51:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Add time description, active time (SDP) */
|
2023-03-30 14:53:13 +00:00
|
|
|
MSGB_PRINTF_OR_RET("t=0 0\r\n");
|
2018-06-07 16:51:31 +00:00
|
|
|
|
2023-11-28 17:28:44 +00:00
|
|
|
/* Add RTP address port and codecs */
|
|
|
|
if (mgcp_msg->presence & MGCP_MSG_PRESENCE_AUDIO_PORT) {
|
|
|
|
if (mgcp_msg->audio_port == 0) {
|
|
|
|
LOGPMGW(mgcp, LOGL_ERROR,
|
|
|
|
"Invalid port number, can not generate MGCP message\n");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
MSGB_PRINTF_OR_RET("m=audio %u RTP/AVP", mgcp_msg->audio_port);
|
|
|
|
for (i = 0; i < mgcp_msg->codecs_len; i++) {
|
|
|
|
pt = map_codec_to_pt(mgcp_msg->ptmap, mgcp_msg->ptmap_len, mgcp_msg->codecs[i]);
|
|
|
|
MSGB_PRINTF_OR_RET(" %u", pt);
|
2018-06-07 16:51:31 +00:00
|
|
|
|
2023-11-28 17:28:44 +00:00
|
|
|
}
|
|
|
|
MSGB_PRINTF_OR_RET("\r\n");
|
2018-06-07 16:51:31 +00:00
|
|
|
}
|
|
|
|
|
2019-03-05 12:56:59 +00:00
|
|
|
/* Add optional codec parameters (fmtp) */
|
|
|
|
if (mgcp_msg->param_present) {
|
|
|
|
for (i = 0; i < mgcp_msg->codecs_len; i++) {
|
|
|
|
/* The following is only applicable for AMR */
|
|
|
|
if (mgcp_msg->codecs[i] != CODEC_AMR_8000_1 && mgcp_msg->codecs[i] != CODEC_AMRWB_16000_1)
|
2023-12-05 12:35:41 +00:00
|
|
|
continue;
|
2019-03-05 12:56:59 +00:00
|
|
|
pt = map_codec_to_pt(mgcp_msg->ptmap, mgcp_msg->ptmap_len, mgcp_msg->codecs[i]);
|
|
|
|
if (mgcp_msg->param.amr_octet_aligned_present && mgcp_msg->param.amr_octet_aligned)
|
2023-03-30 14:53:13 +00:00
|
|
|
MSGB_PRINTF_OR_RET("a=fmtp:%u octet-align=1\r\n", pt);
|
2019-03-05 12:56:59 +00:00
|
|
|
else if (mgcp_msg->param.amr_octet_aligned_present && !mgcp_msg->param.amr_octet_aligned)
|
2023-03-30 14:53:13 +00:00
|
|
|
MSGB_PRINTF_OR_RET("a=fmtp:%u octet-align=0\r\n", pt);
|
2019-03-05 12:56:59 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-06-07 16:51:31 +00:00
|
|
|
for (i = 0; i < mgcp_msg->codecs_len; i++) {
|
|
|
|
pt = map_codec_to_pt(mgcp_msg->ptmap, mgcp_msg->ptmap_len, mgcp_msg->codecs[i]);
|
2019-04-16 15:23:09 +00:00
|
|
|
|
2018-06-07 16:51:31 +00:00
|
|
|
/* Note: Only dynamic payload type from the range 96-127
|
|
|
|
* require to be explained further via rtpmap. All others
|
|
|
|
* are implcitly definedby the number in m=audio */
|
|
|
|
if (pt >= 96 && pt <= 127) {
|
2019-02-27 04:56:53 +00:00
|
|
|
codec = get_value_string_or_null(osmo_mgcpc_codec_names, mgcp_msg->codecs[i]);
|
2018-06-07 16:51:31 +00:00
|
|
|
|
|
|
|
/* Note: Use codec descriptors from enum mgcp_codecs
|
|
|
|
* in mgcp_client only! */
|
2023-03-30 14:53:13 +00:00
|
|
|
if (!codec)
|
2021-06-11 13:16:13 +00:00
|
|
|
return -EINVAL;
|
2019-04-16 15:23:09 +00:00
|
|
|
|
2023-03-30 14:53:13 +00:00
|
|
|
MSGB_PRINTF_OR_RET("a=rtpmap:%u %s\r\n", pt, codec);
|
2018-06-07 16:51:31 +00:00
|
|
|
}
|
|
|
|
}
|
2019-04-16 15:23:09 +00:00
|
|
|
|
2018-06-07 16:51:31 +00:00
|
|
|
if (mgcp_msg->ptime)
|
2023-03-30 14:53:13 +00:00
|
|
|
MSGB_PRINTF_OR_RET("a=ptime:%u\r\n", mgcp_msg->ptime);
|
2021-06-11 13:16:13 +00:00
|
|
|
|
|
|
|
return 0;
|
2023-03-30 14:53:13 +00:00
|
|
|
#undef MSGB_PRINTF_OR_RET
|
2018-06-07 16:51:31 +00:00
|
|
|
}
|
|
|
|
|
2018-01-19 23:58:16 +00:00
|
|
|
/*! Generate an MGCP message
|
|
|
|
* \param[in] mgcp MGCP client descriptor.
|
|
|
|
* \param[in] mgcp_msg Message description
|
|
|
|
* \returns message buffer on success, NULL on error. */
|
2017-10-05 16:25:37 +00:00
|
|
|
struct msgb *mgcp_msg_gen(struct mgcp_client *mgcp, struct mgcp_msg *mgcp_msg)
|
|
|
|
{
|
|
|
|
mgcp_trans_id_t trans_id = mgcp_client_next_trans_id(mgcp);
|
|
|
|
uint32_t mandatory_mask;
|
|
|
|
struct msgb *msg = msgb_alloc_headroom(4096, 128, "MGCP tx");
|
2018-06-07 16:51:31 +00:00
|
|
|
bool use_sdp = false;
|
2019-04-24 20:06:22 +00:00
|
|
|
char buf[32];
|
2017-10-05 16:25:37 +00:00
|
|
|
|
2023-03-30 14:53:13 +00:00
|
|
|
#define MSGB_PRINTF_OR_RET(FMT, ARGS...) do { \
|
|
|
|
if (msgb_printf(msg, FMT, ##ARGS) != 0) { \
|
|
|
|
LOGPMGW(mgcp, LOGL_ERROR, "Message buffer too small, can not generate MGCP/SDP message\n"); \
|
|
|
|
goto exit_error; \
|
|
|
|
} \
|
|
|
|
} while (0)
|
|
|
|
|
2017-10-05 16:25:37 +00:00
|
|
|
msg->l2h = msg->data;
|
|
|
|
msg->cb[MSGB_CB_MGCP_TRANS_ID] = trans_id;
|
|
|
|
|
|
|
|
/* Add command verb */
|
|
|
|
switch (mgcp_msg->verb) {
|
|
|
|
case MGCP_VERB_CRCX:
|
|
|
|
mandatory_mask = MGCP_CRCX_MANDATORY;
|
2023-03-30 14:53:13 +00:00
|
|
|
MSGB_PRINTF_OR_RET("CRCX %u", trans_id);
|
2017-10-05 16:25:37 +00:00
|
|
|
break;
|
|
|
|
case MGCP_VERB_MDCX:
|
|
|
|
mandatory_mask = MGCP_MDCX_MANDATORY;
|
2023-03-30 14:53:13 +00:00
|
|
|
MSGB_PRINTF_OR_RET("MDCX %u", trans_id);
|
2017-10-05 16:25:37 +00:00
|
|
|
break;
|
|
|
|
case MGCP_VERB_DLCX:
|
|
|
|
mandatory_mask = MGCP_DLCX_MANDATORY;
|
2023-03-30 14:53:13 +00:00
|
|
|
MSGB_PRINTF_OR_RET("DLCX %u", trans_id);
|
2017-10-05 16:25:37 +00:00
|
|
|
break;
|
|
|
|
case MGCP_VERB_AUEP:
|
|
|
|
mandatory_mask = MGCP_AUEP_MANDATORY;
|
2023-03-30 14:53:13 +00:00
|
|
|
MSGB_PRINTF_OR_RET("AUEP %u", trans_id);
|
2017-10-05 16:25:37 +00:00
|
|
|
break;
|
|
|
|
case MGCP_VERB_RSIP:
|
|
|
|
mandatory_mask = MGCP_RSIP_MANDATORY;
|
2023-03-30 14:53:13 +00:00
|
|
|
MSGB_PRINTF_OR_RET("RSIP %u", trans_id);
|
2017-10-05 16:25:37 +00:00
|
|
|
break;
|
|
|
|
default:
|
2021-09-03 15:50:51 +00:00
|
|
|
LOGPMGW(mgcp, LOGL_ERROR, "Invalid command verb, can not generate MGCP message\n");
|
2023-03-30 14:53:13 +00:00
|
|
|
goto exit_error;
|
2017-10-05 16:25:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Check if mandatory fields are missing */
|
|
|
|
if (!((mgcp_msg->presence & mandatory_mask) == mandatory_mask)) {
|
2021-09-03 15:50:51 +00:00
|
|
|
LOGPMGW(mgcp, LOGL_ERROR,
|
|
|
|
"One or more missing mandatory fields, can not generate MGCP message\n");
|
2023-03-30 14:53:13 +00:00
|
|
|
goto exit_error;
|
2017-10-05 16:25:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Add endpoint name */
|
2017-12-10 21:52:22 +00:00
|
|
|
if (mgcp_msg->presence & MGCP_MSG_PRESENCE_ENDPOINT) {
|
|
|
|
if (strlen(mgcp_msg->endpoint) <= 0) {
|
2021-09-03 15:50:51 +00:00
|
|
|
LOGPMGW(mgcp, LOGL_ERROR, "Empty endpoint name, can not generate MGCP message\n");
|
2023-03-30 14:53:13 +00:00
|
|
|
goto exit_error;
|
2017-12-10 21:52:22 +00:00
|
|
|
}
|
2018-01-31 13:13:45 +00:00
|
|
|
|
|
|
|
if (strstr(mgcp_msg->endpoint, "@") == NULL) {
|
2021-09-03 15:50:51 +00:00
|
|
|
LOGPMGW(mgcp, LOGL_ERROR,
|
|
|
|
"Endpoint name (%s) lacks separator (@), can not generate MGCP message\n",
|
|
|
|
mgcp_msg->endpoint);
|
2023-03-30 14:53:13 +00:00
|
|
|
goto exit_error;
|
2018-01-31 13:13:45 +00:00
|
|
|
}
|
|
|
|
|
2023-03-30 14:53:13 +00:00
|
|
|
MSGB_PRINTF_OR_RET(" %s", mgcp_msg->endpoint);
|
2017-12-10 21:52:22 +00:00
|
|
|
}
|
2017-10-05 16:25:37 +00:00
|
|
|
|
|
|
|
/* Add protocol version */
|
2023-03-30 14:53:13 +00:00
|
|
|
MSGB_PRINTF_OR_RET(" MGCP 1.0\r\n");
|
2017-10-05 16:25:37 +00:00
|
|
|
|
|
|
|
/* Add call id */
|
|
|
|
if (mgcp_msg->presence & MGCP_MSG_PRESENCE_CALL_ID)
|
2023-03-30 14:53:13 +00:00
|
|
|
MSGB_PRINTF_OR_RET("C: %x\r\n", mgcp_msg->call_id);
|
2017-10-05 16:25:37 +00:00
|
|
|
|
|
|
|
/* Add connection id */
|
2017-12-10 21:52:22 +00:00
|
|
|
if (mgcp_msg->presence & MGCP_MSG_PRESENCE_CONN_ID) {
|
|
|
|
if (strlen(mgcp_msg->conn_id) <= 0) {
|
2021-09-03 15:50:51 +00:00
|
|
|
LOGPMGW(mgcp, LOGL_ERROR, "Empty connection id, can not generate MGCP message\n");
|
2023-03-30 14:53:13 +00:00
|
|
|
goto exit_error;
|
2017-12-10 21:52:22 +00:00
|
|
|
}
|
2023-03-30 14:53:13 +00:00
|
|
|
MSGB_PRINTF_OR_RET("I: %s\r\n", mgcp_msg->conn_id);
|
2017-12-10 21:52:22 +00:00
|
|
|
}
|
2017-10-05 16:25:37 +00:00
|
|
|
|
2023-11-28 17:28:44 +00:00
|
|
|
/* Using SDP makes sense when a valid IP or Port is specified,
|
2018-06-07 16:51:31 +00:00
|
|
|
* if we do not know this information yet, we fall back to LCO */
|
|
|
|
if (mgcp_msg->presence & MGCP_MSG_PRESENCE_AUDIO_IP
|
2023-11-28 17:28:44 +00:00
|
|
|
|| mgcp_msg->presence & MGCP_MSG_PRESENCE_AUDIO_PORT)
|
2018-06-07 16:51:31 +00:00
|
|
|
use_sdp = true;
|
|
|
|
|
|
|
|
/* Add local connection options (LCO) */
|
|
|
|
if (!use_sdp
|
|
|
|
&& (mgcp_msg->verb == MGCP_VERB_CRCX
|
2021-06-11 13:16:13 +00:00
|
|
|
|| mgcp_msg->verb == MGCP_VERB_MDCX)) {
|
2023-02-22 15:30:02 +00:00
|
|
|
if (add_lco(msg, mgcp_msg) < 0) {
|
|
|
|
LOGPMGW(mgcp, LOGL_ERROR, "Failed to add LCO, can not generate MGCP message\n");
|
2023-03-30 14:53:13 +00:00
|
|
|
goto exit_error;
|
2023-02-22 15:30:02 +00:00
|
|
|
}
|
2021-06-11 13:16:13 +00:00
|
|
|
}
|
2017-10-05 16:25:37 +00:00
|
|
|
|
|
|
|
/* Add mode */
|
|
|
|
if (mgcp_msg->presence & MGCP_MSG_PRESENCE_CONN_MODE)
|
2023-03-30 14:53:13 +00:00
|
|
|
MSGB_PRINTF_OR_RET("M: %s\r\n", mgcp_client_cmode_name(mgcp_msg->conn_mode));
|
2017-10-05 16:25:37 +00:00
|
|
|
|
2018-08-23 14:36:48 +00:00
|
|
|
/* Add X-Osmo-IGN */
|
|
|
|
if ((mgcp_msg->presence & MGCP_MSG_PRESENCE_X_OSMO_IGN)
|
|
|
|
&& (mgcp_msg->x_osmo_ign != 0))
|
2023-03-30 14:53:13 +00:00
|
|
|
MSGB_PRINTF_OR_RET(MGCP_X_OSMO_IGN_HEADER "%s\r\n",
|
|
|
|
mgcp_msg->x_osmo_ign & MGCP_X_OSMO_IGN_CALLID ? " C" : "");
|
2018-08-23 14:36:48 +00:00
|
|
|
|
2019-04-24 20:06:22 +00:00
|
|
|
/* Add X-Osmo-Osmux */
|
|
|
|
if ((mgcp_msg->presence & MGCP_MSG_PRESENCE_X_OSMO_OSMUX_CID)) {
|
2019-05-23 14:48:24 +00:00
|
|
|
if (mgcp_msg->x_osmo_osmux_cid < -1 || mgcp_msg->x_osmo_osmux_cid > OSMUX_CID_MAX) {
|
2021-09-03 15:50:51 +00:00
|
|
|
LOGPMGW(mgcp, LOGL_ERROR, "Wrong Osmux CID %d, can not generate MGCP message\n",
|
|
|
|
mgcp_msg->x_osmo_osmux_cid);
|
2023-03-30 14:53:13 +00:00
|
|
|
goto exit_error;
|
2019-05-23 14:48:24 +00:00
|
|
|
}
|
2019-04-24 20:06:22 +00:00
|
|
|
snprintf(buf, sizeof(buf), " %d", mgcp_msg->x_osmo_osmux_cid);
|
2023-03-30 14:53:13 +00:00
|
|
|
MSGB_PRINTF_OR_RET(MGCP_X_OSMO_OSMUX_HEADER "%s\r\n",
|
|
|
|
mgcp_msg->x_osmo_osmux_cid == -1 ? " *" : buf);
|
2019-04-24 20:06:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-06-07 16:51:31 +00:00
|
|
|
/* Add session description protocol (SDP) */
|
|
|
|
if (use_sdp
|
|
|
|
&& (mgcp_msg->verb == MGCP_VERB_CRCX
|
|
|
|
|| mgcp_msg->verb == MGCP_VERB_MDCX)) {
|
2023-02-22 15:30:02 +00:00
|
|
|
if (add_sdp(msg, mgcp_msg, mgcp) < 0) {
|
|
|
|
LOGPMGW(mgcp, LOGL_ERROR, "Failed to add SDP, can not generate MGCP message\n");
|
2023-03-30 14:53:13 +00:00
|
|
|
goto exit_error;
|
2023-02-22 15:30:02 +00:00
|
|
|
}
|
2017-10-05 16:25:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return msg;
|
2023-03-30 14:53:13 +00:00
|
|
|
|
|
|
|
exit_error:
|
|
|
|
msgb_free(msg);
|
|
|
|
return NULL;
|
|
|
|
#undef MSGB_PRINTF_OR_RET
|
2017-10-05 16:25:37 +00:00
|
|
|
}
|
|
|
|
|
2018-01-19 23:58:16 +00:00
|
|
|
/*! Retrieve the MGCP transaction ID from a msgb generated by mgcp_msg_gen()
|
|
|
|
* \param[in] msg message buffer
|
|
|
|
* \returns Transaction id. */
|
mgcp_client: add transaction cleanup
So far, if an MGCP message is sent, the transaction gets enqueued, but there is
no way to end the transaction other than receiving a valid reply. So, if the
caller decides that the transaction timed out and tears down the priv pointer
passed to mgcp_client_tx, and if then a late reply arrives, the callback will
dereference the invalid priv pointer and cause a segfault. Hence it is possible
to crash an mgcp_client program by sending a late response.
Furthermore, if no reply ever arrives, we would keep the pending response in
the list forever, amounting to a "memory leak".
Add mgcp_client_cancel() to discard a pending transaction. The caller can now
decide to discard a pending response when it sees fit (e.g. the caller's
timeout expired). This needs to be added to OsmoMSC and OsmoBSC.
Add mgcp_msg_trans_id() to provide an obvious way to obtain the transaction id
from a generated MGCP message.
No public API is broken; but refine the negative return code from
mgcp_client_rx(): return -ENOENT if no such transaction ID is found, and still
-1 if decoding failed. This is mainly for mgcp_client_test.
Implement a test for mgcp_client_cancel() in mgcp_client_test.c.
Tweak internal mgcp_client_response_pending_get() to take only the transaction
id as argument instead of the entire mgcp message struct.
Found-by: dexter
Related: OS#2695 OS#2696
Change-Id: I16811e168a46a82a05943252a737b3434143f4bd
2017-11-30 12:43:11 +00:00
|
|
|
mgcp_trans_id_t mgcp_msg_trans_id(struct msgb *msg)
|
|
|
|
{
|
|
|
|
return (mgcp_trans_id_t)msg->cb[MSGB_CB_MGCP_TRANS_ID];
|
|
|
|
}
|
|
|
|
|
2021-09-02 08:08:56 +00:00
|
|
|
/*! Get the configuration parameters for a given MGCP client instance
|
2018-01-19 23:58:16 +00:00
|
|
|
* \param[in] mgcp MGCP client descriptor.
|
|
|
|
* \returns configuration */
|
2017-09-03 23:02:56 +00:00
|
|
|
struct mgcp_client_conf *mgcp_client_conf_actual(struct mgcp_client *mgcp)
|
2017-07-10 13:07:22 +00:00
|
|
|
{
|
|
|
|
return &mgcp->actual;
|
|
|
|
}
|
libosmo-mgcp-client: fix debian, make self-contained
Add mgcp_common.h to libosmo-mgcp-client, to not need to share a header file
with libosmo-legacy-mgcp (nor the upcoming libosmo-mgcp). Both libraries use
the enum mgcp_connection_mode (and a for-loop macro and a string mangling
function), so far declared in mgcp.h, and both mgcp-dev and mgcp-client-dev
debian packages would require this header file to be installed. So far the
mgcp-client-dev lacks this header file, which causes the osmo-msc debian
package to fail building. Ways to solve:
- If both -dev debian packages installed the same header file in the same
place, they would conflict if ever installed at the same time.
- mgcp-client-dev can depend on mgcp-dev, but it seems bad to keep such a large
dependency for just one enum and two helpers.
- Instead, this patch solves this by copying the few definitions to
libosmo-mgcp-client.
Once libosmo-mgcp-client has its own copy of those definitions, it is
fully self-contained and depending builds (osmo-msc deb) will succeed.
Copy the few actually common definitions to new header
<osmocom/mgcp_client/mgcp_common.h>. The nature of this .h is that it may be
shared with future libosmo-mgcp without causing linking problems.
Remove libosmo-legacy-mgcp/mgcp_common.c file from the build of
libosmo-mgcp-client, no longer needed.
Add to new mgcp_common.h:
- enum mgcp_connection_mode;
- for_each_non_empty_line() macro.
- mgcp_msg_terminate_nul() as static function. Its complexity is just above
your average static inline, but being inline is a way to use it in both mgcp
and mgcp_client without linking problems.
Replace for_each_line() use in mgcp_client with for_each_non_empty_line()
(for_each_non_empty_line() replaces for_each_line() and uses strtok_r() instead
of a local reinvention).
mgcp_connection_mode_strs are actually only used in libosmo-mgcp-client, so
rename to mgcp_client_ prefix and move to mgcp_client.c.
BTW, the future plan for upcoming libosmo-mgcp is to use the identical header
file, and keep only one copy in the git source tree. The second copy may be
generated during 'make', to avoid code dup while having two distinct headers.
Related: I8e3359bedf973077c0a038aa04f5371a00c48fa0 (fix osmo-msc after this),
I7a5d3b9a2eb90be7e34b95efa529429f2e6c3ed8 (mgcp_common.h)
Change-Id: Ifb8f3fc2b399662a9dbba174e942352a1a21df3f
2017-09-21 22:52:54 +00:00
|
|
|
|
|
|
|
const struct value_string mgcp_client_connection_mode_strs[] = {
|
|
|
|
{ MGCP_CONN_NONE, "none" },
|
|
|
|
{ MGCP_CONN_RECV_SEND, "sendrecv" },
|
|
|
|
{ MGCP_CONN_SEND_ONLY, "sendonly" },
|
|
|
|
{ MGCP_CONN_RECV_ONLY, "recvonly" },
|
2023-07-05 10:36:49 +00:00
|
|
|
{ MGCP_CONN_CONFECHO, "confecho" },
|
libosmo-mgcp-client: fix debian, make self-contained
Add mgcp_common.h to libosmo-mgcp-client, to not need to share a header file
with libosmo-legacy-mgcp (nor the upcoming libosmo-mgcp). Both libraries use
the enum mgcp_connection_mode (and a for-loop macro and a string mangling
function), so far declared in mgcp.h, and both mgcp-dev and mgcp-client-dev
debian packages would require this header file to be installed. So far the
mgcp-client-dev lacks this header file, which causes the osmo-msc debian
package to fail building. Ways to solve:
- If both -dev debian packages installed the same header file in the same
place, they would conflict if ever installed at the same time.
- mgcp-client-dev can depend on mgcp-dev, but it seems bad to keep such a large
dependency for just one enum and two helpers.
- Instead, this patch solves this by copying the few definitions to
libosmo-mgcp-client.
Once libosmo-mgcp-client has its own copy of those definitions, it is
fully self-contained and depending builds (osmo-msc deb) will succeed.
Copy the few actually common definitions to new header
<osmocom/mgcp_client/mgcp_common.h>. The nature of this .h is that it may be
shared with future libosmo-mgcp without causing linking problems.
Remove libosmo-legacy-mgcp/mgcp_common.c file from the build of
libosmo-mgcp-client, no longer needed.
Add to new mgcp_common.h:
- enum mgcp_connection_mode;
- for_each_non_empty_line() macro.
- mgcp_msg_terminate_nul() as static function. Its complexity is just above
your average static inline, but being inline is a way to use it in both mgcp
and mgcp_client without linking problems.
Replace for_each_line() use in mgcp_client with for_each_non_empty_line()
(for_each_non_empty_line() replaces for_each_line() and uses strtok_r() instead
of a local reinvention).
mgcp_connection_mode_strs are actually only used in libosmo-mgcp-client, so
rename to mgcp_client_ prefix and move to mgcp_client.c.
BTW, the future plan for upcoming libosmo-mgcp is to use the identical header
file, and keep only one copy in the git source tree. The second copy may be
generated during 'make', to avoid code dup while having two distinct headers.
Related: I8e3359bedf973077c0a038aa04f5371a00c48fa0 (fix osmo-msc after this),
I7a5d3b9a2eb90be7e34b95efa529429f2e6c3ed8 (mgcp_common.h)
Change-Id: Ifb8f3fc2b399662a9dbba174e942352a1a21df3f
2017-09-21 22:52:54 +00:00
|
|
|
{ MGCP_CONN_LOOPBACK, "loopback" },
|
|
|
|
{ 0, NULL }
|
|
|
|
};
|
2021-09-03 15:50:51 +00:00
|
|
|
|
|
|
|
/*! Get MGCP client instance name (VTY).
|
|
|
|
* \param[in] mgcp MGCP client descriptor.
|
|
|
|
* \returns MGCP client name.
|
|
|
|
*
|
|
|
|
* The user can only modify the name of an MGCP client instance when it is
|
|
|
|
* part of a pool. For single MGCP client instances and MGCP client instance
|
|
|
|
* where no description is set via the VTY, the MGW domain name will be used
|
|
|
|
* as name. */
|
|
|
|
const char *mgcp_client_name(const struct mgcp_client *mgcp)
|
|
|
|
{
|
|
|
|
if (!mgcp)
|
|
|
|
return "(null)";
|
|
|
|
|
|
|
|
if (mgcp->actual.description)
|
|
|
|
return mgcp->actual.description;
|
|
|
|
else
|
|
|
|
return mgcp_client_endpoint_domain(mgcp);
|
|
|
|
}
|