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