diff --git a/daemon/Makefile b/daemon/Makefile index 527963de3..07207da7e 100644 --- a/daemon/Makefile +++ b/daemon/Makefile @@ -32,7 +32,7 @@ LDFLAGS+= `dpkg-buildflags --get LDFLAGS` endif SRCS= main.c kernel.c poller.c aux.c control_tcp.c streambuf.c call.c control_udp.c redis.c \ - bencode.c cookie_cache.c udp_listener.c control_ng.c sdp.c str.c stun.c + bencode.c cookie_cache.c udp_listener.c control_ng.c sdp.c str.c stun.c rtcp.c OBJS= $(SRCS:.c=.o) diff --git a/daemon/call.c b/daemon/call.c index fa6a07269..f0db9e71f 100644 --- a/daemon/call.c +++ b/daemon/call.c @@ -27,6 +27,7 @@ #include "sdp.h" #include "str.h" #include "stun.h" +#include "rtcp.h" @@ -111,6 +112,12 @@ static char *rtp_codecs[] = { [33] = "MP2T", [34] = "H263", }; +const char *transport_protocol_strings[__PROTO_RTP_LAST] = { + [PROTO_RTP_AVP] = "RTP/AVP", + [PROTO_RTP_SAVP] = "RTP/SAVP", + [PROTO_RTP_AVPF] = "RTP/AVPF", + [PROTO_RTP_SAVPF] = "RTP/SAVPF", +}; @@ -208,12 +215,39 @@ void kernelize(struct callstream *c) { +int __dummy_stream_handler(str *s) { + abort(); + return 0; +} + +static stream_handler determine_handler(struct streamrelay *in) { + if (in->peer.protocol == in->peer_advertised.protocol) + goto dummy; + if (in->peer.protocol == PROTO_UNKNOWN) + goto dummy; + if (in->peer_advertised.protocol == PROTO_UNKNOWN) + goto dummy; + + if (in->peer.protocol == PROTO_RTP_AVPF && in->peer_advertised.protocol == PROTO_RTP_AVP) { + if (!in->rtcp) + goto dummy; + return rtcp_avpf2avp; + } + if (in->peer.protocol == PROTO_RTP_AVP && in->peer_advertised.protocol == PROTO_RTP_AVPF) + goto dummy; + + /* XXX warn? */ + +dummy: + return __dummy_stream_handler; +} + /* called with r->up (== cs) locked */ static int stream_packet(struct streamrelay *sr_incoming, str *s, struct sockaddr_in6 *fsin) { struct streamrelay *sr_outgoing, *sr_out_rtcp; struct peer *p_incoming, *p_outgoing; struct callstream *cs_incoming; - int ret, update = 0, stun_ret = 0; + int ret, update = 0, stun_ret = 0, handler_ret = 0; struct sockaddr_in sin; struct sockaddr_in6 sin6; struct msghdr mh; @@ -255,6 +289,11 @@ static int stream_packet(struct streamrelay *sr_incoming, str *s, struct sockadd return 0; } + if (!sr_incoming->handler) + sr_incoming->handler = determine_handler(sr_incoming); + if (sr_incoming->handler != __dummy_stream_handler) + handler_ret = sr_incoming->handler(s); + use_cand: if (p_incoming->confirmed || !p_incoming->filled || sr_incoming->idx != 0) goto forward; @@ -283,15 +322,18 @@ peerinfo: sr_out_rtcp->peer.ip46 = sr_outgoing->peer.ip46; sr_out_rtcp->peer.port = sr_outgoing->peer.port + 1; /* sr_out_rtcp->idx == 1 */ + update = 1; + + if (sr_incoming->handler != __dummy_stream_handler) + goto forward; + if (p_incoming->confirmed && p_outgoing->confirmed && p_outgoing->filled) kernelize(cs_incoming); - update = 1; - forward: if (is_addr_unspecified(&sr_incoming->peer_advertised.ip46) || !sr_incoming->peer_advertised.port || !sr_incoming->fd.fd_family - || stun_ret) + || stun_ret || handler_ret) goto drop; ZERO(mh); @@ -1017,11 +1059,11 @@ static int setup_peer(struct peer *p, struct stream_input *s, const str *tag) { a->peer.port = b->peer.port = s->stream.port; if (b->peer.port) b->peer.port++; + a->peer.protocol = b->peer.protocol = s->stream.protocol; a->peer_advertised = a->peer; b->peer_advertised = b->peer; a->rtcp = s->is_rtcp; b->rtcp = 1; - p->protocol = s->protocol; for (i = 0; i < 2; i++) { switch (s->direction[i]) { @@ -1072,7 +1114,6 @@ static void steal_peer(struct peer *dest, struct peer *src) { dest->desired_family = src->desired_family; dest->ice_ufrag = src->ice_ufrag; dest->ice_pwd = src->ice_pwd; - dest->protocol = src->protocol; for (i = 0; i < 2; i++) { sr = &dest->rtps[i]; @@ -2302,6 +2343,8 @@ static void call_ng_process_flags(struct sdp_ng_flags *out, GQueue *streams, ben else if (!str_cmp(&s, "force")) out->ice_force = 1; } + + bencode_dictionary_get_str(input, "transport-protocol", &out->transport_protocol); } static unsigned int stream_hash(struct stream_input *s) { diff --git a/daemon/call.h b/daemon/call.h index b6ef096d9..ad1d2a8a5 100644 --- a/daemon/call.h +++ b/daemon/call.h @@ -53,7 +53,10 @@ enum transport_protocol { PROTO_RTP_SAVP, PROTO_RTP_AVPF, PROTO_RTP_SAVPF, + + __PROTO_RTP_LAST }; +extern const char *transport_protocol_strings[__PROTO_RTP_LAST]; struct stats { u_int64_t packets; @@ -65,12 +68,12 @@ struct stream { struct in6_addr ip46; u_int16_t port; int num; + enum transport_protocol protocol; }; struct stream_input { struct stream stream; enum stream_direction direction[2]; int consecutive_num; - enum transport_protocol protocol; int has_rtcp:1; int is_rtcp:1; }; @@ -79,6 +82,10 @@ struct udp_fd { int fd_family; u_int16_t localport; }; + +typedef int (*stream_handler)(str *); +int __dummy_stream_handler(str *); + struct streamrelay { struct udp_fd fd; struct stream peer; @@ -89,6 +96,7 @@ struct streamrelay { struct stats stats; struct stats kstats; time_t last; + stream_handler handler; int stun:1; int rtcp:1; }; @@ -108,7 +116,6 @@ struct peer { int desired_family; str ice_ufrag; str ice_pwd; - enum transport_protocol protocol; int kernelized:1; int filled:1; int confirmed:1; diff --git a/daemon/rtcp.c b/daemon/rtcp.c new file mode 100644 index 000000000..ed9ef2801 --- /dev/null +++ b/daemon/rtcp.c @@ -0,0 +1,322 @@ +#include "rtcp.h" + +#include +#include +#include + +#include "str.h" +#include "call.h" +#include "log.h" + + + + +#define RTCP_PT_SR 200 /* sender report */ +#define RTCP_PT_RR 201 /* receiver report */ +#define RTCP_PT_SDES 202 /* source description */ +#define RTCP_PT_BYE 203 /* bye */ +#define RTCP_PT_APP 204 /* application specific */ +#define RTCP_PT_RTPFB 205 /* transport layer feedback message (RTP/AVPF) */ +#define RTCP_PT_PSFB 206 /* payload-specific feedback message (RTP/AVPF) */ + +#define SDES_TYPE_END 0 +#define SDES_TYPE_CNAME 1 +#define SDES_TYPE_NAME 2 +#define SDES_TYPE_EMAIL 3 +#define SDES_TYPE_PHONE 4 +#define SDES_TYPE_LOC 5 +#define SDES_TYPE_TOOL 6 +#define SDES_TYPE_NOTE 7 +#define SDES_TYPE_PRIV 8 + + + +struct rtcp_header { + unsigned char v_p_x; + unsigned char pt; + u_int16_t length; +} __attribute__ ((packed)); + +struct rtcp_packet { + struct rtcp_header header; + u_int32_t ssrc; +} __attribute__ ((packed)); + +struct report_block { + u_int32_t ssrc; + unsigned char fraction_lost; + unsigned char number_lost[3]; + u_int32_t high_seq_received; + u_int32_t jitter; + u_int32_t lsr; + u_int32_t dlsr; +} __attribute__ ((packed)); + +struct sender_report_packet { + struct rtcp_packet rtcp; + u_int32_t ntp_msw; + u_int32_t ntp_lsw; + u_int32_t timestamp; + u_int32_t packet_count; + u_int32_t octet_count; + struct report_block reports[0]; +} __attribute__ ((packed)); + +struct receiver_report_packet { + struct rtcp_packet rtcp; + struct report_block reports[0]; +} __attribute__ ((packed)); + +struct sdes_item { + unsigned char type; + unsigned char length; + unsigned char data[0]; +} __attribute__ ((packed)); + +struct sdes_chunk { + u_int32_t ssrc; + struct sdes_item items[0]; +} __attribute__ ((packed)); + +struct source_description_packet { + struct rtcp_header header; + struct sdes_chunk chunks[0]; + +} __attribute__ ((packed)); + +struct bye_packet { + struct rtcp_header header; + u_int32_t ssrcs[0]; +} __attribute__ ((packed)); + +struct app_packet { + struct rtcp_packet rtcp; + unsigned char name[4]; + unsigned char data[0]; +} __attribute__ ((packed)); + +struct fb_packet { + struct rtcp_packet rtcp; + u_int32_t media_ssrc; + unsigned char information[0]; +} __attribute__ ((packed)); + +struct rtcp_chain_element { + int type; + unsigned int len; + union { + void *buf; + struct rtcp_packet *rtcp_packet; + struct sender_report_packet *sr; + struct receiver_report_packet *rr; + struct source_description_packet *sdes; + struct bye_packet *bye; + struct app_packet *app; + } u; +}; + + + + + +typedef struct rtcp_chain_element *(*rtcp_handler_func)(str *); + +static struct rtcp_chain_element *rtcp_sr(str *s); +static struct rtcp_chain_element *rtcp_rr(str *s); +static struct rtcp_chain_element *rtcp_sdes(str *s); +static struct rtcp_chain_element *rtcp_bye(str *s); +static struct rtcp_chain_element *rtcp_app(str *s); +static struct rtcp_chain_element *rtcp_rtpfb(str *s); +static struct rtcp_chain_element *rtcp_psfb(str *s); + + + + + +static const rtcp_handler_func handler_funcs[] = { + [RTCP_PT_SR] = rtcp_sr, + [RTCP_PT_RR] = rtcp_rr, + [RTCP_PT_SDES] = rtcp_sdes, + [RTCP_PT_BYE] = rtcp_bye, + [RTCP_PT_APP] = rtcp_app, + [RTCP_PT_RTPFB] = rtcp_rtpfb, + [RTCP_PT_PSFB] = rtcp_psfb, +}; + + + + + + +static void *rtcp_length_check(str *s, size_t min_len, unsigned int *len_p) { + struct rtcp_header *h; + + if (s->len < min_len) + return NULL; + + h = (void *) s->s; + *len_p = (ntohs(h->length) + 1) << 2; + + if (*len_p > s->len) + return NULL; + + return h; +} + +static struct rtcp_chain_element *rtcp_new_element(void *p, unsigned int len, int type) { + struct rtcp_chain_element *el; + + el = g_slice_alloc(sizeof(*el)); + el->type = type; + el->len = len; + el->u.buf = p; + + return el; +} + +static struct rtcp_chain_element *rtcp_generic(str *s, int type) { + struct rtcp_header *p; + unsigned int len; + + if (!(p = rtcp_length_check(s, sizeof(*p), &len))) + return NULL; + + return rtcp_new_element(p, len, type); +} + +static struct rtcp_chain_element *rtcp_Xr(str *s, int type, size_t struct_len) { + struct rtcp_packet *p; + unsigned int len; + + if (!(p = rtcp_length_check(s, struct_len, &len))) + return NULL; + + if (len < (p->header.v_p_x & 0x1f) * sizeof(struct report_block)) + return NULL; + + return rtcp_new_element(p, len, type); +} + +static struct rtcp_chain_element *rtcp_sr(str *s) { + return rtcp_Xr(s, RTCP_PT_SR, sizeof(struct sender_report_packet)); +} + +static struct rtcp_chain_element *rtcp_rr(str *s) { + return rtcp_Xr(s, RTCP_PT_RR, sizeof(struct receiver_report_packet)); +} + +static struct rtcp_chain_element *rtcp_sdes(str *s) { + struct source_description_packet *p; + unsigned int len; + + if (!(p = rtcp_length_check(s, sizeof(*p), &len))) + return NULL; + + /* sdes items ... */ + + return rtcp_new_element(p, len, RTCP_PT_SDES); +} + +static struct rtcp_chain_element *rtcp_bye(str *s) { + return rtcp_generic(s, RTCP_PT_BYE); +} + +static struct rtcp_chain_element *rtcp_app(str *s) { + return rtcp_generic(s, RTCP_PT_APP); +} + +static struct rtcp_chain_element *rtcp_rtpfb(str *s) { + return rtcp_generic(s, RTCP_PT_RTPFB); +} + +static struct rtcp_chain_element *rtcp_psfb(str *s) { + return rtcp_generic(s, RTCP_PT_PSFB); +} + +static void rtcp_list_free_cb(void *d) { + g_slice_free1(sizeof(struct rtcp_chain_element), d); +} +static void rtcp_list_free(GQueue *q) { + g_queue_free_full(q, rtcp_list_free_cb); +} + +static int rtcp_parse(GQueue *q, str *_s) { + struct rtcp_packet *hdr; + struct rtcp_chain_element *el; + rtcp_handler_func func; + str s = *_s; + + while (1) { + if (s.len < sizeof(*hdr)) + break; + + hdr = (void *) s.s; + + if ((hdr->header.v_p_x & 0xc0) != 0x80) /* version 2 */ + goto error; + + if (hdr->header.pt >= ARRAYSIZE(handler_funcs)) + goto error; + func = handler_funcs[hdr->header.pt]; + if (!func) + goto error; + + el = func(&s); + if (!el) + goto error; + + g_queue_push_tail(q, el); + + if (str_shift(&s, el->len)) + abort(); + } + + return 0; + +error: + rtcp_list_free(q); + return -1; +} + +int rtcp_avpf2avp(str *s) { + GQueue rtcp_list = G_QUEUE_INIT; + GList *l; + struct rtcp_chain_element *el; + void *start; + unsigned int removed, left; + + if (rtcp_parse(&rtcp_list, s)) + return 0; + + left = s->len; + removed = 0; + for (l = rtcp_list.head; l; l = l->next) { + el = l->data; + left -= el->len; + + switch (el->type) { + case RTCP_PT_SR: + case RTCP_PT_RR: + case RTCP_PT_SDES: + case RTCP_PT_BYE: + case RTCP_PT_APP: + break; + + case RTCP_PT_RTPFB: + case RTCP_PT_PSFB: + start = el->u.buf; + memmove(start - removed, start + el->len - removed, left); + removed += el->len; + break; + + default: + abort(); + } + } + + s->len -= removed; + if (!s->len) + return -1; + + return 0; +} diff --git a/daemon/rtcp.h b/daemon/rtcp.h new file mode 100644 index 000000000..32674a664 --- /dev/null +++ b/daemon/rtcp.h @@ -0,0 +1,8 @@ +#ifndef _RTCP_H_ +#define _RTCP_H_ + +#include "str.h" + +int rtcp_avpf2avp(str *); + +#endif diff --git a/daemon/sdp.c b/daemon/sdp.c index d38e8b874..09421c281 100644 --- a/daemon/sdp.c +++ b/daemon/sdp.c @@ -562,15 +562,23 @@ static enum transport_protocol transport_protocol(str *s) { case 7: if (!str_cmp(s, "RTP/AVP")) return PROTO_RTP_AVP; + if (!str_cmp(s, "rtp/avp")) + return PROTO_RTP_AVP; break; case 8: if (!str_cmp(s, "RTP/SAVP")) return PROTO_RTP_SAVP; + if (!str_cmp(s, "rtp/savp")) + return PROTO_RTP_SAVP; if (!str_cmp(s, "RTP/AVPF")) return PROTO_RTP_AVPF; + if (!str_cmp(s, "rtp/avpf")) + return PROTO_RTP_AVPF; break; case 9: - if (!str_cmp(s, "RTP/SAVPF")) + if (!str_cmp(s, "rtp/savpf")) + return PROTO_RTP_SAVPF; + if (!str_cmp(s, "rtp/savpf")) return PROTO_RTP_SAVPF; break; } @@ -611,7 +619,7 @@ int sdp_streams(const GQueue *sessions, GQueue *streams, GHashTable *streamhash, si->stream.num = ++num; si->consecutive_num = (i == 0) ? media->port_count : 1; - si->protocol = tp; + si->stream.protocol = tp; g_hash_table_insert(streamhash, si, si); g_queue_push_tail(streams, si); @@ -634,7 +642,7 @@ int sdp_streams(const GQueue *sessions, GQueue *streams, GHashTable *streamhash, si->stream.num = ++num; si->consecutive_num = 1; si->is_rtcp = 1; - si->protocol = tp; + si->stream.protocol = tp; g_hash_table_insert(streamhash, si, si); g_queue_push_tail(streams, si); @@ -748,6 +756,24 @@ static void fill_relays(struct streamrelay **rtp, struct streamrelay **rtcp, GLi } } +static int replace_transport_protocol(struct sdp_chopper *chop, + struct sdp_media *media, struct streamrelay *sr) +{ + str *tp = &media->transport; + const char *new_tp = transport_protocol_strings[sr->peer.protocol]; + + if (!new_tp) + return 0; /* XXX correct? or give warning? */ + + if (copy_up_to(chop, tp)) + return -1; + chopper_append_c(chop, new_tp); + if (skip_over(chop, tp)) + return -1; + + return 0; +} + static int replace_media_port(struct sdp_chopper *chop, struct sdp_media *media, struct streamrelay *sr) { str *port = &media->port; @@ -1096,10 +1122,15 @@ int sdp_replace(struct sdp_chopper *chop, GQueue *sessions, struct call *call, goto error; fill_relays(&rtp, &rtcp, m, off, sip); + rtp->peer.protocol = transport_protocol(&flags->transport_protocol); + rtcp->peer.protocol = rtp->peer.protocol; + if (replace_media_port(chop, media, rtp)) goto error; if (replace_consecutive_port_count(chop, media, rtp, m, off)) goto error; + if (replace_transport_protocol(chop, media, rtp)) + goto error; if (media->connection.parsed && flags->replace_sess_conn) { if (replace_network_address(chop, &media->connection.address, rtp)) diff --git a/daemon/sdp.h b/daemon/sdp.h index 8d633315f..a92304b57 100644 --- a/daemon/sdp.h +++ b/daemon/sdp.h @@ -10,6 +10,7 @@ struct sdp_ng_flags { int desired_family[2]; str received_from_family; str received_from_address; + str transport_protocol; struct in6_addr parsed_address; int asymmetric:1, symmetric:1, diff --git a/tests/simulator-ng.pl b/tests/simulator-ng.pl index 3a657041f..d34434176 100755 --- a/tests/simulator-ng.pl +++ b/tests/simulator-ng.pl @@ -8,6 +8,7 @@ use BSD::Resource; use Getopt::Long; use Socket6; use Bencode qw( bencode bdecode ); +use Time::HiRes; my ($NUM, $RUNTIME, $STREAMS) = (1000, 30, 1); my ($NODEL, $IP, $IPV6, $KEEPGOING, $REINVITES, $BRANCHES); @@ -62,28 +63,79 @@ msg({command => 'ping'})->{result} eq 'pong' or die; my (@calls, %branches); +sub send_receive { + my ($send_fd, $receive_fd, $payload, $destination) = @_; + + send($send_fd, $payload, 0, $destination) or die $!; + my $x; + my $err = ''; + alarm(1); + recv($receive_fd, $x, 0xffff, 0) or $err = "$!"; + alarm(0); + $err && $err !~ /interrupt/i and die $err; + return $x; +} + +sub send_expect { + my ($send_fd, $receive_fd, $payload, $expect, $destination) = @_; + + my $x = send_receive($send_fd, $receive_fd, $payload, $destination); + if (($x || '') ne $expect) { + return 0; + } + return 1; +} + +sub rtcp_sr { + my @now = Time::HiRes::gettimeofday(); + my $secs = $now[0] + 2208988800; + my $frac = $now[1] / 1000000 * 2**32; + my $sr = pack('CCnN NNN NN', (2 << 6) | 1, 200, 12, rand() * 2**32, $secs, $frac, + 12345, 0, 0); + $sr .= pack('N CCCC NNNN', 0, 0, 0, 0, 0, 0, 0, 0, 0); + return $sr; +} + +sub rtcp_rtpfb { + return pack('CCn NN', (2 << 6) | 1, 205, 2, rand() * 2**32, rand() * 2**32); +} + +sub rtcp_avp { + my $sr = rtcp_sr(); + return ($sr, $sr); +} + +sub rtcp_avpf { + my ($recv) = @_; + my $sr = rtcp_sr(); + my $fb = rtcp_rtpfb(); + my $exp = $sr; + $$recv{name} eq 'RTP/AVPF' and $exp .= $fb; + return ($sr . $fb, $exp); +} + sub do_rtp { print("sending rtp\n"); for my $c (@calls) { $c or next; - my ($fds,$outputs,$protos) = @$c[0,4,6]; + my ($fds,$outputs,$protos,$cfds,$trans) = @$c[0,4,6,7,8]; for my $j (0 .. $#{$$fds[0]}) { for my $i ([0,1],[1,0]) { my ($a, $b) = @$i; my $pr = $$protos[$a]; + my $addr = inet_pton($$pr{family}, $$outputs[$b][$j][1]); my $payload = rand_str(100); - send($$fds[$a][$j], $payload, 0, $$pr{sockaddr}($$outputs[$b][$j][0], - inet_pton($$pr{family}, $$outputs[$b][$j][1]))) or die $!; - my $x; - my $err = ''; - alarm(1); - recv($$fds[$b][$j], $x, 0xffff, 0) or $err = "$!"; - alarm(0); - $err && $err !~ /interrupt/i and die $err; - if (($x || '') ne $payload) { + my $dst = $$pr{sockaddr}($$outputs[$b][$j][0], $addr); + if (!send_expect($$fds[$a][$j], $$fds[$b][$j], $payload, $payload, $dst)) { warn("no rtp reply received, ports $$outputs[$b][$j][0] and $$outputs[$a][$j][0]"); $KEEPGOING or undef($c); } + + my $expect; + ($payload, $expect) = $$trans[$a]{func}($$trans[$b]); + $dst = $$pr{sockaddr}($$outputs[$b][$j][0] + 1, $addr); + my $repl = send_receive($$cfds[$a][$j], $$cfds[$b][$j], $payload, $dst); + $repl eq $expect or die; } } } @@ -114,6 +166,17 @@ $IP and push(@protos_avail, $proto_defs{ipv4}); $IPV6 and push(@protos_avail, $proto_defs{ipv6}); my @sides = qw(A B); +my @transports = ( + { + name => 'RTP/AVP', + func => \&rtcp_avp, + }, + { + name => 'RTP/AVPF', + func => \&rtcp_avpf, + }, +); + sub callid { my $i = rand_str(50); $BRANCHES or return [$i]; @@ -135,13 +198,21 @@ sub update_lookup { my ($callid, $viabranch) = @$c_v; my $protos = $$c[6] || ($$c[6] = []); + my $trans = $$c[8] || ($$c[8] = []); my $fds_a = $$c[0] || ($$c[0] = []); + my $cfds_a = $$c[7] || ($$c[7] = []); for my $x (0,1) { $$protos[$x] and next; $$protos[$x] = $protos_avail[rand(@protos_avail)]; undef($$fds_a[$x]); } + for my $x (0,1) { + $$trans[$x] and next; + #$$trans[$x] = $transports[rand(@transports)]; + $$trans[$x] = $transports[rand(@transports)]; + } my ($pr, $pr_o) = @$protos[$i, $j]; + my ($tr, $tr_o) = @$trans[$i, $j]; my @commands = qw(offer answer); my $ports_a = $$c[1] || ($$c[1] = []); @@ -150,15 +221,23 @@ sub update_lookup { my $ips_t = $$ips_a[$i] || ($$ips_a[$i] = []); my $fds_t = $$fds_a[$i] || ($$fds_a[$i] = []); my $fds_o = $$fds_a[$j]; + my $cfds_t = $$cfds_a[$i] || ($$cfds_a[$i] = []); + my $cfds_o = $$cfds_a[$j]; my $num_streams = int(rand($STREAMS)); ($fds_o && @$fds_o) and $num_streams = $#$fds_o; for my $j (0 .. $num_streams) { if (!$$fds_t[$j]) { - socket($$fds_t[$j], $$pr{family}, SOCK_DGRAM, 0) or die $!; while (1) { + undef($$fds_t[$j]); + undef($$cfds_t[$j]); + socket($$fds_t[$j], $$pr{family}, SOCK_DGRAM, 0) or die $!; + socket($$cfds_t[$j], $$pr{family}, SOCK_DGRAM, 0) or die $!; my $port = rand(0x7000) << 1 + 1024; bind($$fds_t[$j], $$pr{sockaddr}($port, - inet_pton($$pr{family}, $$pr{address}))) and last; + inet_pton($$pr{family}, $$pr{address}))) or next; + bind($$cfds_t[$j], $$pr{sockaddr}($port + 1, + inet_pton($$pr{family}, $$pr{address}))) or next; + last; } my $addr = getsockname($$fds_t[$j]); my $ip; @@ -178,9 +257,11 @@ c=IN $$pr{family_str} $$ips_t[0] t=0 0 ! for my $p (@$ports_t) { + my $cp = $p + 1; $sdp .= <<"!"; -m=audio $p RTP/AVP 8 +m=audio $p $$tr{name} 8 a=rtpmap:8 PCMA/8000 +a=rtcp:$cp ! } @@ -190,6 +271,7 @@ a=rtpmap:8 PCMA/8000 replace => [ qw( origin session-connection ) ], direction => [ $$pr{direction}, $$pr_o{direction} ], 'received-from' => [ qw(IP4 127.0.0.1) ], + 'transport-protocol' => $$tr_o{name}, }; $viabranch and $dict->{'via-branch'} = $viabranch; $i == 1 and $dict->{'to-tag'} = $$tags[1]; @@ -197,7 +279,7 @@ a=rtpmap:8 PCMA/8000 my $o = msg($dict); $$o{result} eq 'ok' or die; my ($rp_af, $rp_add) = $$o{sdp} =~ /c=IN IP([46]) (\S+)/s or die; - my @rp_ports = $$o{sdp} =~ /m=audio (\d+)/gs or die; + my @rp_ports = $$o{sdp} =~ /m=audio (\d+) \Q$$tr_o{name}\E /gs or die; $rp_af ne $$pr_o{reply} and die "incorrect address family reply code"; my $rpl_a = $$c[4] || ($$c[4] = []); my $rpl_t = $$rpl_a[$i] || ($$rpl_a[$i] = []);