Class: OverSIP::SIP::RFC3263::Query

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

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Logger

fg_system_msg2str, load_methods, #log_id

Constructor Details

#initialize(dns_conf, id, uri_scheme, uri_host, uri_host_type, uri_port = nil, uri_transport = nil) ⇒ Query

Returns a new instance of Query.



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
# File 'lib/oversip/sip/rfc3263.rb', line 121

def initialize dns_conf, id, uri_scheme, uri_host, uri_host_type, uri_port=nil, uri_transport=nil
  @id = id
  @uri_scheme = uri_scheme
  @uri_host = uri_host
  @uri_host_type = uri_host_type
  @uri_port = uri_port
  @uri_transport = uri_transport

  @log_id ||= ("RFC3263" << " " << @id)

  @use_dns = dns_conf[:use_dns]
  @transport_preference = dns_conf[:transport_preference]

  @has_sip_ipv4 = dns_conf[:has_sip_ipv4]
  @has_sip_ipv6 = dns_conf[:has_sip_ipv6]
  @has_sip_udp = dns_conf[:has_sip_udp]
  @has_sip_tcp = dns_conf[:has_sip_tcp]
  @has_sip_tls = dns_conf[:has_sip_tls]

  # Just initialize these attributes if URI host is a domain.
  if uri_host_type == :domain
    @ip_type_preference = dns_conf[:ip_type_preference]
    @force_transport_preference = dns_conf[:force_transport_preference]
    @use_naptr = dns_conf[:use_naptr]
    @use_srv = dns_conf[:use_srv]
  end
end

Class Method Details

.class_initObject



117
118
119
# File 'lib/oversip/sip/rfc3263.rb', line 117

def self.class_init
  @@fiber_pool = ::OverSIP::FiberPool.new 50
end

Instance Method Details

#callback(&block) ⇒ Object



149
150
151
# File 'lib/oversip/sip/rfc3263.rb', line 149

def callback &block
  @on_success_block = block
end

#errback(&block) ⇒ Object



153
154
155
# File 'lib/oversip/sip/rfc3263.rb', line 153

def errback &block
  @on_error_block = block
end

#resolveObject

This method can return:

  • Target: in case host is a IP.

  • SrvTargets: in case SRV took place. Then the client must use SrvTargets#randomize and get a SrvRandomizedTargets (an Array of Target entries).

  • MultiTargets: so each element can be one of the above elements.

  • nil: result will be retrieved via callback/errback.

  • Symbol: there is some error (domain does not exist, no records, IP is IPv6 but we don’t support it, invalid transport…).



165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
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
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
# File 'lib/oversip/sip/rfc3263.rb', line 165

