####################################################
# by wiebe @ QuakeNet
#
#
# relies on dnsdb.tcl
#
####################################################


####################################################
# dnsbans:join
####################################################
bind join -|- * dnsbans:join; bind rejn -|- * dnsbans:join
proc dnsbans:join { n u h c } {
  if {![validchan $c] || ![botonchan $c] || [channel get $c inactive] || [isbotnick $n]} { return 0 }
  if {[matchban $n!$u $c]} { return 0 }
  set u [split $u @]; set r [lindex $u 1]; set u [lindex $u 0]; set i ""
  if {[dnsbans:validip $r]} { set i $r }
  dnsbans:check $n $u $r "" $i $c
}


####################################################
# dnsbans:time
####################################################
bind time -|- * dnsbans:time
proc dnsbans:time { mi ho da mo ye } {
  global dnsbans
  if {![info exists dnsbans(global)]} { set dnsbans(global) "" }
  set m [md5 [banlist]]
  if {![string equal $dnsbans(global) $m]} {
    foreach c [channels] { dnsbans:newban $c }
  } else {
    foreach c [channels] {
      set m [md5 [banlist $c]]
      if {![info exists dnsbans($c)]} { set dnsbans($c) "" }
      if {![botonchan $c]} { unset dnsbans($c); continue }
      if {[string equal $dnsbans($c) $m]} { continue }
      dnsbans:newban $c
    }
  }
  unset dnsbans
  set dnsbans(global) [md5 [banlist]]
  foreach c [channels] { set dnsbans($c) [md5 [banlist $c]] }
}


####################################################
# dnsbans:newban
####################################################
proc dnsbans:newban { c } {
  if {![validchan $c]} { return 0 }
  foreach n [chanlist $c] {
    set i ""; set h ""
    if {![string equal [info procs dnsdb:get] ""]} {
      set i [dnsdb:get $n i]; set h [dnsdb:get $n h]
    }
    set r [split [getchanhost $n $c] @]; set u [lindex $r 0]; set r [lindex $r 1]
    dnsbans:check $n $u $r $h $i $c
  }
}


####################################################
# dnsbans:check
####################################################
proc dnsbans:check { n u r h i {chan ""} } {
  if {![string equal $i ""] && ![dnsbans:validip $i]} { return 0 }
  if {[matchban $n!$u@$r]} { return 0 }
  set chan [split $chan]
  if {[string equal $chan ""]} { set chan [channels] } elseif {![validchan $chan]} { return 0 }
  if {![string equal $i ""] && [dnsbans:bancidr $n $u $r $h $i]} { return 0 }
  foreach c $chan {
    if {![botonchan $c]} { continue }
    if {![onchan $n $c]} { continue }
    if {[matchban $n!$u@$r $c]} { continue }
    if {![string equal -nocase $r $h] && ![string equal $h ""]} {
      set b [dnsbans:banresolved $n $u $r $h $c]
      if {$b == "2"} { return 0 } elseif {$b == "1"} { continue }
    }
    if {[string equal $i ""]} { continue }
    if {![string equal -nocase $r $i]} {
      set b [dnsbans:banresolved $n $u $r $i $c]
      if {$b == "2"} { return 0 } elseif {$b == "1"} { continue }
    }
    if {[dnsbans:bancidr $n $u $r $h $i $c]} { return 0 }
  }
}


####################################################
# dnsbans:banresolved
####################################################
proc dnsbans:banresolved { n u r h c } {
  if {![matchban $n!$u@$h $c] && ![matchban @$n!$u@$h $c]} { return 0 }
  set g 1
  if {[matchban $n!$u@$h] || [matchban @$n!$u@$h]} { set bans [banlist]; set g 2 } else { set bans [banlist $c] }
  foreach ban $bans {
    set hm [lindex $ban 0]; set co [lindex $ban 1]; set ex [lindex $ban 2]
    set ta [lindex $ban 3]; set la [lindex $ban 4]; set cr [lindex $ban 5]
    regsub -all {[][\\]} $hm {\\\0} hm
    if {![string match -nocase $hm $n!$u@$h] && ![string match -nocase $hm @$n!$u@$h]} { continue }
    dnsbans:ban $n $u $r $co $c
    return $g
  }
  return 0
}


