Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 19 additions & 33 deletions src/switch_stun.c
Original file line number Diff line number Diff line change
Expand Up @@ -485,31 +485,24 @@ SWITCH_DECLARE(switch_stun_packet_t *) switch_stun_packet_build_header(switch_st
SWITCH_DECLARE(uint8_t) switch_stun_packet_attribute_add_binded_address(switch_stun_packet_t *packet, char *ipstr, uint16_t port, int family)
{
switch_stun_packet_attribute_t *attribute;
switch_stun_ip_t *ip;

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

if (family == AF_INET6) {
switch_stun_ipv6_t *ipv6 = (switch_stun_ipv6_t *) attribute->value;

attribute->length = htons(20);
ipv6->family = 2;
ipv6->port = htons(port ^ (STUN_MAGIC_COOKIE >> 16));
inet_pton(AF_INET6, ipstr, ipv6->address);
} else {
attribute->length = htons(8);
}

ip = (switch_stun_ip_t *) attribute->value;

ip->port = htons(port ^ (STUN_MAGIC_COOKIE >> 16));
switch_stun_ip_t *ip = (switch_stun_ip_t *) attribute->value;

if (family == AF_INET6) {
ip->family = 2;
} else {
attribute->length = htons(8);
ip->family = 1;
}

if (family == AF_INET6) {
inet_pton(AF_INET6, ipstr, (struct in6_addr *) &ip->address);
} else {
inet_pton(AF_INET, ipstr, (int *) &ip->address);
ip->port = htons(port ^ (STUN_MAGIC_COOKIE >> 16));
inet_pton(AF_INET, ipstr, &ip->address);
}

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

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

if (family == AF_INET6) {
switch_stun_ipv6_t *ipv6 = (switch_stun_ipv6_t *) attribute->value;

attribute->length = htons(20);
ipv6->family = 2;
ipv6->port = htons(port ^ (STUN_MAGIC_COOKIE >> 16));
inet_pton(AF_INET6, ipstr, ipv6->address);
v6_xor(ipv6->address, (uint8_t *)packet->header.id);
} else {
attribute->length = htons(8);
}

ip = (switch_stun_ip_t *) attribute->value;

ip->port = htons(port ^ (STUN_MAGIC_COOKIE >> 16));
switch_stun_ip_t *ip = (switch_stun_ip_t *) attribute->value;

if (family == AF_INET6) {
ip->family = 2;
} else {
attribute->length = htons(8);
ip->family = 1;
}

if (family == AF_INET6) {
inet_pton(AF_INET6, ipstr, (struct in6_addr *) &ip->address);
v6_xor((uint8_t *)&ip->address, (uint8_t *)packet->header.id);
} else {
inet_pton(AF_INET, ipstr, (int *) &ip->address);
ip->port = htons(port ^ (STUN_MAGIC_COOKIE >> 16));
inet_pton(AF_INET, ipstr, &ip->address);
ip->address = htonl(ntohl(ip->address) ^ STUN_MAGIC_COOKIE);
}

Expand Down
1 change: 1 addition & 0 deletions tests/unit/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ include $(top_srcdir)/build/modmake.rulesam
noinst_PROGRAMS = switch_event switch_hash switch_ivr_originate switch_utils switch_core switch_console switch_vpx switch_core_file \
switch_ivr_play_say switch_core_codec switch_rtp switch_xml
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
noinst_PROGRAMS += switch_stun
noinst_PROGRAMS += test_tts_format
noinst_PROGRAMS+= switch_hold switch_sip

Expand Down
73 changes: 73 additions & 0 deletions tests/unit/conf_stun/freeswitch.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
<?xml version="1.0"?>
<document type="freeswitch/xml">
<X-PRE-PROCESS cmd="exec-set" data="test=echo 1234"/>
<X-PRE-PROCESS cmd="set" data="default_password=$${test}"/>
<X-PRE-PROCESS cmd="set" data="core_video_blank_image=$${conf_dir}/freeswitch-logo.png"/>
<section name="configuration" description="Various Configuration">
<configuration name="modules.conf" description="Modules">
<modules>
<load module="mod_console"/>
<load module="mod_loopback"/>
<load module="mod_commands"/>
<load module="mod_sndfile"/>
<load module="mod_dptools"/>
<load module="mod_tone_stream"/>
<load module="mod_test"/>
<load module="mod_opus"/>
</modules>
</configuration>

<configuration name="console.conf" description="Console Logger">
<mappings>
<map name="all" value="console,debug,info,notice,warning,err,crit,alert"/>
</mappings>
<settings>
<param name="colorize" value="true"/>
<param name="loglevel" value="debug"/>

</settings>
</configuration>

<configuration name="switch.conf" description="Core Configuration">

<default-ptimes>
</default-ptimes>

<settings>

<param name="colorize-console" value="false"/>
<param name="dialplan-timestamps" value="false"/>
<param name="loglevel" value="debug"/>
<param name="rtp-start-port" value="1234"/>
<param name="rtp-end-port" value="1234"/>

</settings>
</configuration>

<configuration name="timezones.conf" description="Timezones">
<timezones>
<zone name="GMT" value="GMT0" />
</timezones>
</configuration>

</section>

<section name="dialplan" description="Regex/XML Dialplan">
<context name="test">
<extension>
<condition field="${sip_h_X-COUNTDOWN}" expression="^0$" break="on-true">
<action application="answer"/>
<action application="playback" data="tone_stream://%(251,0,1004);loops=-1"/>
</condition>
<condition field="${sip_h_X-COUNTDOWN}" expression="^(\d+)$" break="never">
<action application="export" data="_nolocal_sip_h_X-COUNTDOWN=${expr($1 - 1)}"/>
<anti-action application="export" data="_nolocal_sip_h_X-COUNTDOWN=10"/>
</condition>
<condition>
<action application="bridge" data="sofia/test/1234@127.0.0.1"/>
</condition>
</extension>
</context>

</section>
</document>
172 changes: 172 additions & 0 deletions tests/unit/switch_stun.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
/*
* FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
* Copyright (C) 2005-2026, Anthony Minessale II <anthm@freeswitch.org>
*
* Version: MPL 1.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
*
* The Initial Developer of the Original Code is
* Anthony Minessale II <anthm@freeswitch.org>
* Portions created by the Initial Developer are Copyright (C)
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Dmitry Verenitsin <morbit85@gmail.com>
*
* switch_stun.c -- tests STUN (https://www.rfc-editor.org/rfc/rfc5389).
*/


#include <switch.h>
#include <switch_stun.h>
#include <test/switch_test.h>

FST_CORE_BEGIN("./conf_stun")
{
FST_SUITE_BEGIN(switch_stun)
{
FST_SETUP_BEGIN()
{
}
FST_SETUP_END()

FST_TEARDOWN_BEGIN()
{
}
FST_TEARDOWN_END()

FST_TEST_BEGIN(test_stun_add_binded_address_ipv6)
{
/*
* Encode an IPv6 XOR-MAPPED-ADDRESS attribute and verify the
* attribute type, length, address family, and the raw 16-byte
* address payload at its expected offset inside the value.
*/
uint8_t buf[512];
switch_stun_packet_t *packet;
switch_stun_packet_attribute_t *attr;
const char *ipv6_str = "2001:db8::dead:beef";
uint8_t expected[16];
uint8_t *value_bytes;

memset(buf, 0, sizeof(buf));
packet = switch_stun_packet_build_header(SWITCH_STUN_BINDING_RESPONSE, NULL, buf);
fst_xcheck(inet_pton(AF_INET6, ipv6_str, expected) == 1, "test IPv6 literal parses");

switch_stun_packet_attribute_add_binded_address(packet, (char *)ipv6_str, 12345, AF_INET6);

attr = (switch_stun_packet_attribute_t *)packet->first_attribute;
fst_xcheck(ntohs(attr->type) == SWITCH_STUN_ATTR_XOR_MAPPED_ADDRESS, "attribute type is XOR_MAPPED_ADDRESS");
fst_xcheck(ntohs(attr->length) == 20, "attribute length is 20 for IPv6");

/* Attribute value layout: wasted(1) + family(1) + port(2) + address(16). */
value_bytes = (uint8_t *)attr->value;
fst_xcheck(value_bytes[1] == 2, "attribute family byte is 2 for IPv6");
fst_xcheck(memcmp(value_bytes + 4, expected, 16) == 0, "16-byte IPv6 address written at offset 4 of attribute value");
}
FST_TEST_END()

FST_TEST_BEGIN(test_stun_add_xor_binded_address_ipv6)
{
/*
* Encode then decode an IPv6 XOR-MAPPED-ADDRESS attribute and
* confirm the round-trip recovers the original IPv6 string —
* the write path must XOR the address with the transaction ID
* symmetrically to the read path.
*/
uint8_t buf[512];
switch_stun_packet_t *packet;
switch_stun_packet_attribute_t *attr;
const char *ipv6_str = "2001:db8::dead:beef";
char out_ip[64] = { 0 };
uint16_t out_port = 0;

memset(buf, 0, sizeof(buf));
packet = switch_stun_packet_build_header(SWITCH_STUN_BINDING_RESPONSE, NULL, buf);

switch_stun_packet_attribute_add_xor_binded_address(packet, (char *)ipv6_str, 12345, AF_INET6);

attr = (switch_stun_packet_attribute_t *)packet->first_attribute;
fst_xcheck(ntohs(attr->type) == SWITCH_STUN_ATTR_XOR_MAPPED_ADDRESS, "attribute type is XOR_MAPPED_ADDRESS");
fst_xcheck(ntohs(attr->length) == 20, "attribute length is 20 for IPv6");

switch_stun_packet_attribute_get_xor_mapped_address(attr, &packet->header, out_ip, sizeof(out_ip), &out_port);
fst_check_string_equals(out_ip, ipv6_str);
}
FST_TEST_END()

FST_TEST_BEGIN(test_stun_add_binded_address_ipv4)
{
/*
* Encode an IPv4 XOR-MAPPED-ADDRESS attribute and verify the
* attribute type, length, address family, and the raw 4-byte
* address payload at its expected offset inside the value.
*/
uint8_t buf[512];
switch_stun_packet_t *packet;
switch_stun_packet_attribute_t *attr;
const char *ipv4_str = "192.0.2.42";
uint8_t expected[4];
uint8_t *value_bytes;

memset(buf, 0, sizeof(buf));
packet = switch_stun_packet_build_header(SWITCH_STUN_BINDING_RESPONSE, NULL, buf);
fst_xcheck(inet_pton(AF_INET, ipv4_str, expected) == 1, "test IPv4 literal parses");

switch_stun_packet_attribute_add_binded_address(packet, (char *)ipv4_str, 12345, AF_INET);

attr = (switch_stun_packet_attribute_t *)packet->first_attribute;
fst_xcheck(ntohs(attr->type) == SWITCH_STUN_ATTR_XOR_MAPPED_ADDRESS, "attribute type is XOR_MAPPED_ADDRESS");
fst_xcheck(ntohs(attr->length) == 8, "attribute length is 8 for IPv4");

/* Attribute value layout: wasted(1) + family(1) + port(2) + address(4). */
value_bytes = (uint8_t *)attr->value;
fst_xcheck(value_bytes[1] == 1, "attribute family byte is 1 for IPv4");
fst_xcheck(memcmp(value_bytes + 4, expected, 4) == 0, "4-byte IPv4 address written at offset 4 of attribute value");
}
FST_TEST_END()

FST_TEST_BEGIN(test_stun_add_xor_binded_address_ipv4)
{
/*
* Encode then decode an IPv4 XOR-MAPPED-ADDRESS attribute and
* confirm the round-trip recovers the original IPv4 string —
* the write path must XOR the address with the magic cookie
* symmetrically to the read path.
*/
uint8_t buf[512];
switch_stun_packet_t *packet;
switch_stun_packet_attribute_t *attr;
const char *ipv4_str = "192.0.2.42";
char out_ip[64] = { 0 };
uint16_t out_port = 0;

memset(buf, 0, sizeof(buf));
packet = switch_stun_packet_build_header(SWITCH_STUN_BINDING_RESPONSE, NULL, buf);

switch_stun_packet_attribute_add_xor_binded_address(packet, (char *)ipv4_str, 12345, AF_INET);

attr = (switch_stun_packet_attribute_t *)packet->first_attribute;
fst_xcheck(ntohs(attr->type) == SWITCH_STUN_ATTR_XOR_MAPPED_ADDRESS, "attribute type is XOR_MAPPED_ADDRESS");
fst_xcheck(ntohs(attr->length) == 8, "attribute length is 8 for IPv4");

switch_stun_packet_attribute_get_xor_mapped_address(attr, &packet->header, out_ip, sizeof(out_ip), &out_port);
fst_check_string_equals(out_ip, ipv4_str);
}
FST_TEST_END()
}
FST_SUITE_END()
}
FST_CORE_END()

Loading