libosmo-mgcp: Connection Identifiers are allocated by MGW, not CA

The MGCP connection identifier is allocated by the MGW while processing
the CRCX, see RFC3435 2.1.3.2:. Including/Accepting a connection
identifier in CRCX is "forbidden" as per RFC3435 Section 3.2.2.

So the MGW side must *reject* a CRCX message with 'I' parameter, and
allocate a connection identifier which is subsequently returned in the
response.

Closes: OS#2648
Change-Id: Iab6a6038e7610c62f34e642cd49c93d11151252c
diff --git a/src/libosmo-mgcp-client/mgcp_client.c b/src/libosmo-mgcp-client/mgcp_client.c
index a89da99..7e83f95 100644
--- a/src/libosmo-mgcp-client/mgcp_client.c
+++ b/src/libosmo-mgcp-client/mgcp_client.c
@@ -257,6 +257,58 @@
 	return 0;
 }
 
+/* Parse a line like "I: 0cedfd5a19542d197af9afe5231f1d61" */
+static int mgcp_parse_conn_id(struct mgcp_response *r, const char *line)
+{
+	if (strlen(line) < 4)
+		goto response_parse_failure;
+
+	if (memcmp("I: ", line, 3) != 0)
+		goto response_parse_failure;
+
+	osmo_strlcpy(r->head.conn_id, line + 3, sizeof(r->head.conn_id));
+	return 0;
+
+response_parse_failure:
+	LOGP(DLMGCP, LOGL_ERROR,
+	     "Failed to parse MGCP response (connectionIdentifier)\n");
+	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);
+	char *data = r->body;
+	char *data_end = strstr(r->body, "\n\n");
+
+	/* Protect SDP body, for_each_non_empty_line() will
+	 * only parse until it hits \0 mark. */
+	if (data_end)
+		*data_end = '\0';
+
+	for_each_non_empty_line(line, data) {
+		switch (line[0]) {
+		case 'I':
+			rc = mgcp_parse_conn_id(r, line);
+			if (rc)
+				goto exit;
+			break;
+		default:
+			/* skip unhandled parameters */
+			break;
+		}
+	}
+exit:
+	/* Restore original state */
+	if (data_end)
+		*data_end = '\n';
+
+	return rc;
+}
+
 static struct mgcp_response_pending *mgcp_client_response_pending_get(
 					 struct mgcp_client *mgcp,
 					 struct mgcp_response *r)
@@ -287,7 +339,13 @@
 
 	rc = mgcp_response_parse_head(&r, msg);
 	if (rc) {
-		LOGP(DLMGCP, LOGL_ERROR, "Cannot parse MGCP response\n");
+		LOGP(DLMGCP, LOGL_ERROR, "Cannot parse MGCP response (head)\n");
+		return -1;
+	}
+
+	rc = parse_head_params(&r);
+	if (rc) {
+		LOGP(DLMGCP, LOGL_ERROR, "Cannot parse MGCP response (head parameters)\n");
 		return -1;
 	}
 
@@ -648,7 +706,6 @@
 
 #define MGCP_CRCX_MANDATORY (MGCP_MSG_PRESENCE_ENDPOINT | \
 			     MGCP_MSG_PRESENCE_CALL_ID | \
-			     MGCP_MSG_PRESENCE_CONN_ID | \
 			     MGCP_MSG_PRESENCE_CONN_MODE)
 #define MGCP_MDCX_MANDATORY (MGCP_MSG_PRESENCE_ENDPOINT | \
 			     MGCP_MSG_PRESENCE_CONN_ID)
@@ -719,8 +776,7 @@
 		rc += msgb_printf(msg, "I: %s\r\n", mgcp_msg->conn_id);
 
 	/* Add local connection options */
-	if (mgcp_msg->presence & MGCP_MSG_PRESENCE_CONN_ID
-	    && mgcp_msg->verb == MGCP_VERB_CRCX)
+	if (mgcp_msg->verb == MGCP_VERB_CRCX)
 		rc += msgb_printf(msg, "L: p:20, a:AMR, nt:IN\r\n");
 
 	/* Add mode */