Initial commit
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
# Build outputs
|
||||
bin/
|
||||
|
||||
# IDE files
|
||||
.idea/
|
||||
+133
@@ -0,0 +1,133 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var startPattern = "DNSHelper <<-> START CONFIG"
|
||||
var endPattern = "DNSHelper <->> END CONFIG"
|
||||
|
||||
func extractLinesToEdit(data *workingData) error {
|
||||
startIndex := 0
|
||||
endIndex := 0
|
||||
for i, line := range data.DefaultContent {
|
||||
if strings.Contains(line, startPattern) {
|
||||
startIndex = i
|
||||
continue
|
||||
}
|
||||
if strings.Contains(line, endPattern) {
|
||||
endIndex = i
|
||||
break
|
||||
}
|
||||
}
|
||||
var fileModified bool
|
||||
var fileCorrupted bool
|
||||
switch {
|
||||
case startIndex == 0 && endIndex == 0:
|
||||
fileModified = false
|
||||
fileCorrupted = false
|
||||
case startIndex > 0 && endIndex > startIndex:
|
||||
fileModified = true
|
||||
fileCorrupted = false
|
||||
default:
|
||||
fileModified = true
|
||||
fileCorrupted = true
|
||||
}
|
||||
|
||||
if fileCorrupted == true {
|
||||
return errors.New("content has been corrupted")
|
||||
}
|
||||
|
||||
if fileModified == true {
|
||||
data.PrefixContent = data.DefaultContent[:startIndex]
|
||||
data.PostfixContent = data.DefaultContent[endIndex+1:]
|
||||
data.ExistingContent = data.DefaultContent[startIndex+1 : endIndex]
|
||||
} else {
|
||||
data.PrefixContent = data.DefaultContent
|
||||
data.PostfixContent = make([]string, 0)
|
||||
data.ExistingContent = make([]string, 0)
|
||||
}
|
||||
data.NewContent = make([]string, 0)
|
||||
//fmt.Println("PrefixContent:")
|
||||
//for _, line := range data.PrefixContent {
|
||||
// fmt.Println(line)
|
||||
//}
|
||||
//fmt.Println("ExistingContent:")
|
||||
//for _, line := range data.ExistingContent {
|
||||
// fmt.Println(line)
|
||||
//}
|
||||
//fmt.Println("PostfixContent:")
|
||||
//for _, line := range data.PostfixContent {
|
||||
// fmt.Println(line)
|
||||
//}
|
||||
return nil
|
||||
}
|
||||
|
||||
func removeHostsFromExistingContent(data *workingData) {
|
||||
var updatedContent []string
|
||||
for _, host := range data.Hosts {
|
||||
for _, line := range data.ExistingContent {
|
||||
if !strings.Contains(line, host) {
|
||||
updatedContent = append(updatedContent, line)
|
||||
}
|
||||
}
|
||||
}
|
||||
data.NewContent = updatedContent
|
||||
}
|
||||
|
||||
func addHostsToWorkingData(data *workingData, hosts *string) {
|
||||
hostToAddList := strings.Split(*hosts, ",")
|
||||
var newHosts []string
|
||||
for _, host := range hostToAddList {
|
||||
newHosts = append(newHosts, host)
|
||||
}
|
||||
data.Hosts = newHosts
|
||||
}
|
||||
|
||||
func addResolversToWorkingData(data *workingData, resolvers *string) {
|
||||
resolverToAddList := strings.Split(*resolvers, ",")
|
||||
var newResolvers []string
|
||||
for _, resolver := range resolverToAddList {
|
||||
newResolvers = append(newResolvers, resolver)
|
||||
}
|
||||
data.Resolvers = newResolvers
|
||||
}
|
||||
|
||||
func addNameToIPMappingsToNewContent(data *workingData) {
|
||||
for _, host := range data.Hosts {
|
||||
for _, resolver := range data.Resolvers {
|
||||
ips := lookupIP(host, resolver)
|
||||
if len(ips) > 0 {
|
||||
for _, ip := range ips {
|
||||
data.NewContent = append(data.NewContent, fmt.Sprintf("%s\t%s", ip, host))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func slicesEqual(a, b []string) bool {
|
||||
if len(a) != len(b) {
|
||||
return false
|
||||
}
|
||||
for i := range a {
|
||||
if a[i] != b[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func dedupeSlice(slice []string) []string {
|
||||
newSlice := make([]string, 0, len(slice))
|
||||
seen := make(map[string]bool)
|
||||
for _, value := range slice {
|
||||
if !seen[value] {
|
||||
seen[value] = true
|
||||
newSlice = append(newSlice, value)
|
||||
}
|
||||
}
|
||||
return newSlice
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
func lookupIP(hostname string, resolver string) []string {
|
||||
c := dns.Client{}
|
||||
m := dns.Msg{}
|
||||
m.SetQuestion(dns.Fqdn(hostname), dns.TypeA)
|
||||
r, _, err := c.Exchange(&m, resolver+":53")
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
if r.Rcode != dns.RcodeSuccess {
|
||||
fmt.Println("Error: querying " + resolver + " for A record of " + hostname + " failed with code " + dns.RcodeToString[r.Rcode])
|
||||
return nil
|
||||
}
|
||||
ips := make([]string, 0)
|
||||
for _, ans := range r.Answer {
|
||||
if rec, ok := ans.(*dns.A); ok {
|
||||
ips = append(ips, rec.A.String())
|
||||
}
|
||||
}
|
||||
|
||||
if len(ips) > 0 {
|
||||
return ips
|
||||
}
|
||||
|
||||
c = dns.Client{}
|
||||
m = dns.Msg{}
|
||||
m.SetQuestion(dns.Fqdn(hostname), dns.TypeCNAME)
|
||||
r, _, err = c.Exchange(&m, resolver+":53")
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
if r.Rcode != dns.RcodeSuccess {
|
||||
fmt.Println("Error: querying " + resolver + " for CNAME record of " + hostname + " failed with code " + dns.RcodeToString[r.Rcode])
|
||||
return nil
|
||||
}
|
||||
for _, ans := range r.Answer {
|
||||
if rec, ok := ans.(*dns.CNAME); ok {
|
||||
ips = lookupIP(rec.Target, resolver)
|
||||
for _, ip := range ips {
|
||||
ips = append(ips, ip)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(ips) > 0 {
|
||||
return ips
|
||||
}
|
||||
|
||||
fmt.Println("Error: no IP addresses found for " + hostname)
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"os"
|
||||
)
|
||||
|
||||
func fileExists(path string) (bool, error) {
|
||||
_, err := os.Stat(path)
|
||||
if err == nil {
|
||||
return true, nil
|
||||
}
|
||||
if os.IsNotExist(err) {
|
||||
return false, nil
|
||||
}
|
||||
return false, err
|
||||
}
|
||||
|
||||
func getFileContentLines(path string) ([]string, error) {
|
||||
file, err := os.Open(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer file.Close()
|
||||
var lines []string
|
||||
scanner := bufio.NewScanner(file)
|
||||
for scanner.Scan() {
|
||||
lines = append(lines, scanner.Text())
|
||||
}
|
||||
if err := scanner.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return lines, nil
|
||||
}
|
||||
|
||||
func writeOverContentInFile(path string, lines []string) error {
|
||||
file, err := os.OpenFile(path, os.O_WRONLY|os.O_TRUNC, 0644)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
writer := bufio.NewWriter(file)
|
||||
for _, line := range lines {
|
||||
_, err := writer.WriteString(line + "\n")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
err = writer.Flush()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func writeHostsFile(data *workingData) error {
|
||||
var newFileContent []string
|
||||
newFileContent = append(newFileContent, data.PrefixContent...)
|
||||
if len(data.NewContent) > 0 {
|
||||
data.NewContent = dedupeSlice(data.NewContent)
|
||||
newFileContent = append(newFileContent, "# "+startPattern)
|
||||
newFileContent = append(newFileContent, data.NewContent...)
|
||||
newFileContent = append(newFileContent, "# "+endPattern)
|
||||
}
|
||||
newFileContent = append(newFileContent, data.PostfixContent...)
|
||||
if slicesEqual(newFileContent, data.DefaultContent) == false {
|
||||
err := writeOverContentInFile(data.HostsFileLocation, newFileContent)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
//go:build darwin
|
||||
|
||||
package main
|
||||
|
||||
import "errors"
|
||||
|
||||
func getHostsFileLocation() (string, error) {
|
||||
var hostsFileLocation string
|
||||
hostsFileLocation = "/etc/hosts"
|
||||
exists, err := fileExists(hostsFileLocation)
|
||||
if err != nil {
|
||||
term(err)
|
||||
}
|
||||
if exists == false {
|
||||
term(errors.New("hosts file does not exist at " + hostsFileLocation))
|
||||
}
|
||||
return hostsFileLocation, nil
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
//go:build linux
|
||||
|
||||
package main
|
||||
|
||||
import "errors"
|
||||
|
||||
func getHostsFileLocation() (string, error) {
|
||||
var hostsFileLocation string
|
||||
hostsFileLocation = "/etc/hosts"
|
||||
exists, err := fileExists(hostsFileLocation)
|
||||
if err != nil {
|
||||
term(err)
|
||||
}
|
||||
if exists == false {
|
||||
term(errors.New("hosts file does not exist at " + hostsFileLocation))
|
||||
}
|
||||
return hostsFileLocation, nil
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
//go:build windows
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
)
|
||||
|
||||
func getHostsFileLocation() (string, error) {
|
||||
//return "hosts", nil
|
||||
var hostsFileLocation string
|
||||
value, envExists := os.LookupEnv("SystemRoot")
|
||||
if envExists {
|
||||
hostsFileLocation = value + "\\System32\\drivers\\etc\\hosts"
|
||||
} else {
|
||||
hostsFileLocation = "C:\\Windows\\System32\\drivers\\etc\\hosts"
|
||||
}
|
||||
exists, err := fileExists(hostsFileLocation)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if exists == false {
|
||||
return "", errors.New("file does not exist: " + hostsFileLocation)
|
||||
}
|
||||
return hostsFileLocation, nil
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
module dns-helper
|
||||
|
||||
go 1.18
|
||||
|
||||
require (
|
||||
github.com/miekg/dns v1.1.59 // indirect
|
||||
golang.org/x/mod v0.16.0 // indirect
|
||||
golang.org/x/net v0.22.0 // indirect
|
||||
golang.org/x/sys v0.18.0 // indirect
|
||||
golang.org/x/tools v0.19.0 // indirect
|
||||
)
|
||||
@@ -0,0 +1,10 @@
|
||||
github.com/miekg/dns v1.1.59 h1:C9EXc/UToRwKLhK5wKU/I4QVsBUc8kE6MkHBkeypWZs=
|
||||
github.com/miekg/dns v1.1.59/go.mod h1:nZpewl5p6IvctfgrckopVx2OlSEHPRO/U4SYkRklrEk=
|
||||
golang.org/x/mod v0.16.0 h1:QX4fJ0Rr5cPQCF7O9lh9Se4pmwfwskqZfq5moyldzic=
|
||||
golang.org/x/mod v0.16.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc=
|
||||
golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
|
||||
golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
|
||||
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/tools v0.19.0 h1:tfGCXNR1OsFG+sVdLAitlpjAvD/I6dHDKnYrpEZUHkw=
|
||||
golang.org/x/tools v0.19.0/go.mod h1:qoJWxmGSIBmAeriMx19ogtrEPrGtDbPK634QFIcLAhc=
|
||||
@@ -0,0 +1,94 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var fileName = filepath.Base(os.Args[0])
|
||||
|
||||
func init() {
|
||||
fmt.Println("DNSHelper v1.0")
|
||||
fmt.Println("Copyright (c) 2024 Emberkom LLC")
|
||||
fmt.Println("")
|
||||
if len(os.Args) < 2 {
|
||||
printUsage()
|
||||
defer os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func printUsage() {
|
||||
fmt.Println("This utility will update the local hosts file with DNS entries obtained by the specified DNS server.")
|
||||
fmt.Println("Usage: " + fileName + " [add|delete] -host hostname [-server dns.example.com]")
|
||||
fmt.Println("Example:")
|
||||
fmt.Println(" " + fileName + " add -host xyz.acme.com -server dns.example.com")
|
||||
fmt.Println(" This will use dns.example.com to find and add all IP addresses for xyz.acme.com to the local hosts file.")
|
||||
fmt.Println(" " + fileName + " delete -host hostname.example.com")
|
||||
fmt.Println(" This will delete all entries for hostname.example.com from the local hosts file.")
|
||||
fmt.Println(" " + fileName + " delete all")
|
||||
fmt.Println(" This will delete all entries from the local hosts file that were added by this utility.")
|
||||
fmt.Println("Note: Adding a hostname will first remove all entries in the hosts file that match the same hostname.")
|
||||
fmt.Println(" This utility will only remove entries from the hosts file that it added.")
|
||||
}
|
||||
|
||||
func term(err error) {
|
||||
fmt.Println("Error: " + err.Error())
|
||||
defer os.Exit(1)
|
||||
}
|
||||
|
||||
func main() {
|
||||
modePing := flag.NewFlagSet("ping", flag.ExitOnError)
|
||||
modeAdd := flag.NewFlagSet("add", flag.ExitOnError)
|
||||
hostToAdd := modeAdd.String("host", "", "Comma separated list of hostnames to add")
|
||||
dnsResolver := modeAdd.String("server", "", "DNS resolver to use")
|
||||
modeDelete := flag.NewFlagSet("delete", flag.ExitOnError)
|
||||
hostToDelete := modeDelete.String("host", "", "Comma separated list of hostnames to delete")
|
||||
|
||||
data := new(workingData)
|
||||
|
||||
var err error
|
||||
data.HostsFileLocation, err = getHostsFileLocation()
|
||||
if err != nil {
|
||||
term(err)
|
||||
}
|
||||
|
||||
data.DefaultContent, err = getFileContentLines(data.HostsFileLocation)
|
||||
if err != nil {
|
||||
term(err)
|
||||
}
|
||||
|
||||
if err = extractLinesToEdit(data); err != nil {
|
||||
term(err)
|
||||
}
|
||||
|
||||
switch strings.ToLower(os.Args[1]) {
|
||||
case "a", "add":
|
||||
err = modeAdd.Parse(os.Args[2:])
|
||||
if err != nil {
|
||||
term(err)
|
||||
}
|
||||
addHostsToWorkingData(data, hostToAdd)
|
||||
addResolversToWorkingData(data, dnsResolver)
|
||||
removeHostsFromExistingContent(data)
|
||||
addNameToIPMappingsToNewContent(data)
|
||||
case "d", "del", "delete":
|
||||
err = modeDelete.Parse(os.Args[2:])
|
||||
if err != nil {
|
||||
term(err)
|
||||
}
|
||||
if strings.ToLower(*hostToDelete) != "all" && strings.ToLower(*hostToDelete) != "a" {
|
||||
addHostsToWorkingData(data, hostToDelete)
|
||||
removeHostsFromExistingContent(data)
|
||||
}
|
||||
default:
|
||||
printUsage()
|
||||
}
|
||||
|
||||
err = writeHostsFile(data)
|
||||
if err != nil {
|
||||
term(err)
|
||||
}
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
package main
|
||||
|
||||
type workingData struct {
|
||||
HostsFileLocation string
|
||||
IsNew bool
|
||||
Hosts []string
|
||||
Resolvers []string
|
||||
DefaultContent []string
|
||||
PrefixContent []string
|
||||
NewContent []string
|
||||
ExistingContent []string
|
||||
PostfixContent []string
|
||||
}
|
||||
Reference in New Issue
Block a user