steamrb 0.1.0

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.
@@ -0,0 +1,113 @@
1
+ # frozen_string_literal: true
2
+ require 'openssl'
3
+ require 'zlib'
4
+ require 'base64'
5
+
6
+ module Steam
7
+ # Handles Steam AES key encryption and decrptyion of raw packet data.
8
+ class Crypto
9
+ # The Steam public key
10
+ KEY = 'MIGdMA0GCSqGSIb3DQEBAQUAA4GLADCBhwKBgQDf7BrWLBBmLBc1OhSwfFkRf53'\
11
+ 'T2Ct64+AVzRkeRuh7h3SiGEYxqQMUeYKO6UWiSRKpI2hzic9pobFhRr3Bvr/WARvY'\
12
+ 'gdTckPv+T1JzZsuVcNfFjrocejN1oWI0Rrtgt4Bo+hOneoo3S57G9F1fOpn5nsQ66'\
13
+ 'WOiu4gZKODnFMBCiQIBEQ=='
14
+
15
+ # Encrypt a given string with a given key, get the encrypted data back.
16
+ #
17
+ # @param data [String] the data to encrypt
18
+ # @param key [String] the key to encrypt the data with
19
+ def self.encrypt(data, key)
20
+ new.encrypt(StringIO.new(data), key).string
21
+ end
22
+
23
+ # Decrypt a given string with a given key, get the decrypted data back.
24
+ #
25
+ # @param data [String] the data to decrypt
26
+ # @param key [String] the key to decrypt the data with
27
+ def self.decrypt(data, key)
28
+ new.decrypt(StringIO.new(data), key).string
29
+ end
30
+
31
+ # Generates a tuple representing the session key, both plain text and
32
+ # encrypted
33
+ #
34
+ # @example Generating a key
35
+ # crypto = Cyrpto.new
36
+ # crypted_key, key = crypto.generate_key
37
+ #
38
+ # @return [Array] the crypted_key, plain_key
39
+ def generate_key
40
+ key = OpenSSL::PKey::RSA.new(Base64.strict_decode64(KEY))
41
+ plain = Random.new.bytes(32)
42
+ padding = OpenSSL::PKey::RSA::PKCS1_OAEP_PADDING
43
+
44
+ [key.public_encrypt(plain, padding), plain]
45
+ end
46
+
47
+ # Encrypts the given IO stream using the given key
48
+ #
49
+ # @example Encrypting data
50
+ # crypto = Crypto.new
51
+ # crypto.encrypt('data', 'key')
52
+ #
53
+ # @param io [#read] The IO object to encrypt
54
+ # @param key [String] The key
55
+ # @return [:read] an IO object represeting the encrypted data
56
+ def encrypt(io, key)
57
+ iv = Random.new.bytes(16)
58
+
59
+ iv_cipher = iv_cipher(key, :encrypt)
60
+ cipher = data_cipher(key, iv, :encrypt)
61
+
62
+ StringIO.new(iv_cipher.update(iv) + iv_cipher.final +
63
+ cipher.update(io.read) + cipher.final)
64
+ end
65
+
66
+ # Decrypts the given IO stream using the given key
67
+ #
68
+ # @example Encrypting data
69
+ # crypto = Crypto.new
70
+ # crypto.decrypt(StringIO.new('data'), 'key')
71
+ #
72
+ # @param io [#read] The IO object to encrypt
73
+ # @param key [String] The key
74
+ # @return [:read] an IO object represeting the decrypted data
75
+ def decrypt(io, key)
76
+ iv_cipher = iv_cipher(key, :decrypt)
77
+ crypted_iv = io.read(16)
78
+ iv = iv_cipher.update(crypted_iv) + iv_cipher.final
79
+
80
+ cipher = data_cipher(key, iv, :decrypt)
81
+
82
+ StringIO.new(cipher.update(io.read) + cipher.final)
83
+ end
84
+
85
+ # Get the 32 bit crc for the given key
86
+ #
87
+ # @param key [String] The key
88
+ # @return [Integer] the crc
89
+ def session_key_crc(key)
90
+ Zlib.crc32(key)
91
+ end
92
+
93
+ private
94
+
95
+ # @api private
96
+ def data_cipher(key, iv, type)
97
+ cipher = OpenSSL::Cipher::AES.new(256, :CBC)
98
+ cipher.send(type)
99
+ cipher.key = key
100
+ cipher.iv = iv
101
+ cipher
102
+ end
103
+
104
+ # @api private
105
+ def iv_cipher(key, type)
106
+ iv_cipher = OpenSSL::Cipher::AES.new(256, :ECB)
107
+ iv_cipher.send(type)
108
+ iv_cipher.key = key
109
+ iv_cipher.padding = 0
110
+ iv_cipher
111
+ end
112
+ end
113
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+ module Steam
3
+ # Utility to provide EMsg convenience. Allows looking up
4
+ # EMsg name by the EMsg integer value.
5
+ #
6
+ # EMsgUtil.new(123).name # => 'SOME_EMSG'
7
+ class EMsgUtil
8
+ # Instantiate the object
9
+ #
10
+ # @param emsg [Integer] the integer emsg value
11
+ def initialize(emsg)
12
+ @emsg = emsg
13
+ end
14
+
15
+ # Return the name of the EMsg
16
+ #
17
+ # @return [String]
18
+ def name
19
+ emsg.to_s
20
+ end
21
+
22
+ private
23
+
24
+ # @api private
25
+ def emsg
26
+ EMsg.constants.select { |x| EMsg.const_get(x) == @emsg }.first
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,78 @@
1
+ # frozen_string_literal: true
2
+ module Steam
3
+ module Handler
4
+ # Steam Auth handler. Handles channel encryption and decryption.
5
+ #
6
+ # @example Using the handler inside Handler::Collection
7
+ # collection = Handler::Collection.new
8
+ # collection << Handler::Auth.new
9
+ # collection.handle(packet)
10
+ #
11
+ # @example Using the handler stand alone
12
+ # handler = Handler::Auth.new
13
+ # handler.handle_packet(packet)
14
+ class Auth
15
+ include Handler::Base
16
+
17
+ # Respond the Channel encrypt requests and results
18
+ handles EMsg::CHANNEL_ENCRYPT_REQUEST, EMsg::CHANNEL_ENCRYPT_RESULT
19
+
20
+ # Override the constructor to set our key to nil. This key will be
21
+ # used if the encryption is a success to decode and encode all future
22
+ # packet data.
23
+ def initialize(*)
24
+ super
25
+ @key = nil
26
+ end
27
+
28
+ # Handles the given packet. This handler is only concerned about the
29
+ # channel encryption request and result.
30
+ #
31
+ # @param packet [Networking::Packet] the packet to handle
32
+ def handle(packet)
33
+ case packet.msg_type
34
+ when EMsg::CHANNEL_ENCRYPT_REQUEST
35
+ handle_channel_encrypt_request(packet)
36
+ when EMsg::CHANNEL_ENCRYPT_RESULT
37
+ handle_channel_encrypt_result(packet)
38
+ end
39
+ end
40
+
41
+ private
42
+
43
+ # Decodes the packet as a MsgChannelEncryptResult. If the result is
44
+ # a success the client's connection session key is set.
45
+ #
46
+ # @param packet [Networking::Packet]
47
+ def handle_channel_encrypt_result(packet)
48
+ msg = packet.as_message(MsgChannelEncryptResult.new)
49
+ raise 'encrypt result failed' unless msg.body.result == EResult::OK
50
+
51
+ # Set the session key
52
+ client.session_key = @key
53
+ client.ready
54
+ Steam.logger.debug('Client channel encrypted')
55
+ true
56
+ end
57
+
58
+ # Decodes the packet as a MsgChannelEncryptResponse. A key is generated
59
+ # and sent to the steam servers. If the encryption request is a success
60
+ # the connection's session key is set in the handling of the result
61
+ #
62
+ # @param _packet [Networking::Packet]
63
+ def handle_channel_encrypt_request(_packet)
64
+ encrypt = Crypto.new
65
+ crypted_key, key = encrypt.generate_key
66
+
67
+ msg = ClientMessage.new(MsgHdr.new,
68
+ MsgChannelEncryptResponse.new,
69
+ EMsg::CHANNEL_ENCRYPT_RESPONSE)
70
+ msg.payload.write(crypted_key)
71
+ msg.payload.write_int64(encrypt.session_key_crc(crypted_key))
72
+
73
+ send_msg(msg)
74
+ @key = key
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,79 @@
1
+ # frozen_string_literal: true
2
+ module Steam
3
+ module Handler
4
+ # Defines the base Handler
5
+ #
6
+ # @example Creating a handler
7
+ # class MyHandler
8
+ # include HandlerBase
9
+ #
10
+ # handles :CLIENT_LOG_OFF
11
+ #
12
+ # def handle(packet)
13
+ # end
14
+ # end
15
+ module Base
16
+ # Inject class methods into the hosting class
17
+ def self.included(base)
18
+ base.send(:extend, ClassMethods)
19
+ end
20
+
21
+ # The Client object
22
+ #
23
+ # @see Client
24
+ attr_reader :client
25
+
26
+ # Instantiate a Handler with a Client
27
+ #
28
+ # @example Creating a Handler
29
+ # class MyHandler
30
+ # include Handler::Base
31
+ # end
32
+ #
33
+ # handler = MyHandler.new(Client.new)
34
+ def initialize(client)
35
+ @client = client
36
+ end
37
+
38
+ # Handle the incoming packet
39
+ #
40
+ # @param _packet [Networking::Packet] the packet to handle
41
+ def handle(_packet)
42
+ raise NotImplementedError
43
+ end
44
+
45
+ # Determines if this handler can handle the given message type. It
46
+ # compares the symbols given via .handles to known EMsg constants.
47
+ #
48
+ # @param msg [Integer]
49
+ # @return [Bool]
50
+ def handles?(msg)
51
+ self.class.handled_messages.any? { |m| msg == m }
52
+ end
53
+
54
+ # Send a message to the client
55
+ #
56
+ # @param msg [Message] The Message to send to steam
57
+ # @return [Bool]
58
+ def send_msg(msg)
59
+ @client.send_msg(msg)
60
+ end
61
+
62
+ # Inject methods into the hosting class
63
+ module ClassMethods
64
+ # The list of EMsgs that this handler knows
65
+ # how to handle
66
+ def handled_messages
67
+ @handled_messages ||= []
68
+ end
69
+
70
+ # Specify with EMsgs this Handler cares about
71
+ def handles(*args)
72
+ handled_messages << args.dup.flatten
73
+ handled_messages.flatten!
74
+ handled_messages.uniq!
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+ module Steam
3
+ module Handler
4
+ # Holds a collection of Handler objects
5
+ #
6
+ # @example Create a collection of Handler objects
7
+ # collection = Handler::Collection.new
8
+ # collection << MyHandler.new(client)
9
+ class Collection
10
+ include Enumerable
11
+
12
+ # Creates an empty list of handlers
13
+ def initialize
14
+ @handlers = []
15
+ end
16
+
17
+ # Handle a packet. If a handler is found that cares about this packet,
18
+ # the packet object is passed to the handler.
19
+ #
20
+ # @param packet [Networking::Packet] the packet to handle
21
+ def handle(packet)
22
+ handler = find_handler(packet.msg_type)
23
+
24
+ if handler.nil?
25
+ Steam.logger.debug("No hander found for: #{EMsgUtil.new(packet.emsg).name}")
26
+ return false
27
+ end
28
+
29
+ handler.handle(packet)
30
+ end
31
+
32
+ # Add a handler to the collection
33
+ #
34
+ # @param handlers [Array<Handler::Base>] the handler to add
35
+ def add(*handlers)
36
+ handlers.each do |handler|
37
+ @handlers << handler
38
+ Steam.logger.debug("Added handler #{handler.class.name}")
39
+ end
40
+ end
41
+
42
+ # Iterate through each Handler. Allows the collection to act as an
43
+ # Enumerable
44
+ def each(&block)
45
+ @handlers.each(&block)
46
+ end
47
+
48
+ private
49
+
50
+ # @api private
51
+ # :nocov:
52
+ def find_handler(msg)
53
+ message_handlers = @handlers.select do |handler|
54
+ handler.handles?(msg)
55
+ end
56
+ message_handlers.first
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,80 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'securerandom'
4
+ module Steam
5
+ module Handler
6
+ # Game coordinator handler
7
+ class GameCoordinator
8
+ include Handler::Base
9
+
10
+ # Describes the EMsgs this Handler cares about
11
+ handles EMsg::CLIENT_FROM_GC
12
+
13
+ # Send a message to the Game Coordinator
14
+ #
15
+ # @note you must have asked and been given a GC session
16
+ # @todo remove hardcoded app id
17
+ #
18
+ # @param msg [GcMessage] The message to send
19
+ # @return [Bool]
20
+ def send_msg(msg)
21
+ gcmsg = Steamclient::CMsgGCClient.new
22
+ gcmsg.appid = 730
23
+ gcmsg.msgtype = msg.header.msg
24
+ gcmsg.payload = msg.encode
25
+
26
+ header = MsgHdrProtoBuf.new
27
+ # header.proto.routing_appid = 730
28
+ gc = ProtobufMessage.new(header, gcmsg, EMsg::CLIENT_TO_GC)
29
+
30
+ client.send_msg(gc)
31
+ end
32
+
33
+ # Handle a packet
34
+ #
35
+ # @param packet [Networking::Packet] the packet to handle
36
+ def handle(packet)
37
+ case packet.msg_type
38
+ when EMsg::CLIENT_FROM_GC
39
+ handle_gc_message(packet)
40
+ end
41
+ end
42
+
43
+ # Tell the Steam servers that the Client is playing a game.
44
+ #
45
+ # @param game_id [Integer] the game to play
46
+ #
47
+ # @example Playing CS:GO
48
+ # game_coordinator = client.game_coordinator
49
+ # game_coordinator.play(730, "CS:GO")
50
+ def play(game_id)
51
+ game = Steamclient::CMsgClientGamesPlayed.new
52
+
53
+ played = Steamclient::CMsgClientGamesPlayed::GamePlayed.new
54
+ played.game_id = game_id
55
+ played.token = @client.connect_tokens.shift
56
+
57
+ game.games_played ||= [played]
58
+
59
+ msg = ProtobufMessage.new(MsgHdrProtoBuf.new, game,
60
+ EMsg::CLIENT_GAMES_PLAYED_WITH_DATA_BLOB)
61
+ client.send_msg(msg)
62
+ end
63
+
64
+ private
65
+
66
+ # @api private
67
+ def handle_gc_message(packet)
68
+ msg = packet.as_message(Steamclient::CMsgGCClient.new)
69
+
70
+ gcmsg = GcProtobufMessage.new(
71
+ MsgGCHdrProtoBuf.new,
72
+ msg.body,
73
+ msg.body.msgtype
74
+ )
75
+
76
+ client.gc_message(gcmsg)
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+ module Steam
3
+ module Handler
4
+ # Steam apps handler
5
+ class SteamApps
6
+ include Handler::Base
7
+
8
+ # Describes the EMsgs this Handler cares about
9
+ handles EMsg::CLIENT_GAME_CONNECT_TOKENS
10
+
11
+ # Handle a packet
12
+ #
13
+ # @param packet [Networking::Packet] the packet to handle
14
+ def handle(packet)
15
+ case packet.msg_type
16
+ when EMsg::CLIENT_GAME_CONNECT_TOKENS
17
+ handle_client_game_connect_tokens(packet)
18
+ end
19
+ end
20
+
21
+ private
22
+
23
+ # Handles a packet that contains connect tokens. Connect tokens
24
+ # are required to tell the Steam servers you are playing a game.
25
+ #
26
+ # @param packet [Networking::Packet] the packet
27
+ def handle_client_game_connect_tokens(packet)
28
+ msg = packet.as_message(Steamclient::CMsgClientGameConnectTokens.new)
29
+
30
+ @client.update_connect_tokens(msg.body.tokens,
31
+ msg.body.max_tokens_to_keep)
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,170 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Steam
4
+ module Handler
5
+ # Handles messages releated to a Steam user
6
+ #
7
+ # @example Logging in
8
+ # steam_user = client.steam_user
9
+ # steam_user.login(username, password, token, hash)
10
+ #
11
+ # @note You need to handle the LogonResponse to catch Steam Guard prompts
12
+ # and 2FA
13
+ class SteamUser
14
+ include Handler::Base
15
+
16
+ handles EMsg::CLIENT_LOG_ON_RESPONSE, EMsg::CLIENT_UPDATE_MACHINE_AUTH,
17
+ EMsg::CLIENT_LOGGED_OFF, EMsg::CLIENT_NEW_LOGIN_KEY
18
+
19
+ # rubocop:disable MethodLength
20
+ # rubocop:disable AbcSize
21
+ #
22
+ # Log a Client in
23
+ #
24
+ # @param username [String] the username
25
+ # @param password [String] the password
26
+ # @param token [String] the SteamGuard token or nil
27
+ # @param hash [String] the SteamGuard hash or nil
28
+ #
29
+ # @return True or false if the message was sent. You must handle the
30
+ # ClientLogonResponse to know the real logon result
31
+ def login(username, password, token = nil, hash = nil)
32
+ msg = ProtobufMessage.new(MsgHdrProtoBuf.new,
33
+ Steamclient::CMsgClientLogon.new,
34
+ EMsg::CLIENT_LOGON)
35
+
36
+ msg.body.auth_code = token if token
37
+ msg.body.eresult_sentryfile = EResult::FILE_NOT_FOUND
38
+
39
+ if hash
40
+ msg.body.sha_sentryfile = hash
41
+ msg.body.eresult_sentryfile = EResult::OK
42
+ end
43
+
44
+ ip = LocalIp.new.to_i ^ MsgClientLogon::OBFUSCATION_MASK
45
+ msg.body.obfustucated_private_ip = ip
46
+ msg.body.account_name = username
47
+ msg.body.password = password
48
+ msg.body.protocol_version = MsgClientLogon::CURRENT_PROTOCOL
49
+ msg.body.client_os_type = EOSType::WIN311
50
+ msg.body.client_package_version = 1771
51
+
52
+ send_msg(msg)
53
+ end
54
+
55
+ # Change the Client's persona state.
56
+ #
57
+ # @example Setting a Client as "Online"
58
+ # steam_user = client.steam_user
59
+ # steam_user.change_persona_state(EPersonaState::ONLINE)
60
+ #
61
+ # @param state [EMsg::E_PERSONA_STATE] the new client state
62
+ # @return True if the message was sent, false otherwise
63
+ def change_persona_state(state)
64
+ client_status = Steamclient::CMsgClientChangeStatus.new
65
+ client_status.persona_state = state
66
+
67
+ client_change_status = ProtobufMessage.new(MsgHdrProtoBuf.new,
68
+ client_status,
69
+ EMsg::CLIENT_CHANGE_STATUS)
70
+ send_msg(client_change_status)
71
+ end
72
+
73
+ # Change the Client's name
74
+ #
75
+ # @example Setting a Client as "Jimbo"
76
+ # steam_user = client.steam_user
77
+ # steam_user.change_persona_name("Jimbo")
78
+ #
79
+ # @param name [String] the new client name
80
+ # @return True if the message was sent, false otherwise
81
+ def change_persona_name(name)
82
+ client_status = Steamclient::CMsgClientChangeStatus.new
83
+ client_status.player_name = name
84
+
85
+ client_change_status = ProtobufMessage.new(MsgHdrProtoBuf.new,
86
+ client_status,
87
+ EMsg::CLIENT_CHANGE_STATUS)
88
+ send_msg(client_change_status)
89
+ end
90
+
91
+ # Logs the Client off the Steam network
92
+ #
93
+ # @return [Bool]
94
+ def logoff
95
+ logoff = ProtobufMessage.new(MsgHdrProtoBuf.new,
96
+ Steamclient::CMsgClientLogOff.new,
97
+ EMsg::CLIENT_LOG_OFF)
98
+ send_msg(logoff)
99
+ end
100
+
101
+ # Handles a given packet
102
+ #
103
+ # @param packet [Networking::Packet] the packet to handle
104
+ def handle(packet)
105
+ case packet.msg_type
106
+ when EMsg::CLIENT_LOG_ON_RESPONSE
107
+ handle_client_logon_response(packet)
108
+ when EMsg::CLIENT_UPDATE_MACHINE_AUTH
109
+ handle_machine_auth_update(packet)
110
+ when EMsg::CLIENT_NEW_LOGIN_KEY
111
+ handle_login_key(packet)
112
+ when EMsg::CLIENT_LOGGED_OFF
113
+ handle_client_logoff(packet)
114
+ end
115
+ end
116
+
117
+ private
118
+
119
+ # @api private
120
+ def handle_client_logoff(packet)
121
+ msg = packet.as_message(Steamclient::CMsgClientLoggedOff.new)
122
+ raise NotImplementedError, msg.inspect.to_s
123
+ end
124
+
125
+ # @api private
126
+ def handle_login_key(packet)
127
+ msg = packet.as_message(Steamclient::CMsgClientNewLoginKey.new)
128
+
129
+ resp = Steamclient::CMsgClientNewLoginKeyAccepted.new
130
+ resp.unique_id = msg.body.unique_id
131
+ resp = ProtobufMessage.new(MsgHdrProtoBuf.new,
132
+ resp, EMsg::CLIENT_NEW_LOGIN_KEY_ACCEPTED)
133
+ send_msg(resp)
134
+ end
135
+
136
+ # @api private
137
+ def handle_machine_auth_update(packet)
138
+ msg = packet.as_message(Steamclient::CMsgClientUpdateMachineAuth.new)
139
+ digest = Digest::SHA1.digest(msg.body.bytes)
140
+
141
+ resp = Steamclient::CMsgClientUpdateMachineAuthResponse.new
142
+ resp.sha_file = digest
143
+
144
+ file = SentryFile.new
145
+ file.write(digest)
146
+
147
+ header = MsgHdrProtoBuf.new
148
+ header.proto.jobid_target = msg.header.proto.jobid_source
149
+ resp = ProtobufMessage.new(header, resp,
150
+ EMsg::CLIENT_UPDATE_MACHINE_AUTH_RESPONSE)
151
+ send_msg(resp)
152
+ end
153
+
154
+ # @api private
155
+ def handle_client_logon_response(packet)
156
+ resp = packet.as_message(Steamclient::CMsgClientLogonResponse.new)
157
+
158
+ body = resp.body
159
+ proto = resp.header.proto
160
+ if body.eresult == EResult::OK && proto.client_sessionid.positive?
161
+ client.create_session(proto.steamid,
162
+ proto.client_sessionid)
163
+ client.start_heartbeat(body.out_of_game_heartbeat_seconds)
164
+ end
165
+
166
+ client.on_logon(resp)
167
+ end
168
+ end
169
+ end
170
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+ module Steam
3
+ # Messsage handlers
4
+ #
5
+ # @see Handler::Base
6
+ module Handler
7
+ end
8
+ end
9
+
10
+ require 'steam/handler/base'
11
+ require 'steam/handler/collection'
12
+ require 'steam/handler/auth'
13
+ require 'steam/handler/steam_user'
14
+ require 'steam/handler/steam_apps'
15
+ require 'steam/handler/game_coordinator'
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+ require 'socket'
3
+ require 'ipaddr'
4
+
5
+ module Steam
6
+ # Access to the Local IP
7
+ #
8
+ # @example Get Local IP
9
+ # ip = LocalIp.new
10
+ # ip.to_s # => '192.168.1.158'
11
+ # ip.to_i # => 3232235934
12
+ class LocalIp
13
+ def initialize
14
+ @addr = Socket.ip_address_list.detect(&:ipv4_private?)
15
+ end
16
+
17
+ # Get the IP address as a string
18
+ #
19
+ # @return [String] the ip
20
+ def to_s
21
+ @addr.ip_address
22
+ end
23
+ alias address to_s
24
+
25
+ # Get the IP address as a int
26
+ #
27
+ # @return [Integer] the ip
28
+ def to_i
29
+ IPAddr.new(address).to_i
30
+ end
31
+ end
32
+ end