diameter 0.1.0 → 0.2.0pre1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -10,45 +10,27 @@ module Diameter
10
10
  # as.
11
11
  module AVPType
12
12
  # Represents an AVP of Grouped type
13
- GROUPED = :Grouped
13
+ Grouped = :Grouped
14
14
 
15
15
  # Represents an AVP of Unsigned32 type
16
- U32 = :Unsigned32
16
+ Unsigned32 = :Unsigned32
17
+ Enumerated = Unsigned32
17
18
 
19
+ Unsigned64 = :Unsigned64
20
+ Integer32 = :Integer32
21
+ Integer64 = :Integer64
22
+ Float32 = :Float32
23
+ Float64 = :Float64
24
+
18
25
  # Represents an AVP of OctetString type
19
- OCTETSTRING = :OctetString
26
+ OctetString = :OctetString
27
+ DiameterIdentity = OctetString
28
+ DiameterURI = OctetString
29
+ UTF8String = OctetString
30
+
20
31
 
21
32
  # Represents an AVP of IPAddress type
22
- IPADDR = :Address
33
+ IPAddress = :Address
23
34
  end
24
-
25
- include AVPType
26
- include Vendors
27
-
28
- # The AVPs that can be looked up by name.
29
- AVAILABLE_AVPS = {
30
- 'Vendor-Specific-Application-Id' => [260, GROUPED],
31
- 'Vendor-Id' => [266, U32],
32
- 'Auth-Application-Id' => [258, U32],
33
- 'Acct-Application-Id' => [259, U32],
34
- 'Session-Id' => [263, OCTETSTRING],
35
- 'Product-Name' => [269, OCTETSTRING],
36
- 'Auth-Session-State' => [277, U32],
37
- 'Inband-Security-Id' => [299, U32],
38
- 'Origin-Host' => [264, OCTETSTRING],
39
- 'Firmware-Revision' => [267, U32],
40
- 'Result-Code' => [268, U32],
41
- 'Origin-Realm' => [296, OCTETSTRING],
42
- 'Destination-Host' => [293, OCTETSTRING],
43
- 'Destination-Realm' => [283, OCTETSTRING],
44
- 'User-Name' => [1, OCTETSTRING],
45
- 'Host-IP-Address' => [257, IPADDR],
46
- 'Public-Identity' => [601, OCTETSTRING, TGPP],
47
- 'Server-Name' => [602, OCTETSTRING, TGPP],
48
- 'SIP-Number-Auth-Items' => [607, U32, TGPP],
49
- 'SIP-Auth-Data-Item' => [612, GROUPED, TGPP],
50
- 'SIP-Item-Number' => [613, U32, TGPP],
51
- 'SIP-Authentication-Scheme' => [608, OCTETSTRING, TGPP] }
52
-
53
35
  end
54
36
  end
@@ -26,22 +26,23 @@ module Diameter
26
26
 
27
27
  # Creates a new Diameter message.
28
28
  #
29
- # @option opts [Fixnum] command_code
29
+ # @param [Hash] options The options
30
+ # @option options [Fixnum] command_code
30
31
  # The Diameter Command-Code of this messsage.
31
- # @option opts [Fixnum] app_id
32
+ # @option options [Fixnum] app_id
32
33
  # The Diameter application ID of this message, or 0 for base
33
34
  # protocol messages.
34
- # @option opts [Fixnum] hbh
35
+ # @option options [Fixnum] hbh
35
36
  # The hop-by-hop identifier of this message.
36
- # @option opts [Fixnum] ete
37
+ # @option options [Fixnum] ete
37
38
  # The end-to-end identifier of this message.
38
- # @option opts [true, false] request
39
+ # @option options [true, false] request
39
40
  # Whether this message is a request. Defaults to true.
40
- # @option opts [true, false] proxyable
41
+ # @option options [true, false] proxyable
41
42
  # Whether this message can be forwarded on. Defaults to true.
42
- # @option opts [true, false] error
43
+ # @option options [true, false] error
43
44
  # Whether this message is a Diameter protocol error. Defaults to false.
44
- # @option opts [Array<AVP>] avps
45
+ # @option options [Array<AVP>] avps
45
46
  # The list of AVPs to include on this message.
46
47
  def initialize(options = {})
47
48
  @version = 1
@@ -234,7 +235,9 @@ module Diameter
234
235
 
235
236
  avps = opts.fetch(:avps, [])
236
237
  avps << if opts[:experimental_result_vendor]
