lib/sippy_cup/scenario.rb in sippy_cup-0.2.1 vs lib/sippy_cup/scenario.rb in sippy_cup-0.2.2
- old
+ new
@@ -1,19 +1,21 @@
require 'nokogiri'
require 'yaml'
module SippyCup
class Scenario
+ USER_AGENT = "SIPp/sippy_cup"
VALID_DTMF = %w{0 1 2 3 4 5 6 7 8 9 0 * # A B C D}.freeze
MSEC = 1_000
def initialize(name, args = {}, &block)
builder = Nokogiri::XML::Builder.new do |xml|
xml.scenario name: name
end
parse_args args
+ @rtcp_port = args[:rtcp_port]
@filename = args[:filename] || name.downcase.gsub(/\W+/, '_')
@filename = File.expand_path @filename
@doc = builder.doc
@media = Media.new '127.0.0.255', 55555, '127.255.255.255', 5060
@scenario_opts = get_scenario_opts args
@@ -51,39 +53,89 @@
@media << "silence:#{seconds * MSEC}"
end
def invite(opts = {})
opts[:retrans] ||= 500
+ rtp_string = @static_rtcp ? "m=audio #{@rtcp_port.to_i - 1} RTP/AVP 0 101\na=rtcp:#{@rtcp_port}\n" : "m=audio [media_port] RTP/AVP 0 101\n"
# FIXME: The DTMF mapping (101) is hard-coded. It would be better if we could
# get this from the DTMF payload generator
msg = <<-INVITE
INVITE sip:[service]@[remote_ip]:[remote_port] SIP/2.0
Via: SIP/2.0/[transport] [local_ip]:[local_port];branch=[branch]
- From: sipp <sip:#{@from_user}@[local_ip]>;tag=[call_number]
+ From: "#{@from_user}" <sip:#{@from_user}@[local_ip]>;tag=[call_number]
To: <sip:[service]@[remote_ip]:[remote_port]>
Call-ID: [call_id]
CSeq: [cseq] INVITE
- Contact: sip:#{@from_user}@[local_ip]:[local_port]
+ Contact: <sip:#{@from_user}@[local_ip]:[local_port];transport=[transport]>
Max-Forwards: 100
+ User-Agent: #{USER_AGENT}
Content-Type: application/sdp
Content-Length: [len]
v=0
o=user1 53655765 2353687637 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 0 101
+ #{rtp_string}
a=rtpmap:0 PCMU/8000
a=rtpmap:101 telephone-event/8000
a=fmtp:101 0-15
INVITE
send = new_send msg, opts
@scenario << send
end
+ def register(user, password = nil, opts = {})
+ opts[:retrans] ||= 500
+ user, domain = parse_user user
+ msg = register_message user, domain: domain
+ send = new_send msg, opts
+ @scenario << send
+ register_auth(user, password, domain: domain) if password
+ end
+
+ def register_message(user, opts = {})
+ <<-REGISTER
+
+ REGISTER sip:#{opts[:domain]} SIP/2.0
+ Via: SIP/2.0/[transport] [local_ip]:[local_port];branch=[branch]
+ From: <sip:#{user}@#{opts[:domain]}>;tag=[call_number]
+ To: <sip:#{user}@#{opts[:domain]}>
+ Call-ID: [call_id]
+ CSeq: [cseq] REGISTER
+ Contact: <sip:#{@from_user}@[local_ip]:[local_port];transport=[transport]>
+ Max-Forwards: 10
+ Expires: 120
+ User-Agent: #{USER_AGENT}
+ Content-Length: 0
+ REGISTER
+ end
+
+ def register_auth(user, password, opts = {})
+ opts[:retrans] ||= 500
+ @scenario << new_recv(response: '401', auth: true, optional: false)
+ msg = <<-AUTH
+
+ REGISTER sip:#{opts[:domain]} SIP/2.0
+ Via: SIP/2.0/[transport] [local_ip]:[local_port];branch=[branch]
+ From: <sip:#{user}@#{opts[:domain]}>;tag=[call_number]
+ To: <sip:#{user}@#{opts[:domain]}>
+ Call-ID: [call_id]
+ CSeq: [cseq] REGISTER
+ Contact: <sip:#{@from_user}@[local_ip]:[local_port];transport=[transport]>
+ Max-Forwards: 20
+ Expires: 3600
+ [authentication username=#{user} password=#{password}]
+ User-Agent: #{USER_AGENT}
+ Content-Length: 0
+ AUTH
+ send = new_send msg, opts
+ @scenario << send
+ end
+
def receive_trying(opts = {})
opts[:optional] = true if opts[:optional].nil?
opts.merge! response: 100
@scenario << new_recv(opts)
end
@@ -106,10 +158,12 @@
def receive_answer(opts = {})
opts.merge! response: 200
recv = new_recv opts
# Record Record Set: Make the Route headers available via [route] later
recv['rrs'] = true
+ # Response Time Duration: Record the response time
+ recv['rtd'] = true
@scenario << recv
end
alias :receive_200 :receive_answer
##
@@ -125,18 +179,19 @@
def ack_answer(opts = {})
msg = <<-ACK
ACK [next_url] SIP/2.0
Via: SIP/2.0/[transport] [local_ip]:[local_port];branch=[branch]
- From: <sip:#{@from_user}@[local_ip]>;tag=[call_number]
+ From: "#{@from_user}" <sip:#{@from_user}@[local_ip]>;tag=[call_number]
[last_To:]
- [routes]
Call-ID: [call_id]
CSeq: [cseq] ACK
- Contact: sip:#{@from_user}@[local_ip]:[local_port]
+ Contact: <sip:#{@from_user}@[local_ip]:[local_port];transport=[transport]>
Max-Forwards: 100
+ User-Agent: #{USER_AGENT}
Content-Length: 0
+ [routes]
ACK
@scenario << new_send(msg, opts)
start_media
end
@@ -165,19 +220,21 @@
end
def send_bye(opts = {})
msg = <<-MSG
- BYE sip:[service]@[remote_ip]:[remote_port] SIP/2.0
+ BYE [next_url] SIP/2.0
[last_Via:]
- [last_From:]
+ From: "#{@from_user}" <sip:#{@from_user}@[local_ip]>;tag=[call_number]
[last_To:]
[last_Call-ID]
CSeq: [cseq] BYE
- Contact: <sip:[local_ip]:[local_port];transport=[transport]>
+ Contact: <sip:#{@from_user}@[local_ip]:[local_port];transport=[transport]>
Max-Forwards: 100
+ User-Agent: #{USER_AGENT}
Content-Length: 0
+ [routes]
MSG
@scenario << new_send(msg, opts)
end
##
@@ -198,16 +255,17 @@
SIP/2.0 200 OK
[last_Via:]
[last_From:]
[last_To:]
- [routes]
[last_Call-ID:]
[last_CSeq:]
- Contact: <sip:[local_ip]:[local_port];transport=[transport]>
+ Contact: <sip:#{@from_user}@[local_ip]:[local_port];transport=[transport]>
Max-Forwards: 100
+ User-Agent: #{USER_AGENT}
Content-Length: 0
+ [routes]
ACK
@scenario << new_send(msg, opts)
end
def to_xml
@@ -222,9 +280,18 @@
puts "done."
print "Compiling scenario to #{@filename}.pcap..."
compile_media.to_file filename: "#{@filename}.pcap"
puts "done."
+ end
+
+ #TODO: SIPS support?
+ def parse_user(user)
+ user.slice! 0, 4 if user =~ /sip:/
+ user = user.split(":")[0]
+ user, domain = user.split("@")
+ domain ||= "[remote_ip]"
+ [user, domain]
end
private
def pause(msec)
pause = Nokogiri::XML::Node.new 'pause', @doc