Class: Rubius::Packet

Inherits:
Object
  • Object
show all
Defined in:
lib/rubius/packet.rb

Constant Summary collapse

PACK_HEADER =
'CCna16a*'
HEADER_LENGTH =
1 + 1 + 2 + 16
VSA_TYPE =
26
ACCESS_REQUEST =
'Access-Request'
ACCESS_ACCEPT =
'Access-Accept'
ACCESS_REJECT =
'Access-Reject'
ACCOUNTING_REQUEST =
'Accounting-Request'
ACCOUNTING_RESPONSE =
'Accounting-Response'
ACCESS_CHALLENGE =
'Access-Challenge'
STATUS_SERVER =
'Status-Server'
STATUS_CLIENT =
'Status-Client'
RESPONSES =
{   1 => ACCESS_REQUEST,
2 => ACCESS_ACCEPT,
3 => ACCESS_REJECT,
4 => ACCOUNTING_REQUEST,
5 => ACCOUNTING_RESPONSE,
11 => ACCESS_CHALLENGE,
12 => STATUS_SERVER,
13 => STATUS_CLIENT}

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(dictionary) ⇒ Packet

Returns a new instance of Packet.



31
32
33
34
35
# File 'lib/rubius/packet.rb', line 31

def initialize(dictionary)
  @dictionary = dictionary
  @attributes = Hash.new
  @secret = nil
end

Instance Attribute Details

#authenticatorObject

Returns the value of attribute authenticator.



29
30
31
# File 'lib/rubius/packet.rb', line 29

def authenticator
  @authenticator
end

#codeObject

Returns the value of attribute code.



28
29
30
# File 'lib/rubius/packet.rb', line 28

def code
  @code
end

#identifierObject

Returns the value of attribute identifier.



26
27
28
# File 'lib/rubius/packet.rb', line 26

def identifier
  @identifier
end

#secretObject

Returns the value of attribute secret.



27
28
29
# File 'lib/rubius/packet.rb', line 27

def secret
  @secret
end

Instance Method Details

#packObject



111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
# File 'lib/rubius/packet.rb', line 111

def pack
  attr_string = ''
  
  @attributes.each_pair {|key, value|
    attr_num = @dictionary.attribute_id(key)
    type = @dictionary.attribute_type(attr_num)
    val = pack_attribute(value, type)
    next if val.nil?
    attr_string += [attr_num, val.length + 2, val].pack("CCa*")
  }
  
  rejected_responses = RESPONSES.reject{|k,v| v!=@code}
  rejected_responses = rejected_responses.to_a if RUBY_VERSION < "1.9.2"
  rcode = rejected_responses.flatten.first
  
  return [rcode, @identifier, attr_string.length + HEADER_LENGTH, @authenticator, attr_string].pack(PACK_HEADER)
end

#pack_attribute(data, type) ⇒ Object



57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
# File 'lib/rubius/packet.rb', line 57

def pack_attribute(data, type)
  val = case type
  when 'string'
    data
  when 'integer'
    [data].pack("N")
  when 'ipaddr'
    [IPAddr.new(data).to_i].pack("N")
  when 'date'
    [data].pack("N")
  when 'time'
    [data].pack("N")
  else
    nil
  end
  
  val
end

#response_authenticatorObject



150
151
152
153
154
# File 'lib/rubius/packet.rb', line 150

def response_authenticator
  attributes = ''
  hash_data = [5, @identifier, attributes.length+HEADER_LENGTH, @authenticator, attributes, @secret].pack(PACK_HEADER)
  digest = Digest::MD5.digest(hash_data)
end

#set_attribute(attr_name, value) ⇒ Object



134
135
136
# File 'lib/rubius/packet.rb', line 134

def set_attribute(attr_name, value)
  @attributes[attr_name] = value
end

#set_password(password) ⇒ Object



138
139
140
141
142
143
144
145
146
147
148
# File 'lib/rubius/packet.rb', line 138

def set_password(password)
  lastround = @authenticator
  pwdout = ""
  password += "\000" * (15-(15+password.length)%16)
  0.step(password.length-1, 16) {|i|
    lastround = password[i, 16].xor(Digest::MD5.digest(@secret + lastround))
    pwdout += lastround
  }
  
  set_attribute("User-Password", pwdout)
end

#set_vendor_attribute(vendor_id, attr_id, value) ⇒ Object



129
130
131
132
# File 'lib/rubius/packet.rb', line 129

def set_vendor_attribute(vendor_id, attr_id, value)
  attr_name = @dictionary.attribute_name(attr_id, vendor_id)
  set_attribute(attr_name, value)
end

#unpack(data) ⇒ Object



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
# File 'lib/rubius/packet.rb', line 76

def unpack(data)
  @code, @identifier, @length, @authenticator, attribute_data = data.unpack(PACK_HEADER)
  @code = RESPONSES[@code]
  @attributes = Hash.new
  
  while(attribute_data.length > 0)
    # Read the length of the packet data
    length = attribute_data.unpack("xC")[0].to_i
    
    # read the type header to determine if this is a VSA
    type_id, value = attribute_data.unpack("Cxa#{length-2}")
    type_id = type_id.to_i
    
    if(type_id == VSA_TYPE)
      # Handle VSA's
      vendor_id, vendor_attribute_id, vendor_attribute_length = value.unpack("NCC")
      vendor_attribute_value = value.unpack("xxxxxxa#{vendor_attribute_length-2}")[0]
      
      # look up the type of data so we know how to unpack it
      type = @dictionary.attribute_type(vendor_attribute_id, vendor_id)
      raise "VSA not found in dictionary (#{vendor_id}/#{vendor_attribute_id})" if type.nil?
      
      val = unpack_attribute(vendor_attribute_value, type)
      set_vendor_attribute(vendor_id, vendor_attribute_id, val)
    else
      type = @dictionary.attribute_type(type_id)
      raise "Attribute not found in dictionary (#{Dictionary::DEFAULT_VENDOR}/#{type_id})" if type.nil?
      
      val = unpack_attribute(value, type)
      set_vendor_attribute(Dictionary::DEFAULT_VENDOR, type_id, val)
    end
    attribute_data[0, length] = ''
  end
end