237
- fail
238
+ AVP.create("Experimental-Result",
239
+ [AVP.create("Experimental-Result-Code", result_code),
240
+ AVP.create("Vendor-Id", opts[:experimental_result_vendor])])
238
241
  else
239
242
  AVP.create("Result-Code", result_code)
240
243
  end
@@ -5,6 +5,7 @@ require 'diameter/message'
5
5
  require 'diameter/stack_transport_helpers'
6
6
  require 'diameter/diameter_logger'
7
7
  require 'concurrent'
8
+ require 'dnsruby'
8
9
 
9
10
  module Diameter
10
11
  class Stack
@@ -21,7 +22,10 @@ module Diameter
21
22
  # the Origin-Host AVP).
22
23
  # @param realm [String] The Diameter realm of this stack (for
23
24
  # the Origin-Realm AVP).
24
- def initialize(host, realm)
25
+ # @option opts [Fixnum] timeout (60)
26
+ # The number of seconds to wait for an answer before notifying
27
+ # the caller of a timeout and forgetting about the request.
28
+ def initialize(host, realm, opts={})
25
29
  @local_host = host
26
30
  @local_realm = realm
27
31
 
@@ -34,14 +38,16 @@ module Diameter
34
38
  @peer_table = {}
35
39
  @handlers = {}
36
40
 
37
- @threadpool = pool = Concurrent::ThreadPoolExecutor.new(
38
- min_threads: 5,
39
- max_threads: 5,
40
- max_queue: 100,
41
- overflow_policy: :caller_runs
42
- )
41
+ @answer_timeout = opts.fetch(:timeout, 60)
43
42
 
44
-
43
+ @threadpool = Concurrent::ThreadPoolExecutor.new(
44
+ min_threads: 5,
45
+ max_threads: 5,
46
+ max_queue: 1,
47
+ overflow_policy: :caller_runs
48
+ )
49
+
50
+ @res = Dnsruby::Resolver.new
45
51
  Diameter.logger.log(Logger::INFO, 'Stack initialized')
46
52
  end
47
53
 
@@ -120,6 +126,23 @@ module Diameter
120
126
  end
121
127
 
122
128
  # @!group Peer connections and message sending
129
+
130
+ def connect_to_realm(realm)
131
+ possible_peers = []
132
+ @res.query("_diameter._tcp.#{realm}", "SRV").each_answer do |a|
133
+ possible_peers << {name: a.target.to_s, port: a.port, priority: a.priority, weight: a.weight}
134
+ end
135
+
136
+ # Prefer the lowest priority and the highest weight
137
+ possible_peers.sort!{ |a, b| (a[:priority] <=> b[:priority]) || (b[:weight] <=> a[:weight])}
138
+ Diameter.logger.debug("Sorted list of peers for realm #{realm} is #{possible_peers.inspect}")
139
+
140
+ primary = possible_peers[0]
141
+
142
+ url = "aaa://#{primary[:name]}:#{primary[:port]}"
143
+ Diameter.logger.info("Primary peer for realm #{realm} is #{primary[:name]}, (#{url})")
144
+ connect_to_peer(url, primary[:name], realm)
145
+ end
123
146
 
124
147
  # Creates a Peer connection to a Diameter agent at the specific
125
148
  # network location indicated by peer_uri.
@@ -165,6 +188,15 @@ module Diameter
165
188
  @tcp_helper.send(req.to_wire, peer.cxn)
166
189
  q = Queue.new
167
190
  @pending_ete[req.ete] = q
