Skip to content

Commit bf9c95e

Browse files
morbit85andywolk
andauthored
[core] Use switch_stun_ipv6_t for STUN IPv6 write paths. (#3037)
Route IPv6 writes in `switch_stun_packet_attribute_add_binded_address` and `switch_stun_packet_attribute_add_xor_binded_address` through `switch_stun_ipv6_t` (16-byte `address[]`) instead of `switch_stun_ip_t` (4-byte `uint32_t address`). Add IPv4/IPv6 unit tests for both encoders. Co-authored-by: Andrey Volk <andywolk@gmail.com>
1 parent 9da537a commit bf9c95e

4 files changed

Lines changed: 265 additions & 33 deletions

File tree

src/switch_stun.c

Lines changed: 19 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -485,31 +485,24 @@ SWITCH_DECLARE(switch_stun_packet_t *) switch_stun_packet_build_header(switch_st
485485
SWITCH_DECLARE(uint8_t) switch_stun_packet_attribute_add_binded_address(switch_stun_packet_t *packet, char *ipstr, uint16_t port, int family)
486486
{
487487
switch_stun_packet_attribute_t *attribute;
488-
switch_stun_ip_t *ip;
489488

490489
attribute = (switch_stun_packet_attribute_t *) ((uint8_t *) & packet->first_attribute + ntohs(packet->header.length));
491490
attribute->type = htons(SWITCH_STUN_ATTR_XOR_MAPPED_ADDRESS);
492491

493492
if (family == AF_INET6) {
493+
switch_stun_ipv6_t *ipv6 = (switch_stun_ipv6_t *) attribute->value;
494+
494495
attribute->length = htons(20);
496+
ipv6->family = 2;
497+
ipv6->port = htons(port ^ (STUN_MAGIC_COOKIE >> 16));
498+
inet_pton(AF_INET6, ipstr, ipv6->address);
495499
} else {
496-
attribute->length = htons(8);
497-
}
498-
499-
ip = (switch_stun_ip_t *) attribute->value;
500-
501-
ip->port = htons(port ^ (STUN_MAGIC_COOKIE >> 16));
500+
switch_stun_ip_t *ip = (switch_stun_ip_t *) attribute->value;
502501

503-
if (family == AF_INET6) {
504-
ip->family = 2;
505-
} else {
502+
attribute->length = htons(8);
506503
ip->family = 1;
507-
}
508-
509-
if (family == AF_INET6) {
510-
inet_pton(AF_INET6, ipstr, (struct in6_addr *) &ip->address);
511-
} else {
512-
inet_pton(AF_INET, ipstr, (int *) &ip->address);
504+
ip->port = htons(port ^ (STUN_MAGIC_COOKIE >> 16));
505+
inet_pton(AF_INET, ipstr, &ip->address);
513506
}
514507

515508
packet->header.length += htons(sizeof(switch_stun_packet_attribute_t)) + attribute->length;
@@ -519,32 +512,25 @@ SWITCH_DECLARE(uint8_t) switch_stun_packet_attribute_add_binded_address(switch_s
519512
SWITCH_DECLARE(uint8_t) switch_stun_packet_attribute_add_xor_binded_address(switch_stun_packet_t *packet, char *ipstr, uint16_t port, int family)
520513
{
521514
switch_stun_packet_attribute_t *attribute;
522-
switch_stun_ip_t *ip;
523515

524516
attribute = (switch_stun_packet_attribute_t *) ((uint8_t *) & packet->first_attribute + ntohs(packet->header.length));
525517
attribute->type = htons(SWITCH_STUN_ATTR_XOR_MAPPED_ADDRESS);
526518

527519
if (family == AF_INET6) {
520+
switch_stun_ipv6_t *ipv6 = (switch_stun_ipv6_t *) attribute->value;
521+
528522
attribute->length = htons(20);
523+
ipv6->family = 2;
524+
ipv6->port = htons(port ^ (STUN_MAGIC_COOKIE >> 16));
525+
inet_pton(AF_INET6, ipstr, ipv6->address);
526+
v6_xor(ipv6->address, (uint8_t *)packet->header.id);
529527
} else {
530-
attribute->length = htons(8);
531-
}
532-
533-
ip = (switch_stun_ip_t *) attribute->value;
534-
535-
ip->port = htons(port ^ (STUN_MAGIC_COOKIE >> 16));
528+
switch_stun_ip_t *ip = (switch_stun_ip_t *) attribute->value;
536529

537-
if (family == AF_INET6) {
538-
ip->family = 2;
539-
} else {
530+
attribute->length = htons(8);
540531
ip->family = 1;
541-
}
542-
543-
if (family == AF_INET6) {
544-
inet_pton(AF_INET6, ipstr, (struct in6_addr *) &ip->address);
545-
v6_xor((uint8_t *)&ip->address, (uint8_t *)packet->header.id);
546-
} else {
547-
inet_pton(AF_INET, ipstr, (int *) &ip->address);
532+
ip->port = htons(port ^ (STUN_MAGIC_COOKIE >> 16));
533+
inet_pton(AF_INET, ipstr, &ip->address);
548534
ip->address = htonl(ntohl(ip->address) ^ STUN_MAGIC_COOKIE);
549535
}
550536

tests/unit/Makefile.am

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ include $(top_srcdir)/build/modmake.rulesam
33
noinst_PROGRAMS = switch_event switch_hash switch_ivr_originate switch_utils switch_core switch_console switch_vpx switch_core_file \
44
switch_ivr_play_say switch_core_codec switch_rtp switch_xml
55
noinst_PROGRAMS += switch_core_video switch_core_db switch_vad switch_packetizer switch_core_session test_sofia switch_ivr_async switch_core_asr switch_log
6+
noinst_PROGRAMS += switch_stun
67
noinst_PROGRAMS += test_tts_format
78
noinst_PROGRAMS+= switch_hold switch_sip
89

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
<?xml version="1.0"?>
2+
<document type="freeswitch/xml">
3+
<X-PRE-PROCESS cmd="exec-set" data="test=echo 1234"/>
4+
<X-PRE-PROCESS cmd="set" data="default_password=$${test}"/>
5+
<X-PRE-PROCESS cmd="set" data="core_video_blank_image=$${conf_dir}/freeswitch-logo.png"/>
6+
<section name="configuration" description="Various Configuration">
7+
<configuration name="modules.conf" description="Modules">
8+
<modules>
9+
<load module="mod_console"/>
10+
<load module="mod_loopback"/>
11+
<load module="mod_commands"/>
12+
<load module="mod_sndfile"/>
13+
<load module="mod_dptools"/>
14+
<load module="mod_tone_stream"/>
15+
<load module="mod_test"/>
16+
<load module="mod_opus"/>
17+
</modules>
18+
</configuration>
19+
20+
<configuration name="console.conf" description="Console Logger">
21+
<mappings>
22+
<map name="all" value="console,debug,info,notice,warning,err,crit,alert"/>
23+
</mappings>
24+
<settings>
25+
<param name="colorize" value="true"/>
26+
<param name="loglevel" value="debug"/>
27+
28+
</settings>
29+
</configuration>
30+
31+
<configuration name="switch.conf" description="Core Configuration">
32+
33+
<default-ptimes>
34+
</default-ptimes>
35+
36+
<settings>
37+
38+
<param name="colorize-console" value="false"/>
39+
<param name="dialplan-timestamps" value="false"/>
40+
<param name="loglevel" value="debug"/>
41+
<param name="rtp-start-port" value="1234"/>
42+
<param name="rtp-end-port" value="1234"/>
43+
44+
</settings>
45+
</configuration>
46+
47+
<configuration name="timezones.conf" description="Timezones">
48+
<timezones>
49+
<zone name="GMT" value="GMT0" />
50+
</timezones>
51+
</configuration>
52+
53+
</section>
54+
55+
<section name="dialplan" description="Regex/XML Dialplan">
56+
<context name="test">
57+
<extension>
58+
<condition field="${sip_h_X-COUNTDOWN}" expression="^0$" break="on-true">
59+
<action application="answer"/>
60+
<action application="playback" data="tone_stream://%(251,0,1004);loops=-1"/>
61+
</condition>
62+
<condition field="${sip_h_X-COUNTDOWN}" expression="^(\d+)$" break="never">
63+
<action application="export" data="_nolocal_sip_h_X-COUNTDOWN=${expr($1 - 1)}"/>
64+
<anti-action application="export" data="_nolocal_sip_h_X-COUNTDOWN=10"/>
65+
</condition>
66+
<condition>
67+
<action application="bridge" data="sofia/test/1234@127.0.0.1"/>
68+
</condition>
69+
</extension>
70+
</context>
71+
72+
</section>
73+
</document>

tests/unit/switch_stun.c

Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
/*
2+
* FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
3+
* Copyright (C) 2005-2026, Anthony Minessale II <anthm@freeswitch.org>
4+
*
5+
* Version: MPL 1.1
6+
*
7+
* The contents of this file are subject to the Mozilla Public License Version
8+
* 1.1 (the "License"); you may not use this file except in compliance with
9+
* the License. You may obtain a copy of the License at
10+
* http://www.mozilla.org/MPL/
11+
*
12+
* Software distributed under the License is distributed on an "AS IS" basis,
13+
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14+
* for the specific language governing rights and limitations under the
15+
* License.
16+
*
17+
* The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
18+
*
19+
* The Initial Developer of the Original Code is
20+
* Anthony Minessale II <anthm@freeswitch.org>
21+
* Portions created by the Initial Developer are Copyright (C)
22+
* the Initial Developer. All Rights Reserved.
23+
*
24+
* Contributor(s):
25+
* Dmitry Verenitsin <morbit85@gmail.com>
26+
*
27+
* switch_stun.c -- tests STUN (https://www.rfc-editor.org/rfc/rfc5389).
28+
*/
29+
30+
31+
#include <switch.h>
32+
#include <switch_stun.h>
33+
#include <test/switch_test.h>
34+
35+
FST_CORE_BEGIN("./conf_stun")
36+
{
37+
FST_SUITE_BEGIN(switch_stun)
38+
{
39+
FST_SETUP_BEGIN()
40+
{
41+
}
42+
FST_SETUP_END()
43+
44+
FST_TEARDOWN_BEGIN()
45+
{
46+
}
47+
FST_TEARDOWN_END()
48+
49+
FST_TEST_BEGIN(test_stun_add_binded_address_ipv6)
50+
{
51+
/*
52+
* Encode an IPv6 XOR-MAPPED-ADDRESS attribute and verify the
53+
* attribute type, length, address family, and the raw 16-byte
54+
* address payload at its expected offset inside the value.
55+
*/
56+
uint8_t buf[512];
57+
switch_stun_packet_t *packet;
58+
switch_stun_packet_attribute_t *attr;
59+
const char *ipv6_str = "2001:db8::dead:beef";
60+
uint8_t expected[16];
61+
uint8_t *value_bytes;
62+
63+
memset(buf, 0, sizeof(buf));
64+
packet = switch_stun_packet_build_header(SWITCH_STUN_BINDING_RESPONSE, NULL, buf);
65+
fst_xcheck(inet_pton(AF_INET6, ipv6_str, expected) == 1, "test IPv6 literal parses");
66+
67+
switch_stun_packet_attribute_add_binded_address(packet, (char *)ipv6_str, 12345, AF_INET6);
68+
69+
attr = (switch_stun_packet_attribute_t *)packet->first_attribute;
70+
fst_xcheck(ntohs(attr->type) == SWITCH_STUN_ATTR_XOR_MAPPED_ADDRESS, "attribute type is XOR_MAPPED_ADDRESS");
71+
fst_xcheck(ntohs(attr->length) == 20, "attribute length is 20 for IPv6");
72+
73+
/* Attribute value layout: wasted(1) + family(1) + port(2) + address(16). */
74+
value_bytes = (uint8_t *)attr->value;
75+
fst_xcheck(value_bytes[1] == 2, "attribute family byte is 2 for IPv6");
76+
fst_xcheck(memcmp(value_bytes + 4, expected, 16) == 0, "16-byte IPv6 address written at offset 4 of attribute value");
77+
}
78+
FST_TEST_END()
79+
80+
FST_TEST_BEGIN(test_stun_add_xor_binded_address_ipv6)
81+
{
82+
/*
83+
* Encode then decode an IPv6 XOR-MAPPED-ADDRESS attribute and
84+
* confirm the round-trip recovers the original IPv6 string —
85+
* the write path must XOR the address with the transaction ID
86+
* symmetrically to the read path.
87+
*/
88+
uint8_t buf[512];
89+
switch_stun_packet_t *packet;
90+
switch_stun_packet_attribute_t *attr;
91+
const char *ipv6_str = "2001:db8::dead:beef";
92+
char out_ip[64] = { 0 };
93+
uint16_t out_port = 0;
94+
95+
memset(buf, 0, sizeof(buf));
96+
packet = switch_stun_packet_build_header(SWITCH_STUN_BINDING_RESPONSE, NULL, buf);
97+
98+
switch_stun_packet_attribute_add_xor_binded_address(packet, (char *)ipv6_str, 12345, AF_INET6);
99+
100+
attr = (switch_stun_packet_attribute_t *)packet->first_attribute;
101+
fst_xcheck(ntohs(attr->type) == SWITCH_STUN_ATTR_XOR_MAPPED_ADDRESS, "attribute type is XOR_MAPPED_ADDRESS");
102+
fst_xcheck(ntohs(attr->length) == 20, "attribute length is 20 for IPv6");
103+
104+
switch_stun_packet_attribute_get_xor_mapped_address(attr, &packet->header, out_ip, sizeof(out_ip), &out_port);
105+
fst_check_string_equals(out_ip, ipv6_str);
106+
}
107+
FST_TEST_END()
108+
109+
FST_TEST_BEGIN(test_stun_add_binded_address_ipv4)
110+
{
111+
/*
112+
* Encode an IPv4 XOR-MAPPED-ADDRESS attribute and verify the
113+
* attribute type, length, address family, and the raw 4-byte
114+
* address payload at its expected offset inside the value.
115+
*/
116+
uint8_t buf[512];
117+
switch_stun_packet_t *packet;
118+
switch_stun_packet_attribute_t *attr;
119+
const char *ipv4_str = "192.0.2.42";
120+
uint8_t expected[4];
121+
uint8_t *value_bytes;
122+
123+
memset(buf, 0, sizeof(buf));
124+
packet = switch_stun_packet_build_header(SWITCH_STUN_BINDING_RESPONSE, NULL, buf);
125+
fst_xcheck(inet_pton(AF_INET, ipv4_str, expected) == 1, "test IPv4 literal parses");
126+
127+
switch_stun_packet_attribute_add_binded_address(packet, (char *)ipv4_str, 12345, AF_INET);
128+
129+
attr = (switch_stun_packet_attribute_t *)packet->first_attribute;
130+
fst_xcheck(ntohs(attr->type) == SWITCH_STUN_ATTR_XOR_MAPPED_ADDRESS, "attribute type is XOR_MAPPED_ADDRESS");
131+
fst_xcheck(ntohs(attr->length) == 8, "attribute length is 8 for IPv4");
132+
133+
/* Attribute value layout: wasted(1) + family(1) + port(2) + address(4). */
134+
value_bytes = (uint8_t *)attr->value;
135+
fst_xcheck(value_bytes[1] == 1, "attribute family byte is 1 for IPv4");
136+
fst_xcheck(memcmp(value_bytes + 4, expected, 4) == 0, "4-byte IPv4 address written at offset 4 of attribute value");
137+
}
138+
FST_TEST_END()
139+
140+
FST_TEST_BEGIN(test_stun_add_xor_binded_address_ipv4)
141+
{
142+
/*
143+
* Encode then decode an IPv4 XOR-MAPPED-ADDRESS attribute and
144+
* confirm the round-trip recovers the original IPv4 string —
145+
* the write path must XOR the address with the magic cookie
146+
* symmetrically to the read path.
147+
*/
148+
uint8_t buf[512];
149+
switch_stun_packet_t *packet;
150+
switch_stun_packet_attribute_t *attr;
151+
const char *ipv4_str = "192.0.2.42";
152+
char out_ip[64] = { 0 };
153+
uint16_t out_port = 0;
154+
155+
memset(buf, 0, sizeof(buf));
156+
packet = switch_stun_packet_build_header(SWITCH_STUN_BINDING_RESPONSE, NULL, buf);
157+
158+
switch_stun_packet_attribute_add_xor_binded_address(packet, (char *)ipv4_str, 12345, AF_INET);
159+
160+
attr = (switch_stun_packet_attribute_t *)packet->first_attribute;
161+
fst_xcheck(ntohs(attr->type) == SWITCH_STUN_ATTR_XOR_MAPPED_ADDRESS, "attribute type is XOR_MAPPED_ADDRESS");
162+
fst_xcheck(ntohs(attr->length) == 8, "attribute length is 8 for IPv4");
163+
164+
switch_stun_packet_attribute_get_xor_mapped_address(attr, &packet->header, out_ip, sizeof(out_ip), &out_port);
165+
fst_check_string_equals(out_ip, ipv4_str);
166+
}
167+
FST_TEST_END()
168+
}
169+
FST_SUITE_END()
170+
}
171+
FST_CORE_END()
172+

0 commit comments

Comments
 (0)