#by wiebe @ QuakeNet


# http://opm.blitzed.org/info
# if something@127.0.0.1 joins
# you dns 1.0.0.127.
# if resolves, bingo!

# opm.blitzed.org cbl.abuseat.org ircbl.ahbl.org list.dsbl.org sbl.csma.biz dnsbl.njabl.org sbl-xbl.spamhaus.org


#config

#+dnsbl		on/off
#+dnsbl-id	ban with details on/off
#+dnsbl-hide	hide ban reason, prefixed with @, includes details
# dnsbl-url	url to include in the ban reason, when dnsbl_id is on and dnsbl_hide is off

#what dns domains to check
set dnsbl(servers) "opm.blitzed.org cbl.abuseat.org ircbl.ahbl.org list.dsbl.org sbl.csma.biz dnsbl.njabl.org sbl-xbl.spamhaus.org"

#ban time (in minutes)
set dnsbl(bantime) 1440

#what lookup methode to use
#1 = dns all domains in one go. goes faster but may use more dns lookups than methode 2
#2 = dns one by one, stop when there are nomore domains to check or a match is found
#for large(r) channels, use 2
set dnsbl(methode) 2


#code

setudef flag dnsbl
setudef flag dnsbl-id
setudef flag dnsbl-hide
setudef str dnsbl-url



bind join -|- * dnsbl:join
bind rejn -|- * dnsbl:join

proc dnsbl:join { nick uhost handle chan } {

#not active
  if { ![channel get $chan dnsbl] } { return 0 }
  set host [lindex [split $uhost @] 1]
  dnsbl:check $host $nick $chan $host ""
}



#bind time - "* * * * *" dnsbl:time
#proc dnsbl:time { mi ho da mo ye } { }



bind raw -|- "315" dnsbl:raw

proc dnsbl:raw { server raw text } {
  global dnsbl
  if { ![info exists dnsbl(servers)] } { return 0 }

  set chan [string tolower [lindex [split $text] 1]]

#invalid chan or bot is not on it, bail out
  if { ![validchan $chan] || ![botonchan $chan] } { return 0 }

#not active
  if { ![channel get $chan dnsbl] } { return 0 }

#already checked
  set join [getchanjoin $::botnick $chan]
  if { [expr [unixtime] - $join] > "180" } { return 0 }

#check all users
  set hosts ""

  foreach nick [chanlist $chan] {

    set host [lindex [split [getchanhost $nick] @] 1]
    if { [string equal [lsearch -exact $hosts $host] -1] } {
      lappend hosts $host
    }
  }
  set host [lindex $hosts 0]
  set hosts [lrange $hosts 1 end]
  utimer 60 [list dnsbl:check $host -1 $chan $host $hosts]
}




#initial check
proc dnsbl:check { host nick chan host2 hosts } {
  global dnsbl
  if { ![channel get $chan dnsbl] } { return 0 }
  if { ![info exists dnsbl(servers)] } { return 0 }

#we can not resolve the hostname, move to next
  if { [string match -nocase *.quakenet.org $host] || [string match -nocase quakenet.org $host] } {
    if { [string equal $hosts ""] } { return 0 }
    set host [lindex $hosts 0]
    set hosts [lrange $hosts 1 end]
    dnsbl:check $host $nick $chan $host $hosts

#check if host part is an ip
  } elseif { [regexp {^(?:(?:2(?:[0-4]\d?|5[0-5])|[01]?\d{1,2})(?:\.|$)){4}$} $host] } {
    dnsbl:check2 $host $host 1 $nick $chan $host2 $hosts
#look up ip
  } else {
    dnslookup $host dnsbl:check2 $nick $chan $host2 $hosts
  }  
}




#we have the ip
proc dnsbl:check2 { ip host status nick chan host2 hosts } {

#failed, next
  if { [string equal $status 0] } {
    if { [string equal $hosts ""] } { return 0 }
    set host [lindex $hosts 0]
    set hosts [lrange $hosts 1 end]
    dnsbl:check $host $nick $chan $host $hosts

#success
  } else {

#reverse ip
    set reverseip [lindex [split $ip .] 3].[lindex [split $ip .] 2].[lindex [split $ip .] 1].[lindex [split $ip .] 0]

#get the servers
    global dnsbl
    if { ![info exists dnsbl(servers)] } { return 0 }
    set servers $dnsbl(servers)

#get the methode to use
    if { ![info exists dnsbl(methode)] } { set methode 2 } else { set methode $dnsbl(methode) }
    if { ![string match {[12]} $methode] } { set methode 2 }
  
#resolve
    if { [string equal $methode 1] } {
      foreach server [split $servers] {
        dnslookup $reverseip.$server dnsbl:check3 $ip $host $reverseip "" $nick $chan $host2 $hosts
      }
    } else {
      set server [lindex [split $servers] 0]
      set servers [join [lrange [split $servers] 1 end]]
      dnslookup $reverseip.$server dnsbl:check3 $ip $host $reverseip $servers $nick $chan $host2 $hosts
    }
  }
}