191
+
192
+ # Time this request out if no answer is received
193
+ Concurrent::timer(@answer_timeout) do
194
+ q = @pending_ete.delete(req.ete)
195
+ if q
196
+ q.push(:timeout)
197
+ end
198
+ end
199
+
168
200
  p = Concurrent::Promise.execute(executor: @threadpool) {
169
201
  Diameter.logger.debug("Waiting for answer to message with EtE #{req.ete}, queue #{q}")
170
202
  val = q.pop
@@ -240,7 +272,7 @@ module Diameter
240
272
  elsif @handlers.has_key? msg.app_id
241
273
  @handlers[msg.app_id].call(msg, cxn)
242
274
  else
243
- fail "Received unknown message of type #{msg.command_code}"
275
+ Diameter.logger.warn("Ignoring message from unrecognised application #{msg.app_id} (Command-Code #{msg.command_code})")
244
276
  end
245
277
  end
246
278
 
@@ -273,16 +305,17 @@ module Diameter
273
305
  end
274
306
 
275
307
  def shared_apps(capabilities_msg)
276
- peer_apps = capabilities_msg.all_avps_by_name("Auth-Application-Id").collect(&:uint32)
277
- peer_apps += capabilities_msg.all_avps_by_name("Acct-Application-Id").collect(&:uint32)
308
+ peer_apps = []
278
309
 
279
- capabilities_msg.all_avps_by_name("Vendor-Specific-Application-Id").each do |avp|
280
- if avp.inner_avp("Auth-Application-Id")
281
- peer_apps << avp.inner_avp("Auth-Application-Id").uint32
282
- end
310
+ app_avps = ["Auth-Application-Id", "Acct-Application-Id"]
311
+
312
+ app_avps.each do |name|
313
+ peer_apps += capabilities_msg.all_avps_by_name(name).collect(&:uint32)
283
314
 
284
- if avp.inner_avp("Acct-Application-Id")
285
- peer_apps << avp.inner_avp("Acct-Application-Id").uint32
315
+ capabilities_msg.all_avps_by_name("Vendor-Specific-Application-Id").each do |avp|
316
+ if avp.inner_avp(name)
317
+ peer_apps << avp.inner_avp(name).uint32
318
+ end
286
319
  end
287
320
  end
288
321
 
@@ -35,6 +35,15 @@ module Diameter
35
35
  @wakeup_pipe_wr.puts "wakeup"
36
36
  end
37
37
 
38
+ def read_from(connection, bytes)
39
+ msg, src = connection.recv_nonblock(bytes)
40
+ if msg == ''
41
+ Diameter.logger.warn('Received 0 bytes on read, closing connection')
42
+ close(connection)
43
+ end
44
+ return msg, src
45
+ end
46
+
38
47
  def main_loop
39
48
  rs, _ws, es = IO.select(@all_connections + [@wakeup_pipe_rd], [], @all_connections)
40
49
 
@@ -50,13 +59,8 @@ module Diameter
50
59
 
51
60
  existing_data = @data[r]
52
61
  if existing_data.length < 4
53
- msg, _src = r.recv_nonblock(4 - existing_data.length)
54
- if msg == ''
55
- Diameter.logger.warn('Received 0 bytes on read, closing connection')
56
- close(r)
57
- else
58
- existing_data += msg
59
- end
62
+ msg, _src = read_from(r, 4 - existing_data.length)
63
+ existing_data += msg
60
64
  end
61
65
 
62
66
  expected_len = -1
@@ -64,13 +68,8 @@ module Diameter
64
68
  expected_len = Message.length_from_header(existing_data[0..4])
65
69
  Diameter.logger.debug("Read 4 bytes #{existing_data[0..4].inspect}, " \
66
70
  "reading full message of length #{expected_len}")
67
- msg, _src = r.recv_nonblock(expected_len - existing_data.length)
71
+ msg, _src = read_from(r, expected_len - existing_data.length)
68
72
  existing_data += msg
69
- if msg == ''
70
- # Connection closed
71
- Diameter.logger.warn('Received 0 bytes on read, closing connection')
72
- close(r)
73
- end
74
73
  end
75
74
 
76
75
  if existing_data.length == expected_len
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: diameter
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0pre1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rob Day
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-12-15 00:00:00.000000000 Z
11
+ date: 2015-01-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: concurrent-ruby
@@ -24,6 +24,20 @@ dependencies:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0.7'
27
+ - !ruby/object:Gem::Dependency
28
+ name: dnsruby
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1'
27
41
  - !ruby/object:Gem::Dependency
28
42
  name: rubocop
29
43
  requirement: !ruby/object:Gem::Requirement
@@ -102,6 +116,7 @@ extra_rdoc_files: []
102
116
  files:
103
117
  - lib/diameter.rb
104
118
  - lib/diameter/avp.rb
119
+ - lib/diameter/avp_names.rb
105
120
  - lib/diameter/avp_parser.rb
106
121
  - lib/diameter/constants.rb
107
122
  - lib/diameter/diameter_logger.rb
@@ -126,9 +141,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
126
141
  version: '0'
127
142
  required_rubygems_version: !ruby/object:Gem::Requirement
128
143
  requirements:
129
- - - ">="
144
+ - - ">"
130
145
  - !ruby/object:Gem::Version
131
- version: '0'
146
+ version: 1.3.1
132
147
  requirements: []
133
148
  rubyforge_project:
134
149
  rubygems_version: 2.4.3