gsm0808_enc_aoip_trasp_addr: add length check

Instead of running into OSMO_ASSERT in msgb_put(), return early if the
msgb is too small. As suggested by Pau in [1].

I would have returned -EMSGSIZE, but the function returns uint8_t.

[1]: https://gerrit.osmocom.org/c/osmo-bsc-nat/+/28582/2#message-a183c463ea20a8d958465ce45a83e62c46214cf6

Change-Id: I632986b99d841abff0f14c6da65f030175f5c4a1
diff --git a/src/gsm/gsm0808_utils.c b/src/gsm/gsm0808_utils.c
index 040872b..f1569c9 100644
--- a/src/gsm/gsm0808_utils.c
+++ b/src/gsm/gsm0808_utils.c
@@ -78,7 +78,7 @@
 /*! Encode TS 08.08 AoIP transport address IE
  *  \param[out] msg Message Buffer to which to append IE
  *  \param[in] ss Socket Address to be used in IE
- *  \returns number of bytes added to \a msg */
+ *  \returns number of bytes added to \a msg; 0 if msg is too small */
 uint8_t gsm0808_enc_aoip_trasp_addr(struct msgb *msg,
 				    const struct sockaddr_storage *ss)
 {
@@ -87,16 +87,27 @@
 	struct sockaddr_in6 *sin6;
 	uint16_t port = 0;
 	uint8_t *ptr;
-	uint8_t *old_tail;
-	uint8_t *tlv_len;
+	const uint8_t len_tl = 2;
+	uint8_t len_v = sizeof(port);
 
 	OSMO_ASSERT(msg);
 	OSMO_ASSERT(ss);
 	OSMO_ASSERT(ss->ss_family == AF_INET || ss->ss_family == AF_INET6);
 
+	switch (ss->ss_family) {
+	case AF_INET:
+		len_v += IP_V4_ADDR_LEN;
+		break;
+	case AF_INET6:
+		len_v += IP_V6_ADDR_LEN;
+		break;
+	}
+
+	if (msgb_tailroom(msg) < len_tl + len_v)
+		return 0;
+
 	msgb_put_u8(msg, GSM0808_IE_AOIP_TRASP_ADDR);
-	tlv_len = msgb_put(msg,1);
-	old_tail = msg->tail;
+	msgb_put_u8(msg, len_v);
 
 	switch (ss->ss_family) {
 	case AF_INET:
@@ -114,9 +125,7 @@
 	}
 
 	msgb_put_u16(msg, port);
-
-	*tlv_len = (uint8_t) (msg->tail - old_tail);
-	return *tlv_len + 2;
+	return len_tl + len_v;
 }
 
 /*! Decode TS 08.08 AoIP transport address IE
diff --git a/tests/gsm0808/gsm0808_test.c b/tests/gsm0808/gsm0808_test.c
index 158aeba..b3aa3ab 100644
--- a/tests/gsm0808/gsm0808_test.c
+++ b/tests/gsm0808/gsm0808_test.c
@@ -924,6 +924,28 @@
 	msgb_free(msg);
 }
 
+static void test_enc_aoip_trasp_addr_msg_too_small()
+{
+	struct msgb *msg;
+	struct sockaddr_storage enc_addr;
+	struct sockaddr_in enc_addr_in;
+	uint8_t rc_enc;
+
+	memset(&enc_addr_in, 0, sizeof(enc_addr_in));
+	enc_addr_in.sin_family = AF_INET;
+	enc_addr_in.sin_port = htons(1234);
+	inet_aton("255.0.255.255", &enc_addr_in.sin_addr);
+
+	memset(&enc_addr, 0, sizeof(enc_addr));
+	memcpy(&enc_addr, &enc_addr_in, sizeof(enc_addr_in));
+
+	msg = msgb_alloc(7, "output buffer");
+	rc_enc = gsm0808_enc_aoip_trasp_addr(msg, &enc_addr);
+	OSMO_ASSERT(rc_enc == 0);
+
+	msgb_free(msg);
+}
+
 static void test_gsm0808_enc_dec_speech_codec()
 {
 	struct gsm0808_speech_codec enc_sc = {
@@ -2540,6 +2562,7 @@
 
 	test_enc_dec_aoip_trasp_addr_v4();
 	test_enc_dec_aoip_trasp_addr_v6();
+	test_enc_aoip_trasp_addr_msg_too_small();
 	test_gsm0808_enc_dec_speech_codec();
 	test_gsm0808_enc_dec_speech_codec_ext_with_cfg();
 	test_gsm0808_enc_dec_speech_codec_with_cfg();