diff options
-rwxr-xr-x | bin/mauveclient | 6 | ||||
-rwxr-xr-x | bytemark_example_alerts.sh | 2 | ||||
-rw-r--r-- | lib/mauve/sender.rb | 114 | ||||
-rw-r--r-- | lib/mauve/udp_server.rb | 14 |
4 files changed, 99 insertions, 37 deletions
diff --git a/bin/mauveclient b/bin/mauveclient index 5fbf7dc..30ba57c 100755 --- a/bin/mauveclient +++ b/bin/mauveclient @@ -170,9 +170,11 @@ update.transmission_id = rand(2**63) begin Mauve::Sender.new(ARGV).send(update, verbose) -rescue ArgumentError => ae - error ae.message rescue Protobuf::NotInitializedError => bad error "Alert not initialized fully - you must supply an ID" +rescue ArgumentError => ae + error ae.message +rescue StandardError => ae + error ae.message end diff --git a/bytemark_example_alerts.sh b/bytemark_example_alerts.sh index 1d4041c..faaa833 100755 --- a/bytemark_example_alerts.sh +++ b/bytemark_example_alerts.sh @@ -1,6 +1,6 @@ #!/bin/sh -PRE="ruby -I lib ./bin/mauveclient 127.0.0.1 " +PRE="ruby -I lib ./bin/mauveclient 127.0.0.1" $PRE -o supportbot -i 173123 \ -s "My server is not responding" \ diff --git a/lib/mauve/sender.rb b/lib/mauve/sender.rb index f27efe5..6abe44d 100644 --- a/lib/mauve/sender.rb +++ b/lib/mauve/sender.rb @@ -3,10 +3,12 @@ require 'ipaddr' require 'resolv' require 'socket' require 'mauve/mauve_time' +require 'pp' module Mauve class Sender DEFAULT_PORT = 32741 + include Resolv::DNS::Resource::IN def initialize(*destinations) @@ -22,35 +24,83 @@ module Mauve raise ArgumentError.new("No destinations specified, and could not read any destinations from /etc/mauvealert/mauvesend.destination") end - @destinations = destinations.map do |spec| - next_spec = begin - # FIXME: for IPv6 - port = spec.split(":")[1] || DEFAULT_PORT - IPAddr.new(spec.split(":")[0]) - ["#{spec}:#{port}"] - rescue ArgumentError => not_an_ip_address - Resolv::DNS.open do |dns| - srv_spec = spec[0] == ?_ ? spec : "_mauvealert._udp.#{spec}" - list = dns.getresources(srv_spec, SRV).map do |srv| - srv.target.to_s + ":#{srv.port}" - end - list = [spec] if list.empty? - list.map do |spec2| - spec2_addr, spec2_port = spec2.split(":") - spec2_port ||= DEFAULT_PORT - dns.getresources(spec2_addr, A).map do |a| - "#{a.address}:#{spec2_port}" + # + # Resolv results + # + results = [] + destinations.each do |spec| + case spec + when /^((?:\d{1,3}\.){3}\d{1,3})(?::(\d+))?$/ + # + # IPv4 with a port + # + results << [$1, $2 || DEFAULT_PORT] + + when /^\[?([0-9a-f:]{2,39})\]??$/i + # + # IPv6 without a port + # + results << [$1, $2 || DEFAULT_PORT] + + when /^\[([0-9a-f:]{2,39})\](?::(\d+))?$/i + # + # IPv6 with a port + # + results << [$1, $2 || DEFAULT_PORT] + + when /^([^: ]+)(?::(\d+))?/ + domain = $1 + port = $2 || DEFAULT_PORT + + Resolv::DNS.open do |dns| + # + # Search for SRV records first. If the first character of the + # domain is an underscore, assume that it is a SRV record + # + srv_domain = (domain[0] == ?_ ? domain : "_mauvealert._udp.#{domain}") + + list = dns.getresources(srv_domain, SRV).map do |srv| + [srv.target.to_s, srv.port] end - end - end - end.flatten - error "Can't resolve destination #{spec}" if next_spec.empty? + # + # If nothing found, just use the domain and port + # + list = [[domain, port]] if list.empty? + + list.each do |d,p| + r = [] + + # + # Try IPv4 first. + # + dns.getresources(d, A).each do |a| + r << [a.address.to_s, p] + end + + # + # Try IPv6 too. + # + dns.getresources(d, AAAA).map do |a| + r << [a.address.to_s, p] + end + + results += r unless r.empty? + end + end + end + end + + # + # Validate results. + # + @destinations = [] + + results.each do |ip, port| + ip = IPAddr.new(ip) + @destinations << [ip, port.to_i] + end - next_spec - end. - flatten. - uniq end def send(update, verbose=0) @@ -81,11 +131,13 @@ module Mauve print "Sending #{update.inspect.chomp} to #{@destinations.join(", ")}\n" end - @destinations.each do |spec| - UDPSocket.open do |sock| - ip = spec.split(":")[0] - port = spec.split(":")[1].to_i - sock.send(data, 0, ip, port) + @destinations.each do |ip, port| + begin + UDPSocket.open(ip.family) do |sock| + sock.send(data, 0, ip.to_s, port) + end + rescue StandardError => ex + warn "Got #{ex.to_s} whilst trying to send to #{ip} #{port}" end end end diff --git a/lib/mauve/udp_server.rb b/lib/mauve/udp_server.rb index ba49bd4..59bc5bc 100644 --- a/lib/mauve/udp_server.rb +++ b/lib/mauve/udp_server.rb @@ -4,7 +4,7 @@ require 'socket' require 'mauve/datamapper' require 'mauve/proto' require 'mauve/alert' -require 'log4r' +require 'ipaddr' module Mauve @@ -26,7 +26,15 @@ module Mauve end def open_socket - @socket = UDPSocket.new + # + # check the IP address + # + _ip = IPAddr.new(@ip) + + # + # Specify the family when opening the socket. + # + @socket = UDPSocket.new(_ip.family) @closing_now = false logger.debug("Trying to increase Socket::SO_RCVBUF to 10M.") @@ -101,7 +109,7 @@ module Mauve # # Triggers loop to close socket. # - UDPSocket.open.send("", 0, @socket.addr[2], @socket.addr[1]) unless @socket.nil? or @socket.closed? + UDPSocket.open(Socket.const_get(@socket.addr[0])).send("", 0, @socket.addr[2], @socket.addr[1]) unless @socket.nil? or @socket.closed? super end |