Class: OverSIP::SIP::Proxy

Inherits:
Object
  • Object
show all
Includes:
Logger
Defined in:
lib/oversip/sip/proxy.rb

Constant Summary

Constants included from Logger

Logger::SYSLOG_POSIXMQ_MAPPING

Instance Method Summary collapse

Methods included from Logger

close, #fatal, fg_system_msg2str, init_logger_mq, load_methods, #log_id, syslog_system_msg2str, syslog_user_msg2str

Constructor Details

#initialize(request, proxy_conf) ⇒ Proxy

Returns a new instance of Proxy.



7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# File 'lib/oversip/sip/proxy.rb', line 7

def initialize request, proxy_conf
  @request = request
  @proxy_conf = proxy_conf
  @log_id = "Proxy #{proxy_conf[:name]} #{request.via_branch_id}"

  # Create the server transaction if it doesn't exist yet.
  @server_transaction = @request.server_transaction or case @request.sip_method
    # Here it can arrive an INVITE, ACK-for-2XX and any method but CANCEL.
    when :INVITE
      InviteServerTransaction.new @request
    when :ACK
    else
      NonInviteServerTransaction.new @request
    end
  @request.server_transaction ||= @server_transaction

  # Set this core layer to the server transaction.
  @request.server_transaction.core = self  if @request.server_transaction
end

Instance Method Details

#client_timeoutObject



226
227
228
# File 'lib/oversip/sip/proxy.rb', line 226

def client_timeout
  try_next_target 408, "Client Timeout"
end

#connection_failedObject



231
232
233
# File 'lib/oversip/sip/proxy.rb', line 231

def connection_failed
  try_next_target 500, "Connection Error"
end

#drop_responseObject

If called, current response within the called callback won’t be forwarded.



53
54
55
# File 'lib/oversip/sip/proxy.rb', line 53

def drop_response
  @drop_response = true
end

#invite_timeoutObject

Timer C for INVITE.



242
243
244
245
246
247
248
249
# File 'lib/oversip/sip/proxy.rb', line 242

def invite_timeout
  @on_invite_timeout_block && @on_invite_timeout_block.call

  unless @drop_response
    @request.reply 408, "INVITE Timeout"
  end
  @drop_response = true  # Ignore the possible 487 got from the callee.
end

#on_canceled(&block) ⇒ Object



40
41
42
# File 'lib/oversip/sip/proxy.rb', line 40

def on_canceled &block
  @on_canceled_block = block
end

#on_error(&block) ⇒ Object



48
49
50
# File 'lib/oversip/sip/proxy.rb', line 48

def on_error &block
  @on_error_block = block
end

#on_failure_response(&block) ⇒ Object



36
37
38
# File 'lib/oversip/sip/proxy.rb', line 36

def on_failure_response &block
  @on_failure_response_block = block
end

#on_invite_timeout(&block) ⇒ Object



44
45
46
# File 'lib/oversip/sip/proxy.rb', line 44

def on_invite_timeout &block
  @on_invite_timeout_block = block
end

#on_provisional_response(&block) ⇒ Object



28
29
30
# File 'lib/oversip/sip/proxy.rb', line 28

def on_provisional_response &block
  @on_provisional_response_block = block
end

#on_success_response(&block) ⇒ Object



32
33
34
# File 'lib/oversip/sip/proxy.rb', line 32

def on_success_response &block
  @on_success_response_block = block
end

#receive_cancel(cancel) ⇒ Object

Since we don’t implement parallel forking, directly send our CANCEL downstream.



216
217
218
219
220
221
222
223
# File 'lib/oversip/sip/proxy.rb', line 216

def receive_cancel cancel
  log_system_debug "server transaction canceled, cancelling pending client transaction"  if $oversip_debug

  @canceled = true
  @on_canceled_block && @on_canceled_block.call

  @client_transaction.do_cancel cancel
end

#receive_response(response) ⇒ Object



181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
# File 'lib/oversip/sip/proxy.rb', line 181

def receive_response response
  log_system_debug "received response #{response.status_code}"

  response.delete_header_top "Via"

  if @request.server_transaction.valid_response? response.status_code
    if response.status_code < 200 && ! @canceled
      @on_provisional_response_block && @on_provisional_response_block.call(response)
    elsif response.status_code >= 200 && response.status_code <= 299
      @on_success_response_block && @on_success_response_block.call(response)
    elsif response.status_code >= 300 && ! @canceled
      if response.status_code == 503
        if @proxy_conf[:dns_failover_on_503]
          try_next_target nil, nil, response
          return
        else
          # If the response is 503 convert it into 500 (RFC 3261 16.7).
          response.status_code = 500
          @on_failure_response_block && @on_failure_response_block.call(response)
        end
      else
        @on_failure_response_block && @on_failure_response_block.call(response)
      end
    end
  end

  unless @drop_response
    @request.reply_full response
  else
    @drop_response = false
  end
