]> git.netwichtig.de Git - user/henk/code/ruby/rbot.git/blob - data/rbot/plugins/iplookup.rb
search: wolfram fix
[user/henk/code/ruby/rbot.git] / data / rbot / plugins / iplookup.rb
1 #################################################################
2 # IP Lookup Plugin
3 # ----------------------------
4 # by Chris Gahan (chris@ill-logic.com)
5 #
6 # Purpose:
7 # ------------------
8 # Lets you lookup the owner and their address for any IP address
9 # or IRC user.
10 #
11 #################################################################
12
13 require 'socket'
14 require 'resolv'
15
16 #################################################################
17 ## ARIN Whois module...
18 ##
19
20 module ArinWhois
21
22   class Chunk < Hash
23     def customer?
24       keys.grep(/^(City|Address|StateProv|(Org|Cust)Name)$/).any?
25     end
26
27     def network?
28       keys.grep(/^(CIDR|NetHandle|Parent)$/).any?
29     end
30
31     def contact?
32       keys.grep(/^(R|Org)(Tech|Abuse)(Handle|Name|Phone|Email)$/).any?
33     end
34
35     def valid?
36       customer? or network? or contact?
37     end
38
39     def owner
40       self[keys.grep(/^(Org|Cust)Name$/).first]
41     end
42
43     def location
44       [ self['City'], self['StateProv'], self['Country'] ].compact.join(', ')
45     end
46
47     def address
48       [ self['Address'], location, self['PostalCode'] ].compact.join(', ')
49     end
50
51   end
52
53   class ArinWhoisParser
54
55     def initialize(data)
56       @data = data
57     end
58
59     def split_array_at(a, &block)
60       return a unless a.any?
61       a = a.to_a
62
63       results = []
64       last_cutpoint = 0
65
66       a.each_with_index do |el,i|
67         if block.call(el)
68           unless i == 0
69             results << a[last_cutpoint...i]
70             last_cutpoint = i
71           end
72         end
73       end
74
75       if last_cutpoint < a.size or last_cutpoint == 0
76         results << a[last_cutpoint..-1]
77       end
78
79       results
80     end
81
82     # Whois output format
83     # ------------------------
84     # Owner info block:
85     #   {Org,Cust}Name
86     #   Address
87     #   City
88     #   StateProv
89     #   PostalCode
90     #   Country (2-digit)
91     #
92     # Network Information:
93     #   CIDR (69.195.25.0/25)
94     #   NetHandle (NET-72-14-192-0-1)
95     #   Parent (NET-72-0-0-0-0)
96     #
97     # Contacts:
98     #   ({R,Org}{Tech,Abuse}{Handle,Name,Phone,Email})*
99
100     def parse_chunks
101       return if @data =~ /^No match found /
102       chunks = @data.gsub(/^# ARIN WHOIS database, last updated.+/m, '').scan(/(([^\n]+\n)+\n)/m)
103       chunks.map do |chunk|
104         result = Chunk.new
105
106         chunk[0].scan(/([A-Za-z]+?):(.*)/).each do |tuple|
107           tuple[1].strip!
108           result[tuple[0]] = tuple[1].empty? ? nil : tuple[1]
109         end
110
111         result
112       end
113     end
114
115
116     def get_parsed_data
117       return unless chunks = parse_chunks
118
119       results = split_array_at(chunks) {|chunk|chunk.customer?}
120       results.map do |data|
121         {
122           :customer => data.select{|x|x.customer?}[0],
123           :net      => data.select{|x|x.network?}[0],
124           :contacts => data.select{|x|x.contact?}
125         }
126       end
127     end
128
129     # Return a hash with :customer, :net, and :contacts info filled in.
130     def get_most_specific_owner
131       return unless datas = get_parsed_data
132
133       datas_with_bitmasks = datas.map do |data|
134         bitmask = data[:net]['CIDR'].split('/')[1].to_i
135         [bitmask, data]
136       end
137       #datas_with_bitmasks.sort.each{|x|puts x[0]}
138       winner = datas_with_bitmasks.sort[-1][1]
139     end
140
141   end # of class ArinWhoisParser
142
143 module_function
144
145   def raw_whois(query_string, host)
146     s = TCPsocket.open(host, 43)
147     s.write(query_string+"\n")
148     ret = s.read
149     s.close
150     return ret
151   end
152
153   def lookup(ip)
154     data = raw_whois("+#{ip}", 'whois.arin.net')
155     arin = ArinWhoisParser.new data
156     arin.get_most_specific_owner
157   end
158
159   def lookup_location(ip)
160     result = lookup(ip)
161     result[:customer].location
162   end
163
164   def lookup_address(ip)
165     result = lookup(ip)
166     result[:customer].address
167   end
168
169   def lookup_info(ip)
170     if result = lookup(ip)
171       "#{result[:net]['CIDR']} => #{result[:customer].owner} (#{result[:customer].address})"
172     else
173       "Address not found."
174     end
175   end
176
177 end
178
179
180
181 #################################################################
182 ## The Plugin
183 ##
184
185 class IPLookupPlugin < Plugin
186   def help(plugin, topic="")
187     "iplookup [ip address / domain name] => lookup info about the owner of the IP address from the ARIN whois database"
188   end
189
190   def iplookup(m, params)
191     reply = ""
192     if params[:domain].match(/^#{Regexp::Irc::HOSTADDR}$/)
193       ip = params[:domain]
194     else
195       begin
196         ip = Resolv.getaddress(params[:domain])
197         reply << "#{params[:domain]} | "
198       rescue => e
199         m.reply "#{e.message}"
200         return
201       end
202     end
203
204     reply << ArinWhois.lookup_info(ip)
205
206     m.reply reply
207   end
208
209   def userip(m, params)
210     m.reply "not implemented yet"
211     #users = @channels[m.channel].users
212     #m.reply "users = #{users.inspect}"
213     #m.reply @bot.sendq("WHO #{params[:user]}")
214   end
215
216 end
217
218 plugin = IPLookupPlugin.new
219 plugin.map 'iplookup :domain', :action => 'iplookup', :thread => true
220 plugin.map 'userip :user', :action => 'userip', :requirements => {:user => /\w+/}, :thread => true
221
222
223 if __FILE__ == $0
224   include ArinWhois
225   data = open('whoistest.txt').read
226   c = ArinWhoisParser.new data
227   puts c.get_parsed_data.inspect
228 end