]> git.netwichtig.de Git - user/henk/code/ruby/rbot.git/blob - data/rbot/plugins/iplookup.rb
Plugin header boilerplating.
[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(parse_chunks) {|chunk|chunk.customer?}
120       results.map do |chunks|
121         {
122           :customer => chunks.select{|x|x.customer?}[0],
123           :net      => chunks.select{|x|x.network?}[0],
124           :contacts => chunks.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]
193       begin
194         ip = Resolv.getaddress(params[:domain])
195         reply += "#{params[:domain]} | "
196       rescue => e
197         m.reply "#{e.message}"
198         return
199       end
200     else
201       ip = params[:ip]
202     end
203
204     reply += ArinWhois.lookup_info(ip)
205     m.reply reply
206   end
207
208   def userip(m, params)
209     #users = @channels[m.channel].users
210     #m.reply "users = #{users.inspect}"
211     #m.reply @bot.sendq("WHO #{params[:user]}")
212   end
213
214 end
215
216 plugin = IPLookupPlugin.new
217 plugin.map 'iplookup :ip', :action => 'iplookup', :requirements => {:ip => /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/}
218 plugin.map 'iplookup :domain', :action => 'iplookup', :requirements => {:domain => /^[a-z0-9\.\-]{4,255}$/i}
219 plugin.map 'userip :user', :action => 'userip', :requirements => {:user => /\w+/}
220
221
222 if __FILE__ == $0
223   include ArinWhois
224   data = open('whoistest.txt').read
225   c = ArinWhoisParser.new data
226   puts c.get_parsed_data.inspect
227 end