end

#route(dst_host = nil, dst_port = nil, dst_transport = nil) ⇒ Object



58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
# File 'lib/oversip/sip/proxy.rb', line 58

def route dst_host=nil, dst_port=nil, dst_transport=nil
  # NOTE: Routing can be based on incoming request for an Outbound (RFC 5626) connection
  # or based on normal RFC 3263 procedures.

  # If it's an incoming Outbound connection get the associated connection (but if dst_host is
  # set then don't honor the Outbound connection).

  if @request.incoming_outbound_requested? and not dst_host
    @client_transaction = (::OverSIP::SIP::ClientTransaction.get_class @request).new self, @request, @proxy_conf, @request.route_outbound_flow_token

    if @client_transaction.connection
      add_routing_headers
      @client_transaction.send_request
    else
      unless @request.sip_method == :ACK
        log_system_debug "flow failed"  if $oversip_debug

        @on_error_block && @on_error_block.call(430, "Flow Failed")
        unless @drop_response
          @request.reply 430, "Flow Failed"
        else
          @drop_response = false
        end
      else
        log_system_debug "flow failed for received ACK"  if $oversip_debug
      end
    end

    return
  end


  # If it's not an incoming Outbound connection (or explicit destination is set),
  # let's perform RFC 3263 procedures.

  # Check the request destination.
  # If a destination is given use it. If not route based on request headers.

  # Force the destination.
  if dst_host
    dst_scheme = :sip
    dst_host_type = ::OverSIP::Utils.ip_type(dst_host) || :domain

  # Or use top Route header.
  elsif @request.routes
    top_route = @request.routes[0]
    dst_scheme = top_route.scheme
    dst_host = top_route.host
    dst_host_type = top_route.host_type
    dst_port = top_route.port
    dst_transport = top_route.transport_param

  # Or use the Request URI.
  else
    dst_scheme = @request.ruri.scheme
    dst_host = @request.ruri.host
    dst_host_type = @request.ruri.host_type
    dst_port = @request.ruri.port
    dst_transport = @request.ruri.transport_param
  end

  # If the destination uri_host is an IPv6 reference, convert it to real IPv6.
  if dst_host_type == :ipv6_reference
    dst_host = ::OverSIP::Utils.normalize_ipv6(dst_host, true)
    dst_host_type = :ipv6
  end

  # Loockup in the DNS cache of this proxy.
  if dst_host_type == :domain and @proxy_conf[:use_dns_cache]
    dns_cache_entry = "#{dst_host}|#{dst_port}|#{dst_transport}|#{dst_scheme}"
    if (result = @proxy_conf[:dns_cache][dns_cache_entry])
      log_system_debug "destination found in the DNS cache"  if $oversip_debug
      if result.is_a? ::Symbol
        rfc3263_failed result
      else
        rfc3263_succeeded result
      end
      return
    end
  else
    dns_cache_entry = nil
  end

  # Perform RFC 3261 procedures.
  dns_query = ::OverSIP::SIP::RFC3263::Query.new @proxy_conf, @request.via_branch_id, dst_scheme, dst_host, dst_host_type, dst_port, dst_transport
  case result = dns_query.resolve

  # Async result so DNS took place.
  when nil
    # Async success.
    dns_query.callback do |result|
      # Store the result in the DNS cache.
      if dns_cache_entry
        @proxy_conf[:dns_cache][dns_cache_entry] = result
        ::EM.add_timer(@proxy_conf[:dns_cache_time]) { @proxy_conf[:dns_cache].delete dns_cache_entry }
      end
      rfc3263_succeeded result
    end
    # Async error.
    dns_query.errback do |result|
      # Store the result in the DNS cache.
      if dns_cache_entry
        @proxy_conf[:dns_cache][dns_cache_entry] = result
        ::EM.add_timer(@proxy_conf[:dns_cache_time]) { @proxy_conf[:dns_cache].delete dns_cache_entry }
      end
      rfc3263_failed result
    end
  # Instant error.
  when ::Symbol
    # Store the result in the DNS cache.
    if dns_cache_entry
      @proxy_conf[:dns_cache][dns_cache_entry] = result
      ::EM.add_timer(@proxy_conf[:dns_cache_time]) { @proxy_conf[:dns_cache].delete dns_cache_entry }
    end
    rfc3263_failed result
  # Instant success so it's not a domain (no DNS performed).
  else
    rfc3263_succeeded result
  end

end

#tls_validation_failedObject



236
237
238
# File 'lib/oversip/sip/proxy.rb', line 236

def tls_validation_failed
  try_next_target 500, "TLS Validation Failed"
end