def resolve
  if not @use_dns and @uri_host_type == :domain
    return :rfc3263_no_dns
  end

  case @uri_scheme
  when :sip
  when :sips
    # If URI scheme is :sips and we don't support TLS then reject it.
    return :rfc3263_unsupported_scheme  unless @has_sip_tls
  else
    return :rfc3263_unsupported_scheme
  end

  dns_transport = nil
  dns_port = @uri_port

  # dns_transport means the transport type taken from the destination SIP URI.
  # If @uri_scheme is :sips and no @uri_transport is given (or it's :tcp), then
  # dns_transport is :tls.
  # So dns_transport can be :udp, :tcp or :tls, while @uri_transport should not be
  # :tls (according to RFC 3261) in case scheme is :sips, and maybe :udp, :tcp, :sctp
  # or whatever token. In case scheme is :sip then @uri_transport can be :tls so
  # dns_transport would be :tls.

  ### First select @transport.

  # If it's a domain with no port nor ;transport, then
  # transport will be inspected later with NAPTR.
  if not @uri_transport and ( @uri_host_type != :domain or @uri_port )
    case @uri_scheme
    when :sip
      if @has_sip_udp
        dns_transport = :udp
      # In case we don't support UDP then use TCP (why not? local policy).
      elsif @has_sip_tcp
        dns_transport = :tcp
      else
        return :rfc3263_unsupported_transport
      end
    when :sips
      dns_transport = :tls
    end
  end

  # If the URI has ;transport param, then set dns_transport.
  if @uri_transport
    case @uri_transport
    when :udp
      return :rfc3263_unsupported_transport  unless @has_sip_udp
      if @uri_scheme == :sip
        dns_transport = :udp
      # "sips" is not possible in UDP.
      else
        return :rfc3263_unsupported_transport
      end
    when :tcp
      case (dns_transport = ( @uri_scheme == :sips ? :tls : :tcp ))
        when :tcp ; return :rfc3263_unsupported_transport  unless @has_sip_tcp
        when :tls ; return :rfc3263_unsupported_transport  unless @has_sip_tls
        end
    when :tls
      return :rfc3263_unsupported_transport  unless @has_sip_tls
      dns_transport = :tls
    else
      return :rfc3263_unsupported_transport
    end
  end

  # If URI host is an IP, no DNS query must be done (so no Ruby Fiber must be created).
  unless @uri_host_type == :domain
    if @uri_host_type == :ipv4 and not @has_sip_ipv4
      return :rfc3263_no_ipv4
    elsif @uri_host_type == :ipv6 and not @has_sip_ipv6
      return :rfc3263_no_ipv6
    end

    dns_port ||= 5061 if dns_transport == :tls
    dns_port ||= case @uri_scheme
      when :sip  ; 5060
      when :sips ; 5061
    end

    return Target.new(dns_transport, @uri_host, @uri_host_type, dns_port)
  end


  # URI host is domain so at least a DNS query must be performed.
  # Let's create/use a Fiber then.
  @@fiber_pool.spawn do

    # If URI port is specified perform DNS A/AAAA (then transport has been
    # already set above).
    if @uri_port
      if (targets = resolve_A_AAAA(dns_transport, @uri_host, dns_port))
        if targets.size == 1
          @on_success_block && @on_success_block.call(targets[0])
        else
          @on_success_block && @on_success_block.call(targets)
        end
      else
        @on_error_block && @on_error_block.call(:rfc3263_domain_not_found)
      end


    # If the URI has no port but has ;transport param, then DNS SRV takes place.
    elsif @uri_transport
      if @use_srv
        if (targets = resolve_SRV(@uri_host, @uri_scheme, dns_transport))
          if targets.size == 1
            @on_success_block && @on_success_block.call(targets[0])
          else
            @on_success_block && @on_success_block.call(targets)
          end
        else
          @on_error_block && @on_error_block.call(:rfc3263_domain_not_found)
        end

      # If @use_srv is false then perform A/AAAA queries.
      else
        log_system_debug "SRV is disabled, performing A/AAAA queries"  if $oversip_debug

        port = 5061 if dns_transport == :tls
        port ||= case @uri_scheme
          when :sip  ; 5060
          when :sips ; 5061
        end

        if (targets = resolve_A_AAAA(dns_transport, @uri_host, port))
          if targets.size == 1
            @on_success_block && @on_success_block.call(targets[0])
          else
            @on_success_block && @on_success_block.call(targets)
          end
        else
          @on_error_block && @on_error_block.call(:rfc3263_domain_not_found)
        end

      end


    # If not, the URI has no port neither ;transport param. NAPTR is required.
    else
      # If @use_naptr is false then NAPTR must not be performed.
      if ! @use_naptr
        if @use_srv
          log_system_debug "NAPTR is disabled, performing SRV queries"  if $oversip_debug
          continue_with_SRV

        # If @use_srv is false then perform A/AAAA queries.
        else
          log_system_debug "NAPTR and SRV are disabled, performing A/AAAA queries"  if $oversip_debug
          case @uri_scheme
          when :sip
            if @has_sip_udp
              dns_transport = :udp
              port = 5060
            # In case we don't support UDP then use TCP (why not? local policy).
            elsif @has_sip_tcp
              dns_transport = :tcp
              port = 5060
            else
              @on_error_block && @on_error_block.call(:rfc3263_unsupported_transport)
            end
          when :sips
            dns_transport = :tls
            port = 5061
          end

          if (targets = resolve_A_AAAA(dns_transport, @uri_host, port))
            if targets.size == 1
              @on_success_block && @on_success_block.call(targets[0])
            else
              @on_success_block && @on_success_block.call(targets)
            end
          else
            @on_error_block && @on_error_block.call(:rfc3263_domain_not_found)
          end

        end

      # There are NAPTR records so inspect them (note that there still could be no valid SIP NAPTR records
      # so SRV should take place).
      elsif (naptrs = sync_resolve_NAPTR(@uri_host))

        # If URI scheme is :sips just SIPS+D2T must be searched.
        naptrs.select! do |naptr|
          naptr.flags.downcase == "s" and
          ( (@has_sip_tls and naptr.service.upcase == SIPS_D2T) or
            (@uri_scheme == :sip and @has_sip_tcp and naptr.service.upcase == SIP_D2T) or
            (@uri_scheme == :sip and @has_sip_udp and naptr.service.upcase == SIP_D2U) )
        end

        # There are NAPTR records, but not for SIP (or not for SIPS+D2T in case the URI scheme is :sips).
        # So perform SRV queries.
        if naptrs.empty?
          log_system_debug "cannot get valid NAPTR SIP records, performing SRV queries"  if $oversip_debug
          continue_with_SRV

        # There are NAPTR records for SIP.
        else
          # @force_transport_preference is false so let's use NAPTR preferences.
          unless @force_transport_preference
            # Order based on RR order and preference (just a bit).
            ordered_naptrs = naptrs.sort { |x,y| (x.order <=> y.order).nonzero? or y.preference <=> x.preference }

          # @force_transport_preference is true so let's use @transport_preference for ordering the records.
          else
            ordered_naptrs = []
            @transport_preference.each do |transport|
              service = TRANSPORT_TO_SERVICE[transport]
              ordered_naptrs.concat(naptrs.select { |naptr| naptr.service.upcase == service })
            end

          end

          srv_targets = MultiTargets.allocate
          ordered_naptrs.each do |naptr|
            naptr_transport = case naptr.service.upcase
              when SIPS_D2T ; :tls
              when SIP_D2T  ; :tcp
              when SIP_D2U  ; :udp
              end
            if (result = resolve_SRV(naptr.replacement, nil, nil, naptr_transport))
              case result
              when RFC3263::SrvTargets
                srv_targets << result
                srv_targets.has_srv_weight_targets = true
              # A RFC3263::MultiTargets or an array of RFC3263::Target.
              when RFC3263::MultiTargets, ::Array
                srv_targets.concat result
              end
            end
          end

          if srv_targets.size == 1
            @on_success_block && @on_success_block.call(srv_targets[0])
          else
            @on_success_block && @on_success_block.call(srv_targets)
          end

        end

      # There are not NAPTR records, so try SRV records in preference order.
      else
        log_system_debug "no NAPTR records, performing SRV queries"  if $oversip_debug
        continue_with_SRV

      end

    end

  end  # @@fiber_pool.spawn

  nil
end