TT#49351 create preloaded library for system tests

Change-Id: Ibd87e7c03637a0772dcf3b0f2ed0d8646aa2add9
changes/62/25962/15
Richard Fuchs 6 years ago
parent b593d57e21
commit 44c38c6c83

10
debian/control vendored

@ -14,14 +14,24 @@ Build-Depends:
libavformat-dev (>= 6:10),
libavutil-dev (>= 6:10),
libbcg729-dev <!pkg.ngcp-rtpengine.nobcg729>,
libbencode-perl,
libcrypt-openssl-rsa-perl,
libcrypt-rijndael-perl,
libcurl4-openssl-dev | libcurl4-gnutls-dev,
libdigest-crc-perl,
libdigest-hmac-perl,
libevent-dev (>= 2.0),
libglib2.0-dev (>= 2.30),
libhiredis-dev,
libio-multiplex-perl,
libio-socket-inet6-perl,
libio-socket-ip-perl,
libiptc-dev,
libjson-glib-dev,
libnet-interface-perl,
libpcap0.8-dev,
libpcre3-dev,
libsocket6-perl,
libssl-dev (>= 1.0.1),
libswresample-dev (>= 6:10),
libsystemd-dev,

@ -26,19 +26,32 @@ sub new {
my $self = {};
bless $self, $class;
srand(1234) if $ENV{RTPE_TEST_PSEUDO_RAND};
# detect local interfaces
my (@v4, @v6);
my @intfs = Net::Interface->interfaces();
my @v4 = map {$_->address(&AF_INET)} @intfs;
@v4 = map {Socket6::inet_ntop(&AF_INET, $_)} @v4;
@v4 = grep {$_ !~ /^127\./} @v4;
if ($ENV{RTPE_TEST_V4_ADDRS}) {
@v4 = split(/ /, $ENV{RTPE_TEST_V4_ADDRS});
}
else {
@v4 = map {$_->address(&AF_INET)} @intfs;
@v4 = map {Socket6::inet_ntop(&AF_INET, $_)} @v4;
@v4 = grep {$_ !~ /^127\./} @v4;
}
@v4 = map { { address => $_, sockdomain => &AF_INET } } @v4;
@v4 or die("no IPv4 addresses found");
my @v6 = map {$_->address(&AF_INET6)} @intfs;
@v6 = map {Socket6::inet_ntop(&AF_INET6, $_)} @v6;
@v6 = grep {$_ !~ /^::|^fe80:/} @v6;
if ($ENV{RTPE_TEST_V6_ADDRS}) {
@v6 = split(/ /, $ENV{RTPE_TEST_V6_ADDRS});
}
else {
@v6 = map {$_->address(&AF_INET6)} @intfs;
@v6 = map {Socket6::inet_ntop(&AF_INET6, $_)} @v6;
@v6 = grep {$_ !~ /^::|^fe80:/} @v6;
}
@v6 = map { { address => $_, sockdomain => &AF_INET6 } } @v6;
@v6 or die("no IPv6 addresses found");
@ -51,12 +64,12 @@ sub new {
$self->{mux} = IO::Multiplex->new();
$self->{mux}->set_callback_object($self);
$self->{media_port} = $args{media_port} // 2000;
$self->{media_port} = $args{media_port} // $ENV{RTPE_TEST_MEDIA_PORT} // 2000;
$self->{timers} = [];
$self->{clients} = [];
$self->{control} = NGCP::Rtpengine->new($args{host} // $ENV{RTPENGINE_HOST} // 'localhost',
$args{port} // $ENV{RTPENGINE_POR} // 2223);
$args{port} // $ENV{RTPENGINE_PORT} // 2223);
$self->{callid} = rand();
return $self;

1
t/.gitignore vendored

@ -45,3 +45,4 @@ udp_listener.c
payload-tracker-test
dtmf.c
const_str_hash-test.strhash
tests-preload.so

@ -1,4 +1,4 @@
TARGET= unit-tests
TARGET= all-tests
with_transcoding ?= yes
@ -71,18 +71,30 @@ include ../lib/common.Makefile
include .depend
.PHONY: unit-tests
.PHONY: all-tests unit-tests daemon-tests
TESTS= bitstr-test aes-crypt payload-tracker-test const_str_hash-test.strhash
ifeq ($(with_transcoding),yes)
TESTS+= amr-decode-test amr-encode-test transcode-test
endif
ADD_CLEAN= $(TESTS)
ADD_CLEAN= tests-preload.so $(TESTS)
all-tests: unit-tests daemon-tests
true # override linking recipe from common.Makefile
unit-tests: $(TESTS)
for x in $(TESTS); do echo testing: $$x; G_DEBUG=fatal-warnings ./$$x || exit 1; done
daemon-tests: tests-preload.so
$(MAKE) -C ../daemon
rm -rf fake-sockets
mkdir fake-sockets
LD_PRELOAD=../t/tests-preload.so RTPE_BIN=../daemon/rtpengine TEST_SOCKET_PATH=./fake-sockets \
perl -I../perl auto-daemon-tests.pl
test "$$(ls fake-sockets)" = ""
rmdir fake-sockets
bitstr-test: bitstr-test.o
amr-decode-test: amr-decode-test.o $(COMMONOBJS) codeclib.o resample.o
@ -100,3 +112,6 @@ transcode-test: transcode-test.o $(COMMONOBJS) codeclib.o resample.o codec.o ssr
payload-tracker-test: payload-tracker-test.o $(COMMONOBJS) ssrc.o aux.o auxlib.o rtp.o crypto.o
const_str_hash-test.strhash: const_str_hash-test.strhash.o $(COMMONOBJS)
tests-preload.so: tests-preload.c
$(CC) -g -D_GNU_SOURCE -std=c99 -o $@ -Wall -shared -fPIC $<

@ -0,0 +1,485 @@
#!/usr/bin/perl
use strict;
use warnings;
use NGCP::Rtpengine::Test;
use Test::More;
use File::Temp;
use IPC::Open3;
use Time::HiRes;
use POSIX ":sys_wait_h";
like $ENV{LD_PRELOAD}, qr/tests-preload/, 'LD_PRELOAD present';
is $ENV{RTPE_PRELOAD_TEST_ACTIVE}, '1', 'preload library is active';
ok -x $ENV{RTPE_BIN}, 'RTPE_BIN points to executable';
my $rtpe_stdout = File::Temp::tempfile() or die;
my $rtpe_stderr = File::Temp::tempfile() or die;
my $rtpe_pid = open3(undef, $rtpe_stdout, $rtpe_stderr,
$ENV{RTPE_BIN}, qw(-t -1 -i 203.0.113.1 -i 2001:db8:4321::1 -n 2223 -c 12345 -f -L 7 -E -u 2222));
ok $rtpe_pid, 'daemon launched in background';
# keep trying to connect to the control socket while daemon is starting up
my $c;
for (1 .. 300) {
$c = NGCP::Rtpengine->new($ENV{RTPENGINE_HOST} // 'localhost', $ENV{RTPENGINE_PORT} // 2223);
last if $c->{socket};
Time::HiRes::usleep(100000); # 100 ms x 300 = 30 sec
}
1;
$c->{socket} or die;
my ($cid, $ft, $tt, $r);
sub new_call {
undef($r);
$cid = rand();
$ft = rand();
$tt = rand();
return;
}
sub crlf {
my ($s) = @_;
$s =~ s/\r\n/\n/gs;
return $s;
}
sub sdp_split {
my ($s) = @_;
return split(/--------*\n/, $s);
}
sub offer_answer {
my ($cmd, $name, $req, $sdps) = @_;
my ($sdp_in, $exp_sdp_out) = sdp_split($sdps);
$req->{command} = $cmd;
$req->{'call-id'} = $cid;
$req->{'from-tag'} = $ft;
$req->{sdp} = $sdp_in;
my $resp = $c->req($req);
is $resp->{result}, 'ok', "$name - $cmd status";
my $regexp = "^\Q$exp_sdp_out\E\$";
$regexp =~ s/PORT/(\\d{1,5})/gs;
$regexp =~ s/ICEBASE/([0-9a-zA-Z]{16})/gs;
$regexp =~ s/ICEUFRAG/([0-9a-zA-Z]{8})/gs;
$regexp =~ s/ICEPWD/([0-9a-zA-Z]{26})/gs;
like crlf($resp->{sdp}), qr/$regexp/s, "$name - output $cmd SDP";
return;
}
sub offer {
return offer_answer('offer', @_);
}
sub answer {
my ($name, $req, $sdps) = @_;
$req->{'to-tag'} = $tt;
return offer_answer('answer', $name, $req, $sdps);
}
$r = $c->req({command => 'ping'});
ok $r->{result} eq 'pong', 'ping works, daemon operational';
# SDP in/out tests, various ICE options
new_call;
offer('plain SDP, no ICE', { ICE => 'remove' }, <<SDP);
v=0
o=- 1545997027 1 IN IP4 198.51.100.1
s=tester
t=0 0
m=audio 2000 RTP/AVP 0
c=IN IP4 198.51.100.1
a=sendrecv
----------------------------------
v=0
o=- 1545997027 1 IN IP4 198.51.100.1
s=tester
t=0 0
m=audio PORT RTP/AVP 0
c=IN IP4 203.0.113.1
a=rtpmap:0 PCMU/8000
a=sendrecv
a=rtcp:PORT
SDP
answer('plain SDP, no ICE', { ICE => 'remove' }, <<SDP);
v=0
o=- 1545997027 1 IN IP4 198.51.100.3
s=tester
t=0 0
m=audio 2002 RTP/AVP 0
c=IN IP4 198.51.100.3
a=sendrecv
--------------------------------------
v=0
o=- 1545997027 1 IN IP4 198.51.100.3
s=tester
t=0 0
m=audio PORT RTP/AVP 0
c=IN IP4 203.0.113.1
a=rtpmap:0 PCMU/8000
a=sendrecv
a=rtcp:PORT
SDP
new_call;
offer('plain SDP, add default ICE', { }, <<SDP);
v=0
o=- 1545997027 1 IN IP4 198.51.100.1
s=tester
t=0 0
m=audio 2000 RTP/AVP 0
c=IN IP4 198.51.100.1
a=sendrecv
------------------------------------
v=0
o=- 1545997027 1 IN IP4 198.51.100.1
s=tester
t=0 0
m=audio PORT RTP/AVP 0
c=IN IP4 203.0.113.1
a=rtpmap:0 PCMU/8000
a=sendrecv
a=rtcp:PORT
a=ice-ufrag:ICEUFRAG
a=ice-pwd:ICEPWD
a=candidate:ICEBASE 1 UDP 2130706431 203.0.113.1 PORT typ host
a=candidate:ICEBASE 1 UDP 2130706175 2001:db8:4321::1 PORT typ host
a=candidate:ICEBASE 2 UDP 2130706430 203.0.113.1 PORT typ host
a=candidate:ICEBASE 2 UDP 2130706174 2001:db8:4321::1 PORT typ host
SDP
answer('plain SDP, ICE removed', { ICE => 'remove' }, <<SDP);
v=0
o=- 1545997027 1 IN IP4 198.51.100.3
s=tester
t=0 0
m=audio 2002 RTP/AVP 0
c=IN IP4 198.51.100.3
a=sendrecv
-------------------------------
v=0
o=- 1545997027 1 IN IP4 198.51.100.3
s=tester
t=0 0
m=audio PORT RTP/AVP 0
c=IN IP4 203.0.113.1
a=rtpmap:0 PCMU/8000
a=sendrecv
a=rtcp:PORT
SDP
new_call;
offer('plain SDP, ICE removed', { ICE => 'remove' }, <<SDP);
v=0
o=- 1545997027 1 IN IP4 198.51.100.1
s=tester
t=0 0
m=audio 2000 RTP/AVP 0
c=IN IP4 198.51.100.1
a=sendrecv
----------------------------
v=0
o=- 1545997027 1 IN IP4 198.51.100.1
s=tester
t=0 0
m=audio PORT RTP/AVP 0
c=IN IP4 203.0.113.1
a=rtpmap:0 PCMU/8000
a=sendrecv
a=rtcp:PORT
SDP
answer('plain SDP, no ICE option given', { }, <<SDP);
v=0
o=- 1545997027 1 IN IP4 198.51.100.3
s=tester
t=0 0
m=audio 2002 RTP/AVP 0
c=IN IP4 198.51.100.3
a=sendrecv
--------------------------------
v=0
o=- 1545997027 1 IN IP4 198.51.100.3
s=tester
t=0 0
m=audio PORT RTP/AVP 0
c=IN IP4 203.0.113.1
a=rtpmap:0 PCMU/8000
a=sendrecv
a=rtcp:PORT
SDP
new_call;
offer('ICE SDP, default ICE option', { }, <<SDP);
v=0
o=- 1545997027 1 IN IP4 198.51.100.1
s=tester
t=0 0
m=audio 2000 RTP/AVP 0
c=IN IP4 198.51.100.1
a=sendrecv
a=ice-ufrag:asbsdfds
a=ice-pwd:sfhwsrgyergws45ujhsrthsrhH
a=candidate:sfthqw45hdfgdfsb 1 UDP 2130706431 198.51.100.3 2002 typ host
a=candidate:ujksdfghfdfgdfsb 1 UDP 2130706175 2001:db8:abcd::3 2002 typ host
a=candidate:sfthqw45hdfgdfsb 2 UDP 2130706430 198.51.100.3 2003 typ host
a=candidate:ujksdfghfdfgdfsb 2 UDP 2130706174 2001:db8:abcd::3 2003 typ host
----------------------------
v=0
o=- 1545997027 1 IN IP4 198.51.100.1
s=tester
t=0 0
m=audio PORT RTP/AVP 0
c=IN IP4 203.0.113.1
a=ice-ufrag:asbsdfds
a=ice-pwd:sfhwsrgyergws45ujhsrthsrhH
a=candidate:sfthqw45hdfgdfsb 1 UDP 2130706431 198.51.100.3 2002 typ host
a=candidate:ujksdfghfdfgdfsb 1 UDP 2130706175 2001:db8:abcd::3 2002 typ host
a=candidate:sfthqw45hdfgdfsb 2 UDP 2130706430 198.51.100.3 2003 typ host
a=candidate:ujksdfghfdfgdfsb 2 UDP 2130706174 2001:db8:abcd::3 2003 typ host
a=rtpmap:0 PCMU/8000
a=sendrecv
a=rtcp:PORT
a=candidate:ICEBASE 1 UDP 2097152255 203.0.113.1 PORT typ host
a=candidate:ICEBASE 1 UDP 4294967295 2001:db8:4321::1 PORT typ host
a=candidate:ICEBASE 2 UDP 2097152254 203.0.113.1 PORT typ host
a=candidate:ICEBASE 2 UDP 4294967294 2001:db8:4321::1 PORT typ host
SDP
answer('plain SDP, ICE rejected, no ICE option given', { }, <<SDP);
v=0
o=- 1545997027 1 IN IP4 198.51.100.3
s=tester
t=0 0
m=audio 2002 RTP/AVP 0
c=IN IP4 198.51.100.3
a=sendrecv
--------------------------------
v=0
o=- 1545997027 1 IN IP4 198.51.100.3
s=tester
t=0 0
m=audio PORT RTP/AVP 0
c=IN IP4 203.0.113.1
a=rtpmap:0 PCMU/8000
a=sendrecv
a=rtcp:PORT
SDP
new_call;
offer('ICE SDP with ICE force', { ICE => 'force' }, <<SDP);
v=0
o=- 1545997027 1 IN IP4 198.51.100.1
s=tester
t=0 0
m=audio 2000 RTP/AVP 0
c=IN IP4 198.51.100.1
a=sendrecv
a=ice-ufrag:asbsdfds
a=ice-pwd:sfhwsrgyergws45ujhsrthsrhH
a=candidate:sfthqw45hdfgdfsb 1 UDP 2130706431 198.51.100.3 2002 typ host
a=candidate:ujksdfghfdfgdfsb 1 UDP 2130706175 2001:db8:abcd::3 2002 typ host
a=candidate:sfthqw45hdfgdfsb 2 UDP 2130706430 198.51.100.3 2003 typ host
a=candidate:ujksdfghfdfgdfsb 2 UDP 2130706174 2001:db8:abcd::3 2003 typ host
----------------------------
v=0
o=- 1545997027 1 IN IP4 198.51.100.1
s=tester
t=0 0
m=audio PORT RTP/AVP 0
c=IN IP4 203.0.113.1
a=rtpmap:0 PCMU/8000
a=sendrecv
a=rtcp:PORT
a=ice-ufrag:ICEUFRAG
a=ice-pwd:ICEPWD
a=candidate:ICEBASE 1 UDP 2130706431 203.0.113.1 PORT typ host
a=candidate:ICEBASE 1 UDP 2130706175 2001:db8:4321::1 PORT typ host
a=candidate:ICEBASE 2 UDP 2130706430 203.0.113.1 PORT typ host
a=candidate:ICEBASE 2 UDP 2130706174 2001:db8:4321::1 PORT typ host
SDP
answer('plain SDP, ICE rejected, no ICE option given', { }, <<SDP);
v=0
o=- 1545997027 1 IN IP4 198.51.100.3
s=tester
t=0 0
m=audio 2002 RTP/AVP 0
c=IN IP4 198.51.100.3
a=sendrecv
--------------------------------
v=0
o=- 1545997027 1 IN IP4 198.51.100.3
s=tester
t=0 0
m=audio PORT RTP/AVP 0
c=IN IP4 203.0.113.1
a=rtpmap:0 PCMU/8000
a=sendrecv
a=rtcp:PORT
a=ice-ufrag:ICEUFRAG
a=ice-pwd:ICEPWD
a=candidate:ICEBASE 1 UDP 2130706431 203.0.113.1 PORT typ host
a=candidate:ICEBASE 1 UDP 2130706175 2001:db8:4321::1 PORT typ host
a=candidate:ICEBASE 2 UDP 2130706430 203.0.113.1 PORT typ host
a=candidate:ICEBASE 2 UDP 2130706174 2001:db8:4321::1 PORT typ host
SDP
new_call;
offer('ICE SDP with ICE force', { ICE => 'force' }, <<SDP);
v=0
o=- 1545997027 1 IN IP4 198.51.100.1
s=tester
t=0 0
m=audio 2000 RTP/AVP 0
c=IN IP4 198.51.100.1
a=sendrecv
a=ice-ufrag:asbsdfds
a=ice-pwd:sfhwsrgyergws45ujhsrthsrhH
a=candidate:sfthqw45hdfgdfsb 1 UDP 2130706431 198.51.100.3 2002 typ host
a=candidate:ujksdfghfdfgdfsb 1 UDP 2130706175 2001:db8:abcd::3 2002 typ host
a=candidate:sfthqw45hdfgdfsb 2 UDP 2130706430 198.51.100.3 2003 typ host
a=candidate:ujksdfghfdfgdfsb 2 UDP 2130706174 2001:db8:abcd::3 2003 typ host
----------------------------
v=0
o=- 1545997027 1 IN IP4 198.51.100.1
s=tester
t=0 0
m=audio PORT RTP/AVP 0
c=IN IP4 203.0.113.1
a=rtpmap:0 PCMU/8000
a=sendrecv
a=rtcp:PORT
a=ice-ufrag:ICEUFRAG
a=ice-pwd:ICEPWD
a=candidate:ICEBASE 1 UDP 2130706431 203.0.113.1 PORT typ host
a=candidate:ICEBASE 1 UDP 2130706175 2001:db8:4321::1 PORT typ host
a=candidate:ICEBASE 2 UDP 2130706430 203.0.113.1 PORT typ host
a=candidate:ICEBASE 2 UDP 2130706174 2001:db8:4321::1 PORT typ host
SDP
answer('ICE SDP, no ICE option given', { }, <<SDP);
v=0
o=- 1545997027 1 IN IP4 198.51.100.3
s=tester
t=0 0
m=audio 2002 RTP/AVP 0
c=IN IP4 198.51.100.3
a=sendrecv
a=ice-ufrag:bmnhkfdf
a=ice-pwd:jetyhsdfgsdtjhtyjktrthsrhH
a=candidate:keutydghfbhdcfsb 1 UDP 2130706431 198.51.100.3 2002 typ host
a=candidate:ujksdfghfbhdcfsb 1 UDP 2130706175 2001:db8:abcd::3 2002 typ host
a=candidate:keutydghfbhdcfsb 2 UDP 2130706430 198.51.100.3 2003 typ host
a=candidate:ujksdfghfbhdcfsb 2 UDP 2130706174 2001:db8:abcd::3 2003 typ host
--------------------------------
v=0
o=- 1545997027 1 IN IP4 198.51.100.3
s=tester
t=0 0
m=audio PORT RTP/AVP 0
c=IN IP4 203.0.113.1
a=ice-ufrag:bmnhkfdf
a=ice-pwd:jetyhsdfgsdtjhtyjktrthsrhH
a=candidate:keutydghfbhdcfsb 1 UDP 2130706431 198.51.100.3 2002 typ host
a=candidate:ujksdfghfbhdcfsb 1 UDP 2130706175 2001:db8:abcd::3 2002 typ host
a=candidate:keutydghfbhdcfsb 2 UDP 2130706430 198.51.100.3 2003 typ host
a=candidate:ujksdfghfbhdcfsb 2 UDP 2130706174 2001:db8:abcd::3 2003 typ host
a=rtpmap:0 PCMU/8000
a=sendrecv
a=rtcp:PORT
a=candidate:ICEBASE 1 UDP 2097152255 203.0.113.1 PORT typ host
a=candidate:ICEBASE 1 UDP 4294967295 2001:db8:4321::1 PORT typ host
a=candidate:ICEBASE 2 UDP 2097152254 203.0.113.1 PORT typ host
a=candidate:ICEBASE 2 UDP 4294967294 2001:db8:4321::1 PORT typ host
SDP
new_call;
offer('ICE SDP with ICE force', { ICE => 'force' }, <<SDP);
v=0
o=- 1545997027 1 IN IP4 198.51.100.1
s=tester
t=0 0
m=audio 2000 RTP/AVP 0
c=IN IP4 198.51.100.1
a=sendrecv
a=ice-ufrag:asbsdfds
a=ice-pwd:sfhwsrgyergws45ujhsrthsrhH
a=candidate:sfthqw45hdfgdfsb 1 UDP 2130706431 198.51.100.3 2002 typ host
a=candidate:ujksdfghfdfgdfsb 1 UDP 2130706175 2001:db8:abcd::3 2002 typ host
a=candidate:sfthqw45hdfgdfsb 2 UDP 2130706430 198.51.100.3 2003 typ host
a=candidate:ujksdfghfdfgdfsb 2 UDP 2130706174 2001:db8:abcd::3 2003 typ host
----------------------------
v=0
o=- 1545997027 1 IN IP4 198.51.100.1
s=tester
t=0 0
m=audio PORT RTP/AVP 0
c=IN IP4 203.0.113.1
a=rtpmap:0 PCMU/8000
a=sendrecv
a=rtcp:PORT
a=ice-ufrag:ICEUFRAG
a=ice-pwd:ICEPWD
a=candidate:ICEBASE 1 UDP 2130706431 203.0.113.1 PORT typ host
a=candidate:ICEBASE 1 UDP 2130706175 2001:db8:4321::1 PORT typ host
a=candidate:ICEBASE 2 UDP 2130706430 203.0.113.1 PORT typ host
a=candidate:ICEBASE 2 UDP 2130706174 2001:db8:4321::1 PORT typ host
SDP
answer('ICE SDP with ICE force', { ICE => 'force' }, <<SDP);
v=0
o=- 1545997027 1 IN IP4 198.51.100.3
s=tester
t=0 0
m=audio 2002 RTP/AVP 0
c=IN IP4 198.51.100.3
a=sendrecv
a=ice-ufrag:bmnhkfdf
a=ice-pwd:jetyhsdfgsdtjhtyjktrthsrhH
a=candidate:keutydghfbhdcfsb 1 UDP 2130706431 198.51.100.3 2002 typ host
a=candidate:ujksdfghfbhdcfsb 1 UDP 2130706175 2001:db8:abcd::3 2002 typ host
a=candidate:keutydghfbhdcfsb 2 UDP 2130706430 198.51.100.3 2003 typ host
a=candidate:ujksdfghfbhdcfsb 2 UDP 2130706174 2001:db8:abcd::3 2003 typ host
--------------------------------
v=0
o=- 1545997027 1 IN IP4 198.51.100.3
s=tester
t=0 0
m=audio PORT RTP/AVP 0
c=IN IP4 203.0.113.1
a=rtpmap:0 PCMU/8000
a=sendrecv
a=rtcp:PORT
a=ice-ufrag:ICEUFRAG
a=ice-pwd:ICEPWD
a=candidate:ICEBASE 1 UDP 2130706431 203.0.113.1 PORT typ host
a=candidate:ICEBASE 1 UDP 2130706175 2001:db8:4321::1 PORT typ host
a=candidate:ICEBASE 2 UDP 2130706430 203.0.113.1 PORT typ host
a=candidate:ICEBASE 2 UDP 2130706174 2001:db8:4321::1 PORT typ host
SDP
END {
if ($rtpe_pid) {
kill('INT', $rtpe_pid) or die;
# wait for daemon to terminate
my $status = -1;
for (1 .. 50) {
$status = waitpid($rtpe_pid, WNOHANG);
last if $status != 0;
Time::HiRes::usleep(100000); # 100 ms x 50 = 5 sec
}
kill('KILL', $rtpe_pid) if $status == 0;
$status == $rtpe_pid or die;
$? == 0 or die;
}
}
done_testing();

@ -0,0 +1,27 @@
#!/usr/bin/perl
use strict;
use warnings;
use NGCP::Rtpengine::Test;
use IO::Socket;
my $r = NGCP::Rtpengine::Test->new();
my ($a, $b) = $r->client_pair(
{sockdomain => &Socket::AF_INET},
{sockdomain => &Socket::AF_INET6}
);
$r->timer_once(3, sub {
$b->answer($a, ICE => 'remove', label => "callee");
$a->start_rtp();
$a->start_rtcp();
});
$r->timer_once(10, sub { $r->stop(); });
$a->offer($b, ICE => 'remove', label => "caller", 'address-family' => 'IP6');
$b->start_rtp();
$b->start_rtcp();
$r->run();
$a->teardown(dump => 1);

@ -0,0 +1,27 @@
#!/usr/bin/perl
use strict;
use warnings;
use NGCP::Rtpengine::Test;
use IO::Socket;
my $r = NGCP::Rtpengine::Test->new();
my ($a, $b) = $r->client_pair(
{sockdomain => &Socket::AF_INET6},
{sockdomain => &Socket::AF_INET}
);
$r->timer_once(3, sub {
$b->answer($a, ICE => 'remove', label => "callee");
$a->start_rtp();
$a->start_rtcp();
});
$r->timer_once(10, sub { $r->stop(); });
$a->offer($b, ICE => 'remove', label => "caller", 'address-family' => 'IP4');
$b->start_rtp();
$b->start_rtcp();
$r->run();
$a->teardown(dump => 1);

@ -0,0 +1,27 @@
#!/usr/bin/perl
use strict;
use warnings;
use NGCP::Rtpengine::Test;
use IO::Socket;
my $r = NGCP::Rtpengine::Test->new();
my ($a, $b) = $r->client_pair(
{sockdomain => &Socket::AF_INET6},
{sockdomain => &Socket::AF_INET6}
);
$r->timer_once(3, sub {
$b->answer($a, ICE => 'remove', label => "callee");
$a->start_rtp();
$a->start_rtcp();
});
$r->timer_once(10, sub { $r->stop(); });
$a->offer($b, ICE => 'remove', label => "caller");
$b->start_rtp();
$b->start_rtcp();
$r->run();
$a->teardown(dump => 1);

@ -0,0 +1,615 @@
#include <dlfcn.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <sys/un.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <assert.h>
#include <unistd.h>
#include <sys/stat.h>
#include <errno.h>
#include <pthread.h>
typedef struct {
int used_domain,
wanted_domain,
type,
used_protocol,
wanted_protocol;
char unix_path[256];
struct sockaddr_storage sockname;
int open:1,
bound:1;
} socket_t;
typedef struct {
struct sockaddr_un path;
struct sockaddr_storage address;
} peer_t;
#define MAX_SOCKETS 1024
static socket_t real_sockets[MAX_SOCKETS];
static unsigned int anon_sock_inc;
static peer_t remote_peers[MAX_SOCKETS];
static unsigned int anon_peer_inc;
static pthread_mutex_t remote_peers_lock = PTHREAD_MUTEX_INITIALIZER;
static void do_init(void) __attribute__((constructor));
static void do_exit(void) __attribute__((destructor));
static void do_init(void) {
setenv("RTPE_PRELOAD_TEST_ACTIVE", "1", 1);
}
static void do_exit(void) {
for (int i = 0; i < MAX_SOCKETS; i++) {
socket_t *s = &real_sockets[i];
if (!s->open)
continue;
if (s->used_domain != AF_UNIX)
continue;
if (s->wanted_domain == AF_UNIX)
continue;
unlink(s->unix_path);
}
}
static const char *path_prefix(void) {
char *ret = getenv("TEST_SOCKET_PATH");
if (ret)
return ret;
return "/tmp";
}
int socket(int domain, int type, int protocol) {
int use_domain = domain;
int use_protocol = protocol;
if (domain == AF_INET || domain == AF_INET6) {
use_domain = AF_UNIX;
use_protocol = 0;
}
int (*real_socket)(int, int, int) = dlsym(RTLD_NEXT, "socket");
int fd = real_socket(use_domain, type, use_protocol);
if (fd < 0 || fd >= MAX_SOCKETS) {
fprintf(stderr, "preload socket(): fd out of bounds (fd %i)\n", fd);
return fd;
}
real_sockets[fd] = (socket_t) {
.used_domain = use_domain,
.wanted_domain = domain,
.type = type,
.used_protocol = use_protocol,
.wanted_protocol = protocol,
.open = 1,
};
return fd;
}
static const char *addr_translate(struct sockaddr_un *sun, const struct sockaddr *addr,
socklen_t addrlen,
int allow_anon)
{
const char *err;
char sockname[64];
const char *any_name;
unsigned int port;
switch (addr->sa_family) {
case AF_INET:;
struct sockaddr_in *sin = (void *) addr;
err = "addrlen too short";
if (addrlen < sizeof(*sin))
goto err;
err = "failed to print network address";
if (!inet_ntop(addr->sa_family, &sin->sin_addr, sockname, sizeof(sockname)))
goto err;
any_name = "0.0.0.0";
port = ntohs(sin->sin_port);
break;
case AF_INET6:;
struct sockaddr_in6 *sin6 = (void *) addr;
err = "addrlen too short";
if (addrlen < sizeof(*sin6))
goto err;
err = "failed to print network address";
if (!inet_ntop(addr->sa_family, &sin6->sin6_addr, sockname, sizeof(sockname)))
goto err;
any_name = "::";
port = ntohs(sin6->sin6_port);
break;
default:
goto skip;
}
int do_specific = 1;
if (allow_anon) {
err = "Unix socket path truncated";
if (snprintf(sun->sun_path, sizeof(sun->sun_path), "%s/[%s]:%u", path_prefix(), any_name, port)
>= sizeof(sun->sun_path))
goto err;
struct stat sb;
int ret = stat(sun->sun_path, &sb);
if (ret == 0 && sb.st_mode & S_IFSOCK)
do_specific = 0;
}
if (do_specific) {
err = "Unix socket path truncated";
if (snprintf(sun->sun_path, sizeof(sun->sun_path), "%s/[%s]:%u", path_prefix(), sockname, port)
>= sizeof(sun->sun_path))
goto err;
}
sun->sun_family = AF_UNIX;
return NULL;
skip:
return ""; // special return value
err:
return err;
}
int bind(int fd, const struct sockaddr *addr, socklen_t addrlen) {
const char *err;
int (*real_bind)(int, const struct sockaddr *, socklen_t) = dlsym(RTLD_NEXT, "bind");
err = "fd out of bounds";
if (fd < 0 || fd >= MAX_SOCKETS)
goto do_bind_warn;
socket_t *s = &real_sockets[fd];
err = "fd not open";
if (!s->open)
goto do_bind_warn;
assert(s->used_domain == AF_UNIX);
assert(s->wanted_domain == addr->sa_family);
struct sockaddr_un sun;
err = addr_translate(&sun, addr, addrlen, 0);
if (err) {
if (!err[0])
goto do_bind;
goto do_bind_warn;
}
struct sockaddr_storage sst = {0,};
if (addrlen > sizeof(sst))
goto do_bind_warn;
memcpy(&sst, addr, addrlen);
addr = (void *) &sun;
addrlen = sizeof(sun);
if (s->unix_path[0])
unlink(s->unix_path);
assert(sizeof(s->unix_path) >= strlen(sun.sun_path));
strcpy(s->unix_path, sun.sun_path);
s->sockname = sst;
s->bound = 1;
goto do_bind;
do_bind_warn:
fprintf(stderr, "preload bind(): %s (fd %i)\n", err, fd);
do_bind:
return real_bind(fd, addr, addrlen);
}
static void anon_addr(int domain, struct sockaddr_storage *sst, unsigned int id, unsigned int id2) {
memset(sst, 0, sizeof(*sst));
switch (domain) {
case AF_INET:;
struct sockaddr_in sin;
sin.sin_family = AF_INET;
sin.sin_port = htons(id);
sin.sin_addr.s_addr = id2;
memcpy(sst, &sin, sizeof(sin));
break;
case AF_INET6:;
struct sockaddr_in6 sin6;
sin6.sin6_family = AF_INET6;
sin6.sin6_port = htons(id);
memset(&sin6.sin6_addr, -1, sizeof(sin6.sin6_addr));
sin6.sin6_addr.s6_addr16[4] = id2;
memcpy(sst, &sin6, sizeof(sin6));
break;
}
}
static void check_bind(int fd) {
// to make inspecting the peer address on the receiving end possible, we must bind
// to some unix path name
if (fd < 0 || fd >= MAX_SOCKETS)
return;
socket_t *s = &real_sockets[fd];
if (!s->open)
return;
if (s->bound)
return;
if (s->wanted_domain == AF_UNIX || s->used_domain != AF_UNIX)
return;
struct sockaddr_storage sst;
unsigned int auto_inc = __sync_fetch_and_add(&anon_sock_inc, 1);
anon_addr(s->wanted_domain, &sst, auto_inc, getpid());
struct sockaddr_un sun;
sun.sun_family = AF_UNIX;
if (snprintf(sun.sun_path, sizeof(sun.sun_path), "%s/ANON.%u.%u", path_prefix(), getpid(),
auto_inc)
>= sizeof(sun.sun_path))
fprintf(stderr, "preload socket(): failed to print anon (fd %i)\n", fd);
assert(sizeof(real_sockets[fd].unix_path) >= strlen(sun.sun_path));
strcpy(real_sockets[fd].unix_path, sun.sun_path);
int (*real_bind)(int, const struct sockaddr *, socklen_t) = dlsym(RTLD_NEXT, "bind");
if (real_bind(fd, (struct sockaddr *) &sun, sizeof(sun)))
fprintf(stderr, "preload socket(): failed to bind to anon (fd %i): %s\n",
fd, strerror(errno));
s->bound = 1;
}
int close(int fd) {
const char *err;
int (*real_close)(int) = dlsym(RTLD_NEXT, "close");
err = "fd out of bounds";
if (fd < 0 || fd >= MAX_SOCKETS)
goto do_close_warn;
socket_t *s = &real_sockets[fd];
if (!s->open)
goto do_close;
s->open = 0;
if (s->used_domain == AF_UNIX && s->wanted_domain != AF_UNIX && s->unix_path[0])
unlink(s->unix_path);
goto do_close;
do_close_warn:
fprintf(stderr, "preload close(): %s (fd %i)\n", err, fd);
do_close:
return real_close(fd);
}
int getsockname(int fd, struct sockaddr *addr, socklen_t *addrlen) {
check_bind(fd);
const char *err;
int (*real_getsockname)(int) = dlsym(RTLD_NEXT, "getsockname");
err = "fd out of bounds";
if (fd < 0 || fd >= MAX_SOCKETS)
goto do_getsockname_warn;
socket_t *s = &real_sockets[fd];
if (!s->open)
goto do_getsockname;
if (s->used_domain != AF_UNIX || s->wanted_domain == AF_UNIX || !s->bound)
goto do_getsockname;
switch (s->wanted_domain) {
case AF_INET:
if (*addrlen < sizeof(struct sockaddr_in))
memcpy(addr, &s->sockname, *addrlen);
else
memcpy(addr, &s->sockname, sizeof(struct sockaddr_in));
*addrlen = sizeof(struct sockaddr_in);
break;
case AF_INET6:
if (*addrlen < sizeof(struct sockaddr_in6))
memcpy(addr, &s->sockname, *addrlen);
else
memcpy(addr, &s->sockname, sizeof(struct sockaddr_in6));
*addrlen = sizeof(struct sockaddr_in6);
break;
default:
goto do_getsockname;
}
return 0;
do_getsockname_warn:
fprintf(stderr, "preload getsockname(): %s (fd %i)\n", err, fd);
do_getsockname:
return real_getsockname(fd);
}
int connect(int fd, const struct sockaddr *addr, socklen_t addrlen) {
check_bind(fd);
const char *err;
int (*real_connect)(int, const struct sockaddr *, socklen_t) = dlsym(RTLD_NEXT, "connect");
err = "fd out of bounds";
if (fd < 0 || fd >= MAX_SOCKETS)
goto do_connect_warn;
socket_t *s = &real_sockets[fd];
err = "fd not open";
if (!s->open)
goto do_connect_warn;
assert(s->used_domain == AF_UNIX);
assert(s->wanted_domain == addr->sa_family);
struct sockaddr_un sun;
err = addr_translate(&sun, addr, addrlen, 1);
if (err) {
if (!err[0])
goto do_connect;
goto do_connect_warn;
}
addr = (void *) &sun;
addrlen = sizeof(sun);
goto do_connect;
do_connect_warn:
fprintf(stderr, "preload connect(): %s (fd %i)\n", err, fd);
do_connect:
return real_connect(fd, addr, addrlen);
}
int dup(int fd) {
int (*real_dup)(int) = dlsym(RTLD_NEXT, "dup");
int ret = real_dup(fd);
if (fd < 0 || fd >= MAX_SOCKETS || ret < 0 || ret >= MAX_SOCKETS) {
fprintf(stderr, "preload dup(): fd out of bounds (%i/%i)\n", fd, ret);
return ret;
}
real_sockets[ret] = real_sockets[fd];
return ret;
}
int dup2(int oldfd, int newfd) {
int (*real_dup2)(int, int) = dlsym(RTLD_NEXT, "dup2");
int ret = real_dup2(oldfd, newfd);
if (ret != newfd || oldfd < 0 || oldfd >= MAX_SOCKETS || newfd < 0 || newfd >= MAX_SOCKETS) {
fprintf(stderr, "preload dup(): fd out of bounds (%i/%i/%i)\n", oldfd, newfd, ret);
return ret;
}
if (real_sockets[newfd].open) {
if (real_sockets[newfd].used_domain == AF_UNIX && real_sockets[newfd].unix_path[0])
unlink(real_sockets[newfd].unix_path);
}
real_sockets[newfd] = real_sockets[oldfd];
return ret;
}
ssize_t recvmsg(int fd, struct msghdr *msg, int flags) {
const char *err;
ssize_t (*real_recvmsg)(int, struct msghdr *, int) = dlsym(RTLD_NEXT, "recvmsg");
err = "fd out of bounds";
if (fd < 0 || fd >= MAX_SOCKETS)
goto do_recvmsg_warn;
socket_t *s = &real_sockets[fd];
err = "fd not open";
if (!s->open)
goto do_recvmsg_warn;
if (s->used_domain != AF_UNIX || s->wanted_domain == AF_UNIX)
goto do_recvmsg;
struct sockaddr_un sun;
struct sockaddr *sa_orig = NULL;
socklen_t sa_len;
if (msg->msg_name) {
sa_orig = msg->msg_name;
sa_len = msg->msg_namelen;
msg->msg_name = &sun;
msg->msg_namelen = sizeof(sun);
}
ssize_t ret = real_recvmsg(fd, msg, flags);
if (ret <= 0)
goto out;
if (sa_orig && msg->msg_name) {
assert(sun.sun_family == AF_UNIX);
char *path = sun.sun_path;
assert(strlen(path) > 0);
const char *pref = path_prefix();
err = "received from unknown peer";
if (strncmp(path, pref, strlen(pref)))
goto out_warn;
path += strlen(pref);
if (path[0] != '/')
goto out_warn;
path++;
if (!strncmp(path, "ANON.", 5)) {
pthread_mutex_lock(&remote_peers_lock);
peer_t *p = NULL;
for (unsigned int i = 0; i < anon_peer_inc; i++) {
p = &remote_peers[i];
if (!strcmp(p->path.sun_path, path))
goto got_peer;
}
assert(anon_peer_inc < MAX_SOCKETS);
// generate new fake remote response address
p = &remote_peers[anon_peer_inc++];
p->path = sun;
anon_addr(s->wanted_domain, &p->address, anon_peer_inc, getpid());
got_peer:
pthread_mutex_unlock(&remote_peers_lock);
assert(sizeof(p->address) >= sa_len);
memcpy(sa_orig, &p->address, sa_len);
}
else if (path[0] == '[') {
path++;
char *end = strchr(path, ']');
assert(end != NULL);
char addr[64];
if (snprintf(addr, sizeof(addr), "%.*s", (int) (end - path), path) >= sizeof(addr))
abort();
end++;
assert(*end == ':');
end++;
int port = atoi(end);
assert(port != 0);
struct sockaddr_in sin = {0,};
struct sockaddr_in6 sin6 = {0,};
socklen_t addrlen;
struct sockaddr *sa = NULL;
if (inet_pton(AF_INET, addr, &sin.sin_addr)) {
sin.sin_family = AF_INET;
sin.sin_port = htons(port);
sa = (struct sockaddr *) &sin;
addrlen = sizeof(sin);
}
else if (inet_pton(AF_INET6, addr, &sin6.sin6_addr)) {
sin6.sin6_family = AF_INET6;
sin6.sin6_port = htons(port);
sa = (struct sockaddr *) &sin6;
addrlen = sizeof(sin6);
}
else
abort();
assert(addrlen >= sa_len);
memcpy(sa_orig, sa, sa_len);
}
else
abort();
msg->msg_name = sa_orig;
msg->msg_namelen = sa_len;
}
goto out;
out_warn:
fprintf(stderr, "preload recvmsg(): %s (fd %i)\n", err, fd);
out:
return ret;
do_recvmsg_warn:
fprintf(stderr, "preload recvmsg(): %s (fd %i)\n", err, fd);
do_recvmsg:
return real_recvmsg(fd, msg, flags);
}
ssize_t send(int fd, const void *buf, size_t len, int flags) {
check_bind(fd);
ssize_t (*real_send)(int, const void *, size_t, int) = dlsym(RTLD_NEXT, "send");
return real_send(fd, buf, len, flags);
}
static const struct sockaddr *addr_find(const struct sockaddr *addr, socklen_t *addrlen) {
pthread_mutex_lock(&remote_peers_lock);
for (unsigned int i = 0; i < anon_peer_inc; i++) {
peer_t *p = &remote_peers[i];
if (p->address.ss_family != addr->sa_family)
continue;
switch (p->address.ss_family) {
case AF_INET:{
struct sockaddr_in *a = (struct sockaddr_in *) addr,
*b = (struct sockaddr_in *) &p->address;
if (a->sin_port != b->sin_port)
continue;
if (a->sin_addr.s_addr != b->sin_addr.s_addr)
continue;
break;
}
case AF_INET6:{
struct sockaddr_in6 *a = (struct sockaddr_in6 *) addr,
*b = (struct sockaddr_in6 *) &p->address;
if (a->sin6_port != b->sin6_port)
continue;
if (memcmp(&a->sin6_addr, &b->sin6_addr, sizeof(a->sin6_addr)))
continue;
break;
}
default:
continue;
}
// match
*addrlen = sizeof(p->path);
pthread_mutex_unlock(&remote_peers_lock);
return (struct sockaddr *) &p->path;
}
pthread_mutex_unlock(&remote_peers_lock);
return NULL;
}
static const struct sockaddr *addr_send_translate(const struct sockaddr *addr, socklen_t *addrlen) {
const struct sockaddr *ret = addr_find(addr, addrlen);
if (ret)
return ret;
static __thread struct sockaddr_un sun;
const char *err = addr_translate(&sun, addr, *addrlen, 0);
if (!err) {
*addrlen = sizeof(sun);
return (void *) &sun;
}
if (err[0])
fprintf(stderr, "preload addr_send_translate(): %s\n", err);
return addr;
}
ssize_t sendto(int fd, const void *buf, size_t len, int flags, const struct sockaddr *addr, socklen_t addrlen) {
check_bind(fd);
ssize_t (*real_sendto)(int, const void *, size_t, int, const struct sockaddr *, socklen_t)
= dlsym(RTLD_NEXT, "sendto");
addr = addr_send_translate(addr, &addrlen);
return real_sendto(fd, buf, len, flags, addr, addrlen);
}
ssize_t sendmsg(int fd, const struct msghdr *msg, int flags) {
check_bind(fd);
ssize_t (*real_sendmsg)(int, const struct msghdr *, int) = dlsym(RTLD_NEXT, "sendmsg");
struct msghdr msg2 = *msg;
if (msg2.msg_name)
msg2.msg_name = (void *) addr_send_translate(msg2.msg_name, &msg2.msg_namelen);
return real_sendmsg(fd, &msg2, flags);
}
int setsockopt(int fd, int level, int optname, const void *optval, socklen_t optlen) {
const char *err;
int (*real_setsockopt)(int, int, int, const void *, socklen_t) = dlsym(RTLD_NEXT, "setsockopt");
err = "fd out of bounds";
if (fd < 0 || fd >= MAX_SOCKETS)
goto do_set_warn;
socket_t *s = &real_sockets[fd];
err = "fd not open";
if (!s->open)
goto do_set_warn;
assert(s->used_domain == AF_UNIX);
switch (s->wanted_domain) {
case AF_INET:
if (level == SOL_IP && optname == IP_TOS)
return 0;
break;
case AF_INET6:
if (level == SOL_IPV6 && optname == IPV6_V6ONLY)
return 0;
if (level == SOL_IPV6 && optname == IPV6_TCLASS)
return 0;
break;
}
goto do_set;
do_set_warn:
fprintf(stderr, "preload setsockopt(): %s (fd %i)\n", err, fd);
do_set:
return real_setsockopt(fd, level, optname, optval, optlen);
}
Loading…
Cancel
Save