[
  {
    "path": "README.md",
    "content": "# cDomain\n 利用天眼查查询企业备案\n\n[下载地址](https://github.com/canc3s/cDomain/releases)\n\n## 介绍\n\n可以通过两种方式查询自己想要的企业子公司\n\n1. `-n` 参数：利用（http://beian.tianyancha.com）接口给出的关键字先进行查询。（方便，但会搜索结果受关键字影响，假如会出现一些公司名类同的公司的备案）\n\n   ![image-20210301143501471](https://cdn.jsdelivr.net/gh/canc3s/picBed/img/2021/ee1775ac6ca1ba9bd4d126596b2e4707083.png)\n\n2. `-i` 参数：利用给出的公司id对该公司进行查询。（准确，结果唯一，但需要自己先去查找一级公司，该接口目前没有限制不需要 `cookie`）\n\n3. `-f` 参数：对文件里的所有关键字和id进行查询。因为我比较推荐用id查询，而且为了方便多次递归查询，读文件时会先去该行尝试匹配是否存在公司id，假如不存在就把该行作为关键字进行查询。因此查询可以直接把`cSubsidiary`的结果文件当作`cDomain`的输入文件。\n\n4. 因为天眼查风控比较严格，所以使用时会出现几种情况。一、因为某个ip一段时间内查询次数过多，所以查询时会自动跳到登陆界面，这种情况需要使用一个手机号进行登陆，然后增加cookie去继续查询。二、海外ip或者云服务器ip访问天眼查会显示海外用户，所以最好使用正常的出口ip进行查询。三、假如短时间呢使用很多很多次查询的话，天眼查会有人机判断的验证码，需要手动打开天眼查网站进行一下人机验证。（情况较少）\n\n## 用法\n\n```\nadmin@admin cSubsidiary % go run cDomain.go -h\n\n\n ██████╗██████╗  ██████╗ ███╗   ███╗ █████╗ ██╗███╗   ██╗\n██╔════╝██╔══██╗██╔═══██╗████╗ ████║██╔══██╗██║████╗  ██║\n██║     ██║  ██║██║   ██║██╔████╔██║███████║██║██╔██╗ ██║\n██║     ██║  ██║██║   ██║██║╚██╔╝██║██╔══██║██║██║╚██╗██║\n╚██████╗██████╔╝╚██████╔╝██║ ╚═╝ ██║██║  ██║██║██║ ╚████║\n ╚═════╝╚═════╝  ╚═════╝ ╚═╝     ╚═╝╚═╝  ╚═╝╚═╝╚═╝  ╚═══╝\n\t\t\t\t\t\t\tv0.0.4\n\t\thttps://github.com/canc3s/cDomain\n\nUsage of cDomain:\n  -c string\n    \t天眼查的Cookie\n  -delay int\n    \t请求之间的延迟时间(秒)\n  -f string\n    \t包含公司ID号码的文件\n  -i string\n    \t公司ID号码\n  -n string\n    \t公司名称\n  -no-color\n    \tNo Color\n  -o string\n    \t结果输出的文件(可选)\n  -silent\n    \tSilent mode\n  -timeout int\n    \t连接超时时间(秒) (default 15)\n  -verbose\n    \t详细模式\n  -version\n    \t显示软件版本号\n```\n\n查询子公司\n\n```\nadmin@admin cSubsidiary % go run cSubsidiary.go -n 字节跳动\n\n\n ██████╗██████╗  ██████╗ ███╗   ███╗ █████╗ ██╗███╗   ██╗\n██╔════╝██╔══██╗██╔═══██╗████╗ ████║██╔══██╗██║████╗  ██║\n██║     ██║  ██║██║   ██║██╔████╔██║███████║██║██╔██╗ ██║\n██║     ██║  ██║██║   ██║██║╚██╔╝██║██╔══██║██║██║╚██╗██║\n╚██████╗██████╔╝╚██████╔╝██║ ╚═╝ ██║██║  ██║██║██║ ╚████║\n ╚═════╝╚═════╝  ╚═════╝ ╚═╝     ╚═╝╚═╝  ╚═╝╚═╝╚═╝  ╚═══╝\n\t\t\t\t\t\t\tv0.0.4\n\t\thttps://github.com/canc3s/cDomain\n\n[INFO] 正在查询关键字 字节跳动\n[Warning] find IP : 114.246.10.65\n[Warning] find IP : 119.167.189.11\ndgo.ink\nmyzijie.com\ntoutiaocloud.net\ntoutiaocloud.com\ntoutiaocloud.cn\nbytedance.cn\nbytedance.org\nbytedance.com\nbytedance.net\nzjtdchina.cn\nbytecdn.cn\nbytedns.net\nbytedance.tj.cn\nshuiwu360.com\nbyteimg.com\nbytefcdn.com\nvideo518.com\njinritoutiao.js.cn\neat9.cn\ncdndns1.com\ncdndns2.com\nbytedns2.com\nbytedns1.com\nsyzjtd.com\nmykailu.com\nhzzqw.cn\nyxlgzs.cn\nnmklw.com\nshangtout.com\n```\n\n## 其他\n\n软件难免有一些问题，假如大家发现，欢迎大家提意见或者建议。\n\n还有一个工具 `cSubsidiary` 我一般两个一起使用，参考[文章](https://canc3s.github.io/2021/03/01/cSubsidiary和cDomain使用指南/)\n\n## Changelog\n\n* 增加请求延迟功能，防止触发反爬虫（-delay 默认为0，不开启）\n"
  },
  {
    "path": "cmd/cDomain/README.md",
    "content": "# cDomain\n 利用天眼查查询企业备案\n\n[下载地址](https://github.com/canc3s/cDomain/releases)\n\n## 介绍\n\n可以通过两种方式查询自己想要的企业子公司\n\n1. `-n` 参数：利用（http://beian.tianyancha.com）接口给出的关键字先进行查询。（方便，但会搜索结果受关键字影响，假如会出现一些公司名类同的公司的备案）\n\n   ![image-20210301143501471](https://cdn.jsdelivr.net/gh/canc3s/picBed/img/2021/ee1775ac6ca1ba9bd4d126596b2e4707083.png)\n\n2. `-i` 参数：利用给出的公司id对该公司进行查询。（准确，结果唯一，但需要自己先去查找一级公司，该接口目前没有限制不需要 `cookie`）\n\n3. `-f` 参数：对文件里的所有关键字和id进行查询。因为我比较推荐用id查询，而且为了方便多次递归查询，读文件时会先去该行尝试匹配是否存在公司id，假如不存在就把该行作为关键字进行查询。因此查询可以直接把`cSubsidiary`的结果文件当作`cDomain`的输入文件。\n\n4. 因为天眼查风控比较严格，所以使用时会出现几种情况。一、因为某个ip一段时间内查询次数过多，所以查询时会自动跳到登陆界面，这种情况需要使用一个手机号进行登陆，然后增加cookie去继续查询。二、海外ip或者云服务器ip访问天眼查会显示海外用户，所以最好使用正常的出口ip进行查询。三、假如短时间呢使用很多很多次查询的话，天眼查会有人机判断的验证码，需要手动打开天眼查网站进行一下人机验证。（情况较少）\n\n## 用法\n\n```\nadmin@admin cSubsidiary % go run cDomain.go -h\n\n\n ██████╗██████╗  ██████╗ ███╗   ███╗ █████╗ ██╗███╗   ██╗\n██╔════╝██╔══██╗██╔═══██╗████╗ ████║██╔══██╗██║████╗  ██║\n██║     ██║  ██║██║   ██║██╔████╔██║███████║██║██╔██╗ ██║\n██║     ██║  ██║██║   ██║██║╚██╔╝██║██╔══██║██║██║╚██╗██║\n╚██████╗██████╔╝╚██████╔╝██║ ╚═╝ ██║██║  ██║██║██║ ╚████║\n ╚═════╝╚═════╝  ╚═════╝ ╚═╝     ╚═╝╚═╝  ╚═╝╚═╝╚═╝  ╚═══╝\n\t\t\t\t\t\t\tv0.0.4\n\t\thttps://github.com/canc3s/cDomain\n\nUsage of cDomain:\n  -c string\n    \t天眼查的Cookie\n  -f string\n    \t包含公司ID号码的文件\n  -i string\n    \t公司ID号码\n  -n string\n    \t公司名称\n  -no-color\n    \tNo Color\n  -o string\n    \t结果输出的文件(可选)\n  -silent\n    \tSilent mode\n  -timeout int\n    \t连接超时时间 (default 15)\n  -verbose\n    \t详细模式\n  -version\n    \t显示软件版本号\n```\n\n查询子公司\n\n```\nadmin@admin cSubsidiary % go run cSubsidiary.go -n 字节跳动\n\n\n ██████╗██████╗  ██████╗ ███╗   ███╗ █████╗ ██╗███╗   ██╗\n██╔════╝██╔══██╗██╔═══██╗████╗ ████║██╔══██╗██║████╗  ██║\n██║     ██║  ██║██║   ██║██╔████╔██║███████║██║██╔██╗ ██║\n██║     ██║  ██║██║   ██║██║╚██╔╝██║██╔══██║██║██║╚██╗██║\n╚██████╗██████╔╝╚██████╔╝██║ ╚═╝ ██║██║  ██║██║██║ ╚████║\n ╚═════╝╚═════╝  ╚═════╝ ╚═╝     ╚═╝╚═╝  ╚═╝╚═╝╚═╝  ╚═══╝\n\t\t\t\t\t\t\tv0.0.4\n\t\thttps://github.com/canc3s/cDomain\n\n[INFO] 正在查询关键字 字节跳动\n[Warning] find IP : 114.246.10.65\n[Warning] find IP : 119.167.189.11\ndgo.ink\nmyzijie.com\ntoutiaocloud.net\ntoutiaocloud.com\ntoutiaocloud.cn\nbytedance.cn\nbytedance.org\nbytedance.com\nbytedance.net\nzjtdchina.cn\nbytecdn.cn\nbytedns.net\nbytedance.tj.cn\nshuiwu360.com\nbyteimg.com\nbytefcdn.com\nvideo518.com\njinritoutiao.js.cn\neat9.cn\ncdndns1.com\ncdndns2.com\nbytedns2.com\nbytedns1.com\nsyzjtd.com\nmykailu.com\nhzzqw.cn\nyxlgzs.cn\nnmklw.com\nshangtout.com\n```\n\n## 其他\n\n软件难免有一些问题，假如大家发现，欢迎大家提意见或者建议。\n\n还有一个工具 `cSubsidiary` 我一般两个一起使用，我后面写个文章，详细写一下\n\n"
  },
  {
    "path": "cmd/cDomain/cDomain.go",
    "content": "package main\n\nimport (\n\t\"github.com/canc3s/cDomain/internal/runner\"\n)\n\nfunc main() {\n\toptions := runner.ParseOptions()\n\n\trunner.RunEnumeration(options)\n}\n\n"
  },
  {
    "path": "go.mod",
    "content": "module github.com/canc3s/cDomain\n\ngo 1.15\n\nrequire (\n\tgithub.com/antchfx/htmlquery v1.2.3\n\tgithub.com/logrusorgru/aurora v2.0.3+incompatible\n\tgithub.com/mattn/go-colorable v0.1.8\n\tgolang.org/x/net v0.0.0-20210226172049-e18ecbb05110\n)\n"
  },
  {
    "path": "go.sum",
    "content": "github.com/antchfx/htmlquery v1.2.3 h1:sP3NFDneHx2stfNXCKbhHFo8XgNjCACnU/4AO5gWz6M=\ngithub.com/antchfx/htmlquery v1.2.3/go.mod h1:B0ABL+F5irhhMWg54ymEZinzMSi0Kt3I2if0BLYa3V0=\ngithub.com/antchfx/xpath v1.1.6 h1:6sVh6hB5T6phw1pFpHRQ+C4bd8sNI+O58flqtg7h0R0=\ngithub.com/antchfx/xpath v1.1.6/go.mod h1:Yee4kTMuNiPYJ7nSNorELQMr1J33uOpXDMByNYhvtNk=\ngithub.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e h1:1r7pUrabqp18hOBcwBwiTsbnFeTZHV9eER/QT5JVZxY=\ngithub.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=\ngithub.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczGlG91VSDkswnjF5A8=\ngithub.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=\ngithub.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8=\ngithub.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=\ngithub.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=\ngithub.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=\ngolang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\ngolang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=\ngolang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20200421231249-e086a090c8fd/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=\ngolang.org/x/net v0.0.0-20201110031124-69a78807bb2b h1:uwuIcX0g4Yl1NC5XAz37xsr2lTtcqevgzYNVt49waME=\ngolang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=\ngolang.org/x/net v0.0.0-20210226172049-e18ecbb05110 h1:qWPm9rbaAMKs8Bq/9LRpbMqxWRVUAQwMI9fVrssnTfw=\ngolang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=\ngolang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200930185726-fdedc70b468f h1:+Nyd8tzPX9R7BWHguqsrbFdRx3WQ/1ib8I44HXV5yTA=\ngolang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20201119102817-f84b799fce68 h1:nxC68pudNYkKU6jWhgrqdreuFiOQWj1Fs7T3VrH4Pjw=\ngolang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=\ngolang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\n"
  },
  {
    "path": "internal/fileutil/doc.go",
    "content": "// Package fileutil contains all the funcionality related to deal with files\npackage fileutil\n"
  },
  {
    "path": "internal/fileutil/fileutil.go",
    "content": "package fileutil\n\nimport (\n\t\"bufio\"\n\t\"io\"\n\t\"os\"\n)\n\n// FileExists checks if a file exists and is not a directory\nfunc FileExists(filename string) bool {\n\tinfo, err := os.Stat(filename)\n\tif os.IsNotExist(err) {\n\t\treturn false\n\t}\n\treturn !info.IsDir()\n}\n\n// FolderExists checks if a folder exists\nfunc FolderExists(folderpath string) bool {\n\t_, err := os.Stat(folderpath)\n\treturn !os.IsNotExist(err)\n}\n\n// HasStdin determines if the user has piped input\nfunc HasStdin() bool {\n\tstat, err := os.Stdin.Stat()\n\tif err != nil {\n\t\treturn false\n\t}\n\n\tmode := stat.Mode()\n\n\tisPipedFromChrDev := (mode & os.ModeCharDevice) == 0\n\tisPipedFromFIFO := (mode & os.ModeNamedPipe) != 0\n\n\treturn isPipedFromChrDev || isPipedFromFIFO\n}\n\nfunc ReadImf(input io.Reader) []string {\n\tvar imf []string\n\tscanner := bufio.NewScanner(input)\n\tfor scanner.Scan() {\n\t\timf = append(imf,scanner.Text())\n\t}\n\treturn imf\n}"
  },
  {
    "path": "internal/filters/filters.go",
    "content": "package filters\n\nimport \"regexp\"\n\ntype Result struct {\n\tDomains \t[]string\n\tIps\t\t\t[]string\n}\n\nfunc FilterIP(domains []string) Result {\n\tvar results Result\n\tfor _,domain := range domains {\n\t\tre := regexp.MustCompile(`((2[0-4]\\d|25[0-5]|[01]?\\d\\d?)\\.){3}(2[0-4]\\d|25[0-5]|[01]?\\d\\d?)`)\n\t\tif re.MatchString(domain) {\n\t\t\tresults.Ips  = append(results.Ips , domain)\n\t\t} else {\n\t\t\tresults.Domains  = append(results.Domains , domain)\n\t\t}\n\t}\n\treturn results\n}"
  },
  {
    "path": "internal/gologger/doc.go",
    "content": "// Package gologger contains all the funcionality\npackage gologger\n"
  },
  {
    "path": "internal/gologger/gologger.go",
    "content": "package gologger\n\nimport (\n\t\"fmt\"\n\t\"github.com/logrusorgru/aurora\"\n\t\"github.com/mattn/go-colorable\"\n\t\"os\"\n\t\"strings\"\n\t\"sync\"\n)\n\n// Level defines all the available levels we can log at\ntype Level int\n\n// Available logging levels\nconst (\n\tNull Level = iota\n\tFatal\n\tSilent\n\tLabel\n\tMisc\n\tError\n\tWarning\n\tInfo\n\tDebug\n\tVerbose\n)\n\nvar (\n\t// UseColors can be used to control coloring of the output\n\tUseColors = true\n\t// MaxLevel is the maximum level to log at. By default, logging\n\t// is done at Info level. Using verbose will display all the errors too,\n\t// Using silent will display only the most relevant information.\n\tMaxLevel = Info\n\n\tlabels = map[Level]string{\n\t\tWarning: \"Warning\",\n\t\tError:   \"Error\",\n\t\tLabel:   \"WRN\",\n\t\tFatal:   \"Fatal\",\n\t\tDebug:   \"DEBUG\",\n\t\tInfo:    \"INFO\",\n\t}\n\n\tmutex  = &sync.Mutex{}\n\toutput = colorable.NewColorableStdout()\n)\n\nvar stringBuilderPool = &sync.Pool{New: func() interface{} {\n\treturn new(strings.Builder)\n}}\n\n// wrap wraps a given label for a message to a logg-able representation.\n// It checks if colors are specified and what level we are logging at.\nfunc wrap(label string, level Level) string {\n\t// Check if we are not using colors, if not, return\n\tif !UseColors {\n\t\treturn label\n\t}\n\n\tswitch level {\n\tcase Silent:\n\t\treturn label\n\tcase Info, Verbose:\n\t\treturn aurora.Blue(label).String()\n\tcase Fatal:\n\t\treturn aurora.Bold(aurora.Red(label)).String()\n\tcase Error:\n\t\treturn aurora.Red(label).String()\n\tcase Debug:\n\t\treturn aurora.Magenta(label).String()\n\tcase Warning, Label:\n\t\treturn aurora.Yellow(label).String()\n\tdefault:\n\t\treturn label\n\t}\n}\n\n// getLabel generates a label for a given message, depending on the level\n// and the label passed.\nfunc getLabel(level Level, label string, sb *strings.Builder) {\n\tswitch level {\n\tcase Silent, Misc:\n\t\treturn\n\tcase Error, Fatal, Info, Warning, Debug, Label:\n\t\tsb.WriteString(\"[\")\n\t\tsb.WriteString(wrap(labels[level], level))\n\t\tsb.WriteString(\"]\")\n\t\tsb.WriteString(\" \")\n\t\treturn\n\tcase Verbose:\n\t\tsb.WriteString(\"[\")\n\t\tsb.WriteString(wrap(label, level))\n\t\tsb.WriteString(\"]\")\n\t\tsb.WriteString(\" \")\n\t\treturn\n\tdefault:\n\t\treturn\n\t}\n}\n\n// log logs the actual message to the screen\nfunc log(level Level, label string, format string, args ...interface{}) {\n\t// Don't log if the level is null\n\tif level == Null {\n\t\treturn\n\t}\n\n\tif level <= MaxLevel {\n\t\t// Build the log message using the string builder pool\n\t\tsb := stringBuilderPool.Get().(*strings.Builder)\n\n\t\t// Get the label and append it to string builder\n\t\tgetLabel(level, label, sb)\n\n\t\tmessage := fmt.Sprintf(format, args...)\n\t\tsb.WriteString(message)\n\n\t\t//if strings.HasSuffix(message, \"\\n\") == false {\n\t\t//\tsb.WriteString(\"\\n\")\n\t\t//}\n\n\t\tmutex.Lock()\n\t\tswitch level {\n\t\tcase Silent:\n\t\t\tfmt.Fprint(os.Stdout, sb.String())\n\t\tdefault:\n\t\t\tfmt.Fprint(output, sb.String())\n\t\t}\n\t\tmutex.Unlock()\n\n\t\tsb.Reset()\n\t\tstringBuilderPool.Put(sb)\n\t}\n}\n\n// Infof writes a info message on the screen with the default label\nfunc Infof(format string, args ...interface{}) {\n\tlog(Info, \"\", format, args...)\n}\n\n// Warningf writes a warning message on the screen with the default label\nfunc Warningf(format string, args ...interface{}) {\n\tlog(Warning, \"\", format, args...)\n}\n\n// Errorf writes an error message on the screen with the default label\nfunc Errorf(format string, args ...interface{}) {\n\tlog(Error, \"\", format, args...)\n}\n\n// Debugf writes an error message on the screen with the default label\nfunc Debugf(format string, args ...interface{}) {\n\tlog(Debug, \"\", format, args...)\n}\n\n// Verbosef writes a verbose message on the screen with a tabel\nfunc Verbosef(format string, label string, args ...interface{}) {\n\tlog(Verbose, label, format, args...)\n}\n\n// Silentf writes a message on the stdout with no label\nfunc Silentf(format string, args ...interface{}) {\n\tlog(Silent, \"\", format, args...)\n}\n\n// Fatalf exits the program if we encounter a fatal error\nfunc Fatalf(format string, args ...interface{}) {\n\tlog(Fatal, \"\", format, args...)\n\tos.Exit(1)\n}\n\n// Printf prints a string on screen without any extra stuff\nfunc Printf(format string, args ...interface{}) {\n\tlog(Misc, \"\", format, args...)\n}\n\n// Labelf prints a string on screen with a label interface\nfunc Labelf(format string, args ...interface{}) {\n\tlog(Label, \"\", format, args...)\n}"
  },
  {
    "path": "internal/requests/options.go",
    "content": "package requests\n\nimport (\n\t\"crypto/tls\"\n\t\"golang.org/x/net/html\"\n\t\"net\"\n\t\"net/http\"\n\t\"time\"\n)\n\ntype Request struct {\n\tUrl \t\tstring\n\tCookie \t\tstring\n}\n\ntype Response struct {\n\tBody \t\t[]byte\n\tPage \t\t*html.Node\n}\n\nfunc DefaultTransport() *http.Transport {\n\ttransport := &http.Transport{\n\t\tProxy: http.ProxyFromEnvironment,\n\t\tDialContext: (&net.Dialer{\n\t\t\tTimeout:   30 * time.Second,\n\t\t\tKeepAlive: 30 * time.Second,\n\t\t}).DialContext,\n\t\tMaxIdleConnsPerHost: -1,\n\t\tTLSClientConfig: &tls.Config{\n\t\t\tInsecureSkipVerify: true,\n\t\t},\n\t\tDisableKeepAlives: true,\n\t}\n\treturn transport\n}\n"
  },
  {
    "path": "internal/runner/options.go",
    "content": "package runner\n\nimport (\n\t\"flag\"\n\t\"github.com/canc3s/cDomain/internal/fileutil\"\n\t\"github.com/canc3s/cDomain/internal/gologger\"\n\t\"os\"\n)\n\nconst banner = `\n\n ██████╗██████╗  ██████╗ ███╗   ███╗ █████╗ ██╗███╗   ██╗\n██╔════╝██╔══██╗██╔═══██╗████╗ ████║██╔══██╗██║████╗  ██║\n██║     ██║  ██║██║   ██║██╔████╔██║███████║██║██╔██╗ ██║\n██║     ██║  ██║██║   ██║██║╚██╔╝██║██╔══██║██║██║╚██╗██║\n╚██████╗██████╔╝╚██████╔╝██║ ╚═╝ ██║██║  ██║██║██║ ╚████║\n ╚═════╝╚═════╝  ╚═════╝ ╚═╝     ╚═╝╚═╝  ╚═╝╚═╝╚═╝  ╚═══╝\n\t\t\t\t\t\t\tv`\n\n// Version is the current version of C\nconst Version = `0.0.5`\n\n\ntype Options struct {\n\tKeyWord\t\t\tstring\n\tCompanyID\t\t\tstring                 // Target is a single URL/Domain to scan usng a template\n\tInputFile\t\t\tstring                 // Targets specifies the targets to scan using templates.\n\tCookie\t\t\t\tstring\n\tDelay\t\t\t\tint\n\tTimeout \t\t\tint\n\tOutput              string                 // Output is the file to write found subdomains to.\n\tSilent\t\t\t\tbool\n\tNoColor\t\t\t\tbool\n\tVerbose\t\t\t\tbool\n\tVersion             bool                   // Version specifies if we should just show version and exit\n}\n\nfunc ParseOptions() *Options {\n\toptions := &Options{}\n\n\tflag.StringVar(&options.KeyWord, \"n\", \"\", \"公司名称\")\n\tflag.StringVar(&options.CompanyID, \"i\", \"\", \"公司ID号码\")\n\tflag.StringVar(&options.InputFile, \"f\", \"\", \"包含公司ID号码的文件\")\n\tflag.StringVar(&options.Cookie, \"c\", \"\", \"天眼查的Cookie\")\n\tflag.IntVar(&options.Timeout, \"timeout\", 15, \"连接超时时间(秒)\")\n\tflag.IntVar(&options.Delay, \"delay\", 0, \"请求之间的延迟时间(秒)\")\n\tflag.StringVar(&options.Output, \"o\", \"\", \"结果输出的文件(可选)\")\n\tflag.BoolVar(&options.Silent, \"silent\", false, \"Silent mode\")\n\tflag.BoolVar(&options.NoColor, \"no-color\", false, \"No Color\")\n\tflag.BoolVar(&options.Verbose, \"verbose\", false, \"详细模式\")\n\tflag.BoolVar(&options.Version, \"version\", false, \"显示软件版本号\")\n\n\tflag.Parse()\n\n\toptions.configureOutput()\n\n\tshowBanner()\n\n\tif options.Version {\n\t\tgologger.Infof(\"Current Version: %s\\n\", Version)\n\t\tos.Exit(0)\n\t}\n\n\toptions.validateOptions()\n\n\treturn options\n}\n\nfunc (options *Options) validateOptions() {\n\tif options.CompanyID != \"\" && len(options.CompanyID) < 5 {\n\t\tgologger.Fatalf(\"公司ID %s 不正确!\\n\", options.CompanyID)\n\t}\n\tif options.InputFile != \"\" && !fileutil.FileExists(options.InputFile) {\n\t\tgologger.Fatalf(\"文件 %s 不存在!\\n\", options.InputFile)\n\t}\n\tif options.KeyWord == \"\" && options.CompanyID == \"\" && options.InputFile == \"\" {\n\t\tflag.PrintDefaults()\n\t\tos.Exit(0)\n\t}\n}\n\n\n// showBanner is used to show the banner to the user\nfunc showBanner() {\n\tgologger.Printf(\"%s%s\\n\", banner,Version)\n\tgologger.Printf(\"\\t\\thttps://github.com/canc3s/cDomain\\n\\n\")\n\n\t//gologger.Labelf(\"请谨慎使用,您应对自己的行为负责\\n\")\n\t//gologger.Labelf(\"开发人员不承担任何责任，也不对任何滥用或损坏负责.\\n\")\n}\n\nfunc (options *Options) configureOutput() {\n\t// If the user desires verbose output, show verbose output\n\tif options.Verbose {\n\t\tgologger.MaxLevel = gologger.Verbose\n\t}\n\tif options.NoColor {\n\t\tgologger.UseColors = false\n\t}\n\tif options.Silent {\n\t\tgologger.MaxLevel = gologger.Silent\n\t}\n}\n"
  },
  {
    "path": "internal/runner/request.go",
    "content": "package runner\n\nimport (\n\t\"github.com/antchfx/htmlquery\"\n\t\"github.com/canc3s/cDomain/internal/gologger\"\n\t\"github.com/canc3s/cDomain/internal/requests\"\n\t\"golang.org/x/net/html\"\n\t\"io/ioutil\"\n\t\"net/http\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n)\n\nfunc GetPage(url string, options *Options) requests.Response {\n\n\ttime.Sleep(time.Duration(options.Delay) * time.Second)\n\tvar transport = requests.DefaultTransport()\n\tvar client = &http.Client{\n\t\tTransport: transport,\n\t\t//Timeout:       time.Duration(options.Timeout),\n\t\tCheckRedirect: func(req *http.Request, via []*http.Request) error {\n\t\t\treturn http.ErrUseLastResponse /* 不进入重定向 */\n\t\t},\n\t}\n\treq, _ := http.NewRequest(\"GET\", url, nil)\n\treq.Header.Set(\"User-Agent\", \"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5026.0 Safari/537.36 Edg/103.0.1254.0\")\n\tif options.Cookie != \"\" {\n\t\treq.Header.Set(\"Cookie\", options.Cookie)\n\t}\n\tresp, err := client.Do(req)\n\tif err != nil {\n\t\tgologger.Fatalf(\"请求发生错误，请检查网络连接\\n%s\\n\", err)\n\t}\n\n\tif resp.StatusCode == 403 {\n\t\tgologger.Fatalf(\"海外用户或者云服务器ip被禁止访问网站，请更换ip\\n\")\n\t} else if resp.StatusCode == 401 {\n\t\tgologger.Fatalf(\"天眼查Cookie有问题或过期，请重新获取\\n\")\n\t} else if resp.StatusCode == 302 {\n\t\tgologger.Fatalf(\"天眼查免费查询次数已用光，需要加Cookie\\n\")\n\t}\n\tbody, _ := ioutil.ReadAll(resp.Body)\n\tpage,_ := htmlquery.Parse(strings.NewReader(string(body)))\n\n\treturn requests.Response{\n\t\tBody: body,\n\t\tPage:page,\n\t}\n}\n\nfunc JudgePagesK(page *html.Node) int {\n\tlist := htmlquery.Find(page, \"/html/body/div[2]/div/div[2]/div[1]/div[2]/div[3]/ul/li/a\")\n\tnum := 1\n\tif len(list) > 2 {\n\t\tvar err error\n\t\tpages := htmlquery.InnerText(list[len(list)-2])\n\t\tnum,err = strconv.Atoi(strings.Trim(pages, \".\"))\n\t\tif err != nil {\n\t\t\tnum = 1\n\t\t}\n\t}\n\treturn num\n}\n\nfunc JudgePagesI(page *html.Node) int {\n\tlist := htmlquery.Find(page, \"/html/body/div/ul/li/a\")\n\treturn len(list) - 1\n}\n\nfunc EnuDomainByKey(page *html.Node, domains *[]string) {\n\tlist := htmlquery.Find(page, \"/html/body/div[2]/div/div[2]/div[1]/div[2]/div[2]/table/tbody/tr/td[5]/span\")\n\tfor _,node  := range list {\n\t\tdomain := htmlquery.InnerText(node)\n\t\t*domains = append(*domains, domain)\n\t}\n}\n\nfunc GetInformation(page *html.Node) []string {\n\tlist := htmlquery.Find(page, \"/html/body/table/tbody/tr/td[5]\")\n\tvar domains []string\n\tfor _,node := range list {\n\t\tdomain := htmlquery.InnerText(node)\n\t\tdomains = append(domains, domain)\n\t}\n\treturn domains\n}\n\n\nfunc GetDomain(options *Options) []string {\n\tresp := GetPage(\"https://www.tianyancha.com/pagination/icp.xhtml?ps=30&isAjaxLoad=true&pn=1&id=\"+options.CompanyID, options)\n\tpage := JudgePagesI(resp.Page)\n\tdomains := GetInformation(resp.Page)\n\tfor i := 2; i <= page; i++ {\n\t\tresp := GetPage(\"https://www.tianyancha.com/pagination/icp.xhtml?ps=30&isAjaxLoad=true&pn=\"+strconv.Itoa(i)+\"&id=\"+options.CompanyID, options)\n\t\tdomains = append(domains,GetInformation(resp.Page)...)\n\t}\n\treturn domains\n}\n"
  },
  {
    "path": "internal/runner/runner.go",
    "content": "package runner\n\nimport (\n\t\"fmt\"\n\t\"github.com/canc3s/cDomain/internal/fileutil\"\n\t\"github.com/canc3s/cDomain/internal/filters\"\n\t\"github.com/canc3s/cDomain/internal/gologger\"\n\t\"net/url\"\n\t\"os\"\n\t\"regexp\"\n\t\"strconv\"\n)\n\ntype Targets struct {\n\tID\t\t\t[]string\n\tName\t\t[]string\n}\n\nfunc GetDomainByKey(options *Options) []string {\n\tvar domains []string\n\tgologger.Infof(\"正在查询关键字 %s\\n\",options.KeyWord)\n\turl := \"https://beian.tianyancha.com/search/\"+ url.QueryEscape(options.KeyWord)\n\tresp := GetPage(url, options)\n\tEnuDomainByKey(resp.Page, &domains)\n\tnum := JudgePagesK(resp.Page)\n\tif num > 5 && options.Cookie == \"\" {\n\t\tgologger.Errorf(\"域名过多，需要cookie才能完整查询\\n\")\n\t\tnum = 5\n\t}\n\tif num > 1 {\n\t\tfor i := 2; i <= num; i++ {\n\t\t\tresp := GetPage(url+\"/p\"+strconv.Itoa(num), options)\n\t\t\tEnuDomainByKey(resp.Page, &domains)\n\t\t}\n\t}\n\treturn domains\n}\n\nfunc GetDomainByID(options *Options) []string {\n\tdomains := GetDomain(options)\n\treturn domains\n}\n\nfunc RunEnumeration(options *Options) {\n\tvar domains []string\n\tif options.InputFile != \"\" {\n\t\tfin, error := os.OpenFile(options.InputFile, os.O_RDONLY, 0)\n\t\tif error != nil {\n\t\t\tgologger.Fatalf(\"文件读取失败：%s\",error)\n\t\t}\n\t\tdefer fin.Close()\n\t\timf := fileutil.ReadImf(fin)\n\t\ttargets := TransImf(imf)\n\t\tfor _,id := range targets.ID {\n\t\t\toptions.CompanyID = id\n\t\t\tdomains = append(domains,GetDomainByID(options)...)\n\t\t}\n\t\tfor _,name := range targets.Name {\n\t\t\toptions.KeyWord = name\n\t\t\tdomains = append(domains,GetDomainByKey(options)...)\n\t\t}\n\t} else {\n\t\tif options.KeyWord != \"\" && options.CompanyID == \"\" {\n\t\t\tdomains = GetDomainByKey(options)\n\t\t} else if options.CompanyID != \"\" {\n\t\t\tdomains = GetDomainByID(options)\n\t\t}\n\t}\n\n\tresults := filters.FilterIP(domains)\n\tfor _,ip := range results.Ips {\n\t\tgologger.Warningf(\"find IP : %s\\n\",ip)\n\t}\n\tfor _,domain := range results.Domains {\n\t\tfmt.Println(domain)\n\t}\n\tif options.Output != \"\" {\n\t\tfile, err := os.OpenFile(options.Output, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)\n\t\tif err != nil {\n\t\t\tgologger.Fatalf(\"结果无法写入文件：%s\", err)\n\t\t}\n\t\tdefer file.Close()\n\n\t\tfor _,domain := range results.Domains {\n\t\t\tfile.WriteString(domain+\"\\n\")\n\t\t}\n\t}\n}\n\nfunc TransImf(imf []string) Targets {\n\tvar targets Targets\n\tfor _,i := range imf {\n\t\tre := regexp.MustCompile(`(\\d{5,11})`)\n\t\tbuf := re.FindStringSubmatch(i)\n\t\tif buf == nil {\n\t\t\ttargets.Name = append(targets.Name, i)\n\t\t}else{\n\t\t\ttargets.ID = append(targets.ID, buf[0])\n\t\t}\n\t}\n\treturn targets\n}"
  }
]