#we take action
proc dnsbl:check3 { badip badhost status ip host reverseip servers nick chan host2 hosts } {
#failed
  if { [string equal $status 0] } {

#next server
    if { ![string equal $servers ""] } {
      set server [lindex [split $servers] 0]
      set servers [join [lrange [split $servers] 1 end]]
      dnslookup $reverseip.$server dnsbl:check3 $ip $host $reverseip $servers $nick $chan $host2 $hosts
#no servers left, next host
    } else {
      if { [string equal $hosts ""] } { return 0 }
      set host [lindex $hosts 0]
      set hosts [lrange $hosts 1 end]
      dnsbl:check $host $nick $chan $host $hosts
    }

#succes
  } else {

#set bantime
    global dnsbl
    if { [info exists dnsbl(bantime)] } { set bantime $dnsbl(bantime) } else { set bantime 1440 }
    if { ![string is digit $bantime] || $bantime < 0 } { set bantime 1440 }
#set ID
    set badhost [join [lrange [split $badhost .] 4 end] .]
    lappend id "IP: $ip"
    lappend id "LIST: $badhost"
    lappend id "RESULT: $badip"

#ignore ip
    newignore *!*@$ip dnsbl "Possible proxy. $ip $badhost $badip" $bantime

#ignore host if not equal to ip
    if { ![string equal -nocase $ip $host] } {
      newignore *!*@$host dnsbl "Possible proxy. $ip $badhost $badip" $bantime
    }

#get maxbans
    if { ![info exists ::max-bans] || ![string is digit ${::max-bans}] } {
      set maxbans 45
    } else { set maxbans ${::max-bans} }

#check all common channels with the target
    if { [onchan $nick] } {
      foreach c [channels] {
        if { ![onchan $nick $c] } { continue }
        if { ![channel get $c dnsbl] } { continue }

#set reason
        lappend reason "Possible proxy."
        if { [channel get $c dnsbl-hide] } {
          unset reason
          lappend reason "@Possible proxy."
          lappend reason [join $id]
        } elseif { [channel get $c dnsbl-id] } {
          lappend reason [join $id]
          set url [channel get $c dnsbl-url]
          if { ![string equal $url ""] } {
            lappend reason "INFO: $url"
          }
        }
        set reason [join $reason "   "]

#not (half)opped, or
#banlist full
#create ban(s) now
        if { (![botisop $c] && ![botishalfop $c]) || [expr [llength [chanbans $c]] +1] > $maxbans } {
          newchanban $c *!*@$ip dnsbl $reason $bantime
          if { ![string equal -nocase $ip $host] } {
            newchanban $c *!*@$host dnsbl $reason $bantime
          }
#ban
        } else {
          pushmode $c +b *!*@$host2
          utimer 30 [list newchanban $c *!*@$ip dnsbl $reason $bantime]
          if { ![string equal -nocase $ip $host] } {
            utimer 30 [list newchanban $c *!*@$host dnsbl $reason $bantime]
          }
        }
      }
#user gone (left or changed nick) after lookup started, ban on chan
    } else {
#set reason
      lappend reason "Possible proxy."
      if { [channel get $chan dnsbl-hide] } {
        unset reason
        lappend reason "@Possible proxy."
        lappend reason [join $id]
      } elseif { [channel get $chan dnsbl-id] } {
        lappend reason [join $id]
        set url [channel get $chan dnsbl-url]
        if { ![string equal $url ""] } {
          lappend reason "INFO: $url"
        }
      }
      set reason [join $reason "   "]
      pushmode $chan +b *!*@$host2
      utimer 30 [list newchanban $chan *!*@$ip dnsbl $reason $bantime]
      if { ![string equal -nocase $ip $host] } {
        utimer 30 [list newchanban $chan *!*@$host dnsbl $reason $bantime]
      }
    }
#next host
    if { [string equal $hosts ""] } { return 0 }
    set host [lindex $hosts 0]
    set hosts [lrange $hosts 1 end]
    dnsbl:check $host $nick $chan $host $hosts
  }
}

