steamrb 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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