####################################################
# dnsbans:bancidr
####################################################
proc dnsbans:bancidr { n u r h i {c ""} } {
  if {[string equal $c ""]} { set bans [banlist] } else { set bans [banlist $c] }
  foreach ban $bans {
    set hm [lindex $ban 0]; set co [lindex $ban 1]; set ex [lindex $ban 2]
    set ta [lindex $ban 3]; set la [lindex $ban 4]; set cr [lindex $ban 5]
    if {![dnsbans:validcidr [lindex [split $hm @] end]]} { continue }
    if {![dnsbans:matchcidr $n $u $i $hm]} { continue }
    dnsbans:ban $n $u $r $co $c
    return 1
  }
  return 0
}


####################################################
# dnsbans:ban
####################################################
proc dnsbans:ban { n u h r {c ""} } {
  set m "*!*@$h"; set l "120"; set b "dnsbans.tcl"
  if {![string match "~*" $u]} { set m "*!$u@$h" }
  if {[string equal $c ""]} {
    if {![string equal [info procs newban:ban] ""]} { newban:ban $m $b $r $l
    } else { newban $m $b $r $l }
  } else {
    if {![string equal [info procs newchanban:ban] ""]} { newchanban:ban $c $m $b $r $l
    } else { newchanban $c $m $b $r $l }
  }
}


####################################################
# dnsbans:matchcidr
####################################################
proc dnsbans:matchcidr { n u i b } {
  set b [string tolower $b]
  if {![string match "*!*@*" $b]} { return 0 }
  set n [string tolower $n]; set u [string tolower $u]
  if {[string match @* $b]} { set b [string range $b 1 end] }
  set b [split $b !@]; set bn [lindex $b 0]; set bu [lindex $b 1]; set bc [lindex $b 2]
  regsub -all {[][\\]} $bn {\\\0} bn; regsub -all {[][\\]} $bu {\\\0} bu
  if {![string match $bn $n] && ![string match $bn @$n]} { return 0 }
  if {![string match $bu $u]} { return 0 }
  set bc [split $bc /]; set bi [lindex $bc 0]; set bc [lindex $bc 1]
  set li [dnsbans:longip $i]
  set f [lsort [dnsbans:cidr $bi $bc]]
  set l [lindex [split $f] 1]; set f [lindex [split $f] 0]
  set f [dnsbans:longip $f]; set l [dnsbans:longip $l]
  if {$li < $f || $li > $l} { return 0 }
  return 1
}


####################################################
# dnsbans:longip
####################################################
proc dnsbans:longip { ip } {
  if {![dnsbans:validip $ip]} { return "" }
  set i [split $ip .]
  set i1 [lindex $i 0]; set i2 [lindex $i 1]; set i3 [lindex $i 2]; set i4 [lindex $i 3]
  set l [expr "$i1.*256*256*256 + $i2.*256*256 + $i3.*256 + $i4"]
  set l [lindex [split $l .] 0]
  return $l
}


####################################################
# dnsbans:validip
####################################################
proc dnsbans:validip { ip } {
  if {[regexp {^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$} $ip]} { return 1 }
  return 0
}


####################################################
# dnsbans:validcidr
####################################################
proc dnsbans:validcidr { ip } {
  if {[regexp {^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)/(\d|[12]\d|3[0-2])$} $ip]} { return 1 }
  return 0
}


set scriptdb(dnsbans) {
  "provides support for CIDR, IP and hostname bans (may the user be resolved or not), this is an extension of dnsdb.tcl"
}


####################################################
# code beyond this point is not mine, see credits below
#
# small changes made, code shortened, adjusted for use in this script, comments removed
####################################################

####################################################
#http://wiki.tcl.tk/8909
#!/bin/sh
# Emacs: please open this file in -*-Tcl-*- mode
#
# Author: Mark Oakden http://wiki.tcl.tk/MNO
# Version: 1.1
#
####################################################

####################################################
# dnsbans:iptoHex
####################################################
proc dnsbans:iptoHex { IP } {
  binary scan [binary format c4 [split $IP .]] H8 Hex
  return $Hex
}

####################################################
# dnsbans:hexToIP
####################################################
proc dnsbans:hexToIP { Hex } {
  binary scan [binary format H8 $Hex] c4 IPtmp
  foreach num $IPtmp { lappend IP [expr ($num + 0x100) % 0x100] }
  set IP [join $IP .]
  return $IP
}

####################################################
# dnsbans:CIDRtoHexNetmask
####################################################
proc dnsbans:CIDRtoHexNetmask { CIDR } {
  set zeros [expr 32 - $CIDR]
  set ones $CIDR
  set binaryCIDR [string repeat 1 $ones]
  append binaryCIDR [string repeat 0 $zeros]
  binary scan [binary format B32 $binaryCIDR] H8 HexNetmask
  return $HexNetmask
}

####################################################
# dnsbans:ipisValid
####################################################
proc dnsbans:ipisValid { IP } {
  regsub -all {[.0-9]} $IP {} tmpStr
  if {$tmpStr != ""} { return 0 }
  regsub -all {[0-9]} $IP {} tmpStr
  if {$tmpStr != "..."} { return 0 }
  foreach b [split $IP .] {
    if {[string length $b] == 0} { return 0 }
    set ob $b
    scan $b %d b
    if {$b < 0 | $b > 255} { return 0 }
  }
  return 1
}

####################################################
# dnsbans:CIDRisValid
####################################################
proc dnsbans:CIDRisValid { CIDR } {
  if {[string length $CIDR] == 0} { return 0 }
  regsub -all {[0-9]} $CIDR {} tmpStr
  if {[string length $tmpStr] != 0} { return 0 }
  scan $CIDR %d $CIDR
  if {$CIDR < 0 | $CIDR > 32} { return 0 }
  return 1
}

####################################################
# dnsbans:networkAddress
####################################################
proc dnsbans:networkAddress { hexIP hexNetmask } {
  set compNetmask [expr 0x$hexNetmask ^ 0xffffffff]
  set tmpNetAddr [expr ( 0x$hexIP | $compNetmask ) ^ $compNetmask]
  binary scan [binary format I $tmpNetAddr] H8 networkAddress
  return $networkAddress
}

####################################################
# dnsbans:broadcastAddress
####################################################
proc dnsbans:broadcastAddress { hexIP hexNetmask } {
  set tmpBrdAddr [expr 0x$hexIP | ( 0x$hexNetmask ^ 0xffffffff )]
  binary scan [binary format I $tmpBrdAddr] H8 broadcastAddress
  return $broadcastAddress
}

####################################################
# dnsbans:cidr
####################################################
proc dnsbans:cidr { IP CIDR } {
  if {![dnsbans:ipisValid $IP]} { error "IP is not valid" }
  if {![dnsbans:CIDRisValid $CIDR]} { error "CIDR is not valid" }
  set hexIP [dnsbans:iptoHex $IP]
  set hexNetmask [dnsbans:CIDRtoHexNetmask $CIDR]
  set hexNetworkAddress [dnsbans:networkAddress $hexIP $hexNetmask]
  set hexBroadcastAddress [dnsbans:broadcastAddress $hexIP $hexNetmask]
  set networkAddress [dnsbans:hexToIP $hexNetworkAddress]
  set broadcastAddress [dnsbans:hexToIP $hexBroadcastAddress]
  return "$networkAddress $broadcastAddress"
}

