Skip to content
Snippets Groups Projects
Commit 9cded6eb authored by Joshua Colp's avatar Joshua Colp Committed by Gerrit Code Review
Browse files

Merge "Add sipp-sendfax.xml and spandspflow2pcap.py to contrib/scripts."

parents c8e786ff 64b2046f
No related branches found
No related tags found
No related merge requests found
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE scenario SYSTEM "sipp.dtd">
<scenario name="INVITE (optional auth), re-INVITE to T38 and send a Fax, Walter Doekes 2012-2013">
<!--
NOTE: Creating a sipp-sendfax.pcap is as easy as:
- receive a fax with asterisk
- get the incoming side of the spansdp.log (use 'fax set debug on',
check your logger.conf)
- feed it to spandspflow2pcap.py
NOTE: sipp-sendfax.xml requires image pcap play support in SIPp. This
means a version above 3.5.0, or the master git branch from
https://github.com/SIPp/sipp.
-->
<label id="invite"/>
<send retrans="500" start_txn="invite">
<![CDATA[
INVITE sip:[tel]@[remote_ip]:[remote_port] SIP/2.0
Via: SIP/2.0/[transport] [local_ip]:[local_port];branch=[branch]
From: sip:[service]@[local_ip]:[local_port];tag=[pid]SIPpTag00[call_number]
To: sip:[tel]@[remote_ip]:[remote_port]
Contact: sip:[service]@[local_ip]:[local_port]
Call-ID: [call_id]
CSeq: [cseq] INVITE
Max-Forwards: 70
Content-Type: application/sdp
Content-Length: [len]
v=0
o=- 144969 144969 IN IP[local_ip_type] [local_ip]
s=-
c=IN IP[media_ip_type] [media_ip]
t=0 0
m=audio [media_port] RTP/AVP 8 0
a=rtpmap:8 PCMA/8000
a=rtpmap:0 PCMU/8000
]]>
</send>
<recv response="100" optional="true" response_txn="invite"/>
<recv response="180" optional="true" response_txn="invite"/>
<recv response="181" optional="true" response_txn="invite"/>
<recv response="183" optional="true" response_txn="invite"/>
<recv response="200" optional="true" rrs="true" response_txn="invite" next="invite-ack"/>
<recv response="401" optional="true" rrs="true" next="invite-with-auth" auth="true" rrs="true" response_txn="invite"/>
<recv response="407" auth="true" rrs="true" response_txn="invite"/>
<label id="invite-with-auth"/>
<send ack_txn="invite">
<![CDATA[
ACK sip:[tel]@[remote_ip]:[remote_port] SIP/2.0
[last_Via:]
[last_From:]
[last_To:]
Contact: sip:[service]@[local_ip]:[local_port]
Call-ID: [call_id]
CSeq: [cseq] ACK
Max-Forwards: 70
Content-Length: 0
]]>
</send>
<send retrans="500" start_txn="invite">
<![CDATA[
INVITE sip:[tel]@[remote_ip]:[remote_port] SIP/2.0
Via: SIP/2.0/[transport] [local_ip]:[local_port];branch=[branch]
From: sip:[service]@[local_ip]:[local_port];tag=[pid]SIPpTag00[call_number]
To: sip:[tel]@[remote_ip]:[remote_port]
Contact: sip:[service]@[local_ip]:[local_port]
[authentication]
Call-ID: [call_id]
CSeq: [cseq] INVITE
Max-Forwards: 70
Content-Type: application/sdp
Content-Length: [len]
v=0
o=- 144969 144969 IN IP[local_ip_type] [local_ip]
s=-
c=IN IP[media_ip_type] [media_ip]
t=0 0
m=audio [media_port] RTP/AVP 8 0
a=rtpmap:8 PCMA/8000
a=rtpmap:0 PCMU/8000
]]>
</send>
<recv response="100" response_txn="invite"/>
<recv response="180" optional="true" response_txn="invite"/>
<recv response="181" optional="true" response_txn="invite"/>
<recv response="183" optional="true" response_txn="invite"/>
<recv response="200" rrs="true" response_txn="invite"/>
<label id="invite-ack"/>
<send ack_txn="invite">
<![CDATA[
ACK [next_url] SIP/2.0
[last_Via:]
[routes]
[last_From:]
[last_To:]
Contact: sip:[service]@[local_ip]:[local_port]
Call-ID: [call_id]
CSeq: [cseq] ACK
Max-Forwards: 70
Content-Length: 0
]]>
</send>
<!-- Send a bit of noise to start the RTP. You may need to use
the -i MY_IP command line option. -->
<pause milliseconds="500"/>
<nop>
<action>
<exec play_pcap_audio="g711a.pcap"/>
</action>
</nop>
<pause milliseconds="500"/>
<!-- *****************************************************************
Initiate re-INVITE to T38.
****************************************************************** -->
<send start_txn="t38invite">
<![CDATA[
INVITE [next_url] SIP/2.0
Via: SIP/2.0/[transport] [local_ip]:[local_port];branch=[branch]
[routes]
[last_From:]
[last_To:]
Contact: sip:[service]@[local_ip]:[local_port]
Call-ID: [call_id]
CSeq: [cseq] INVITE
Max-Forwards: 70
Content-Type: application/sdp
Content-Length: [len]
v=0
o=- 145280 145280 IN IP[local_ip_type] [local_ip]
s=-
c=IN IP[media_ip_type] [media_ip]
t=0 0
m=image [media_port] udptl t38
a=T38FaxVersion:0
a=T38MaxBitRate:14400
a=T38FaxRateManagement:transferredTCF
a=T38FaxMaxBuffer:200
a=T38FaxMaxDatagram:200
a=T38FaxUdpEC:t38UDPRedundancy
]]>
</send>
<recv response="100" optional="true" response_txn="t38invite"/>
<recv response="488" optional="true" response_txn="t38invite" next="abort"/>
<recv response="200" response_txn="t38invite"/>
<send ack_txn="t38invite">
<![CDATA[
ACK [next_url] SIP/2.0
[last_Via:]
[routes]
[last_From:]
[last_To:]
Contact: sip:[service]@[local_ip]:[local_port]
Call-ID: [call_id]
CSeq: [cseq] ACK
Max-Forwards: 70
Content-Length: 0
]]>
</send>
<!-- Enable the nop/action below to replay the FAX image. You may need to use
the -i MY_IP command line option. -->
<pause milliseconds="500"/>
<nop>
<action>
<exec play_pcap_image="sipp-sendfax.pcap"/>
</action>
</nop>
<!-- *****************************************************************
Wait for re-INVITE back to audio.
****************************************************************** -->
<recv request="INVITE"/>
<send retrans="500">
<![CDATA[
SIP/2.0 200 OK
[last_Via:]
[last_Record-Route:]
[last_From:]
[last_To:]
Contact: sip:[service]@[local_ip]:[local_port]
[last_Call-ID:]
[last_CSeq:]
Max-Forwards: 70
Content-Type: application/sdp
Content-Length: [len]
v=0
o=- 146312 146312 IN IP[local_ip_type] [local_ip]
s=-
c=IN IP[media_ip_type] [media_ip]
t=0 0
m=audio [media_port] RTP/AVP 8 0
a=rtpmap:8 PCMA/8000
a=rtpmap:0 PCMU/8000
]]>
</send>
<recv request="ACK"/>
<recv request="BYE"/>
<send next="done">
<![CDATA[
SIP/2.0 200 OK
[last_Via:]
[last_Record-Route:]
[last_From:]
[last_To:]
Contact: sip:[service]@[local_ip]:[local_port]
[last_Call-ID:]
[last_CSeq:]
Content-Length: 0
]]>
</send>
<!-- *****************************************************************
Abort the call ourselves
****************************************************************** -->
<label id="abort"/>
<send ack_txn="t38invite">
<![CDATA[
ACK [next_url] SIP/2.0
[last_Via:]
[routes]
[last_From:]
[last_To:]
Contact: sip:[service]@[local_ip]:[local_port]
Call-ID: [call_id]
CSeq: [cseq] ACK
Max-Forwards: 70
Content-Length: 0
]]>
</send>
<send start_txn="bye">
<![CDATA[
BYE [next_url] SIP/2.0
[last_Via:]
[routes]
[last_From:]
[last_To:]
Contact: sip:[service]@[local_ip]:[local_port]
Call-ID: [call_id]
CSeq: [cseq] BYE
Max-Forwards: 70
Content-Length: 0
]]>
</send>
<recv response="200" response_txn="bye" next="done"/>
<!-- *****************************************************************
Finalize
****************************************************************** -->
<label id="done"/>
<!-- Keep call open to be able to retransmit stuff -->
<timewait milliseconds="2000"/>
</scenario><!-- vim: set ts=8 sw=2 sts=2 et ai: -->
This diff is collapsed.
#!/usr/bin/env python
# vim: set ts=8 sw=4 sts=4 et ai tw=79:
'''
Usage: ./spandspflow2pcap.py SPANDSP_LOG SENDFAX_PCAP
Takes a log from Asterisk with SpanDSP, extracts the "received" data
and puts it in a pcap file. Use 'fax set debug on' and configure
logger.conf to get fax logs.
Input data should look something like this::
[2013-08-07 15:17:34] FAX[23479] res_fax.c: FLOW T.38 Rx 5: IFP c0 01 ...
Output data will look like a valid pcap file ;-)
This allows you to reconstruct received faxes into replayable pcaps.
Replaying is expected to be done by SIPp with sipp-sendfax.xml. The
SIPp binary used for replaying must have image (fax) support. This means
you'll need a version higher than 3.5.0 (unreleased when writing this),
or the git master branch: https://github.com/SIPp/sipp
Author: Walter Doekes, OSSO B.V. (2013,2015,2016)
License: Public Domain
'''
from base64 import b16decode
from datetime import datetime, timedelta
from re import search
from time import mktime
from struct import pack
import sys
LOSSY = False
EMPTY_RECOVERY = False
def n2b(text):
return b16decode(text.replace(' ', '').replace('\n', '').upper())
class FaxPcap(object):
PCAP_PREAMBLE = n2b('d4 c3 b2 a1 02 00 04 00'
'00 00 00 00 00 00 00 00'
'ff ff 00 00 71 00 00 00')
def __init__(self, outfile):
self.outfile = outfile
self.date = None
self.dateoff = timedelta(seconds=0)
self.seqno = None
self.udpseqno = 128
self.prev_data = None
# Only do this if at pos 0?
self.outfile.write(self.PCAP_PREAMBLE)
def data2packet(self, date, udpseqno, seqno, data, prev_data):
sum16 = '\x43\x21' # checksum is irrelevant for sipp sending
new_prev = data # without seqno..
data = '%s%s' % (pack('>H', seqno), data)
if prev_data:
if LOSSY and (seqno % 3) == 2:
return '', new_prev
if EMPTY_RECOVERY:
# struct ast_frame f[16], we have room for a few
# packets.
packets = 14
data += '\x00%c%s%s' % (
chr(packets + 1), '\x00' * packets, prev_data)
else:
# Add 1 previous packet, without the seqno.
data += '\x00\x01' + prev_data
kwargs = {'udpseqno': pack('>H', udpseqno), 'sum16': sum16}
kwargs['data'] = data
kwargs['lenb16'] = pack('>H', len(kwargs['data']) + 8)
udp = '\x00\x01\x00\x02%(lenb16)s%(sum16)s%(data)s' % kwargs
kwargs['data'] = udp
kwargs['lenb16'] = pack('>H', len(kwargs['data']) + 20)
ip = ('\x45\xb8%(lenb16)s%(udpseqno)s\x00\x00\xf9\x11%(sum16)s\x01'
'\x01\x01\x01\x02\x02\x02\x02%(data)s') % kwargs
kwargs['data'] = ip
frame = ('\x00\x00\x00\x01\x00\x06\x00\x30\x48\xb1\x1c\x34\x00\x00'
'\x08\x00%(data)s') % kwargs
kwargs['data'] = frame
sec = mktime(date.timetuple())
msec = date.microsecond
datalen = len(kwargs['data'])
kwargs['pre'] = pack('<IIII', sec, msec, datalen, datalen)
packet = '%(pre)s%(data)s' % kwargs
return (packet, new_prev)
def add(self, date, seqno, data):
if self.seqno is None:
self.seqno = 0
for i in range(seqno):
# In case the first zeroes were dropped, add them.
self.add(date, i, '\x00')
assert seqno == self.seqno, '%s != %s' % (seqno, self.seqno)
# Data is prepended by len(data).
data = chr(len(data)) + data
# Auto-increasing dates
if self.date is None or date > self.date:
# print 'date is larger', date, self.date
self.date = date
elif (date < self.date.replace(microsecond=0)):
assert False, ('We increased too fast.. decrease delta: %r/%r' %
(date, self.date))
else:
self.date += timedelta(microseconds=9000)
print seqno, '\t', self.date + self.dateoff
# Make packet.
packet, prev_data = self.data2packet(self.date + self.dateoff,
self.udpseqno, self.seqno,
data, self.prev_data)
self.outfile.write(packet)
# Increase values.
self.udpseqno += 1
self.seqno += 1
self.prev_data = prev_data
def add_garbage(self, date):
if self.date is None or date > self.date:
self.date = date
packet, ignored = self.data2packet(self.date, self.udpseqno,
0xffff, 'GARBAGE', '')
self.udpseqno += 1
self.outfile.write(packet)
with open(sys.argv[1], 'r') as infile:
with open(sys.argv[2], 'wb') as outfile:
first = True
p = FaxPcap(outfile)
# p.add(datetime.now(), 0, n2b('06'))
# p.add(datetime.now(), 1, n2b('c0 01 80 00 00 ff'))
for lineno, line in enumerate(infile):
# Look for lines like:
# [2013-08-07 15:17:34] FAX[23479] res_fax.c: \
# FLOW T.38 Rx 5: IFP c0 01 80 00 00 ff
if 'FLOW T.38 Rx' not in line:
continue
if 'IFP' not in line:
continue
match = search(r'(\d{4})-(\d\d)-(\d\d) (\d\d):(\d\d):(\d\d)', line)
assert match
date = datetime(*[int(i) for i in match.groups()])
match = search(r'Rx\s*(\d+):', line)
assert match
seqno = int(match.groups()[0])
match = search(r': IFP ([0-9a-f ]+)', line)
assert match
data = n2b(match.groups()[0])
# Have the file start a second early.
if first:
p.add_garbage(date)
first = False
# Add the packets.
#
# T.38 basic format of UDPTL payload section with redundancy:
#
# UDPTL_SEQNO
# - 2 sequence number (big endian)
# UDPTL_PRIMARY_PAYLOAD (T30?)
# - 1 subpacket length (excluding this byte)
# - 1 type of message (e.g. 0xd0 for data(?))
# - 1 items in data field (e.g. 0x01)
# - 2 length of data (big endian)
# - N data
# RECOVERY (optional)
# - 2 count of previous seqno packets (big endian)
# - N UDPTL_PRIMARY_PAYLOAD of (seqno-1)
# - N UDPTL_PRIMARY_PAYLOAD of (seqno-2)
# - ...
#
p.add(date, seqno, data)
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment