package hostsurvival

/**
 * @Description: 二级域名**
 * @File: domainscan.go
 * @Time: 2020/1/22 18:29
 */
import (
   "fmt"
   "github.com/modood/table"
   "net"
   "runtime"
   "scan/file"
)

type DomainDns struct {
   Ip     [] net.IP
   Cname  string
   Ptr    [] string
   Ns     [] *net.NS
   Mx     [] *net.MX
   Srv    Srv
   Txt    [] string
   domain string //域名
}

type Srv struct {
   cname string
   addrs [] *net.SRV
}

type House struct {
   Ip     string
   Cname  string
   Ptr    string
   Ns     string
   Mx     string
   Txt    string
   Domain string //域名
}

//进行检查
func checkDomain(domain string, domainNamePrefix chan string, exitChan chan bool, data chan DomainDns) {

   for {
      v, ok := <-domainNamePrefix
      if !ok {
         break
      }
      if v != domain {
         v = fmt.Sprintf("%v.%v", v, domain)
      }

      ips, _ := net.LookupIP(v)

      //判定该主机是否有解析
      if len(ips) != 0 {
         //域名的cname
         cname, err := net.LookupCNAME(v)
         if err != nil {
            panic(err)
         }

         //域名的ptr
         ptr := make([] string, 0, 50)
         for _, ip := range ips {
            p, err := net.LookupAddr(ip.String())
            if err != nil {
               break
            }
            ptr = append(ptr, p...)
         }

         //域名ns
         ns, _ := net.LookupNS(v)

         //域名MX
         mx, _ := net.LookupMX(v)

         //域名srv
         srvCname, addrs, _ := net.LookupSRV("xmpp-server", "tcp", v)
         srvVal := Srv{
            cname: srvCname,
            addrs: addrs,
         }

         //域名txt记录
         txt, _ := net.LookupTXT(v)

         rsf := DomainDns{
            Ip:     ips,
            Cname:  cname,
            Ptr:    ptr,
            Ns:     ns,
            Mx:     mx,
            Srv:    srvVal,
            Txt:    txt,
            domain: v,
         }

         data <- rsf
      }
   }

   //进程完毕,写入关闭管道
   exitChan <- true
}

//使用字典对域名进行扫描
func DomainScan(domain string) error {
   //存放结果
   data := make(chan DomainDns, 50)

   //打开字典
   reader, err := file.GetInstance("./dic/domain.dic")
   if err != nil {
      fmt.Println(err)
      return err
   }

   //逐行读取, 支持以切片或管道方式
   domainNamePrefix := make(chan string, 1500)
   domainNamePrefix <- domain //将顶级域名放入
   if err := reader.ReadLine(nil, domainNamePrefix); err != nil {
      fmt.Println(err)
      return err
   }

   //协程检查域名
   cpuNum := runtime.NumCPU()          //设定协程数量
   exitChan := make(chan bool, cpuNum) //关闭管道
   for i := 0; i <= cpuNum; i++ {
      go checkDomain(domain, domainNamePrefix, exitChan, data)
   }

   //关闭结果管道
   go func() {
      for i := 0; i <= cpuNum; i++ {
         _,ok := <-exitChan
         if !ok {
            break
         }
      }
      close(data)
   }()

   s := make([] House, 0, 50)

   //读取结果
   for {
      rsf, ok := <-data
      if !ok {
         break
      }

      //将ip 转为字符串
      ips := ""
      for _, ipV := range rsf.Ip {
         ips += ipV.String() + "|"
      }

      //将ptr转为字符
      ptrs := ""
      for _,ptrV := range rsf.Ptr{
         ptrs += ptrV
      }

      //将ns转为字符串
      nss := ""
      for _, ns := range rsf.Ns {
         nss += (*ns).Host + "|"
      }

      //将mx转为字符
      mxs := ""
      for _, mx := range rsf.Mx {
         mxs += mx.Host + " 优先级:" + string(mx.Pref) + "|"
      }

      //将txt转化
      txtrecords := ""
      for _, txt := range rsf.Txt {
         txtrecords += txt + "|"
      }

      s = append(s, House{
         Ip:     ips,
         Cname:  rsf.Cname,
         Ptr:    ptrs,
         Ns:     nss,
         Mx:     mxs,
         Txt:    txtrecords,
         Domain: rsf.domain,
      }, House{})
   }

   //表格终端输出
   t := table.Table(s)
   fmt.Println(t)
   
   return nil
}

 

 

 

 

package file

import (
   "bufio"
   "fmt"
   "io"
   "os"
   "strings"
)

/**
 * @Description: 逐行读取
 * @File: readline.go
 * @Time: 2020/1/23 10:57
 */
type readline struct {
   FileInfo os.FileInfo   //文件的信息
   File     *os.File      //文件
   Reader   *bufio.Reader //文件缓冲器
}

var instance *readline

/**
 * @Description: 文件操作单例
 * @filePath: 文件路径
 * @File: readline.go
 * @Time: 2020/1/23 10:57
 */
func GetInstance(filePath string) (*readline, error) {
   if instance == nil {

      //读取文件信息
      fileInfo, err := os.Stat(filePath)
      if err != nil {
         return instance, err
      }

      //读取文件
      file, err := os.Open(filePath)
      if err != nil {
         return instance, err
      }

      //创建缓冲器
      reader := bufio.NewReader(file)
      instance = &readline{
         FileInfo: fileInfo,
         File:     file,
         Reader:   reader,
      }
   }
   return instance, nil
}

/**
 * @Description: 文件逐行读取
 * @arr: 读取结果存放切片, 否则为nil
 * @data: 文件结果存放管道, 否则为nil
 * @Time: 2020/1/23 10:57
 */
func (r *readline) ReadLine(arr [] string, data chan string) error {
   if arr == nil && data == nil {
      return fmt.Errorf("parameter error : You can't have two arguments that are nil")
   }

   i := 0
   for {
      //逐行读取
      lineContent, err := r.Reader.ReadBytes('\n')
      if err != nil && err != io.EOF {
         return err
      }

      //存放到结果
      if arr != nil {
         arr[i] = strings.Replace(strings.Replace(string(lineContent), "\n", "", -1), "\r", "", -1)
         i++
      } else {
         data <- strings.Replace(strings.Replace(string(lineContent), "\n", "", -1), "\r", "", -1)
      }

      //如果读取完毕
      if err == io.EOF {
         //如果是管道方式读取,读取完毕关闭管道
         if data != nil {
            close(data)
         }
         break
      }
   }

   r.File.Close()

   return nil
}

 

调用 : 

if err := hostsurvival.DomainScan("google.com"); err != nil{
   panic(err)
}

 

效果图: 

 

 

github 地址 : https://github.com/franklin-aaron/sdnb  (该地址上所开源所有工具仅供学习,以及项目测试用, 不得用于非法用途 !)