Compare commits
18 Commits
stable
...
experiment
| Author | SHA1 | Date |
|---|---|---|
|
|
f9284e63e9 | |
|
|
bc7bbce83a | |
|
|
5b2557701e | |
|
|
b6e1a08b5f | |
|
|
1e2de999d8 | |
|
|
abf51e24d5 | |
|
|
357528d0f4 | |
|
|
3564aea1fc | |
|
|
2ad62c43c0 | |
|
|
5551cab919 | |
|
|
2cb3b541b8 | |
|
|
abb66d2498 | |
|
|
d8a697d3f7 | |
|
|
08721b9289 | |
|
|
f0d459d437 | |
|
|
64c2a166cd | |
|
|
8930f00748 | |
|
|
011feaf557 |
|
|
@ -0,0 +1,148 @@
|
|||
package cfg
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"log"
|
||||
"os"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
const CFG = `
|
||||
# There are 3 types of entries: download, site, and whitelist. Downloads are
|
||||
# downloaded and stripped of comments and bad entries if possible before being
|
||||
# added to a list of sites. Whitelisted urls are removed from the list of sites.
|
||||
# From there all the urls are added to the hosts file for both IPv4 and IPv6.
|
||||
# You can also add comments by prepending with a '#'.
|
||||
|
||||
# This is a static entry
|
||||
#site=www.site.xyz
|
||||
# This is a download entry
|
||||
#download=w3.site.xyz/location/to/config.txt
|
||||
# This is a whitelist entry
|
||||
#whitelist=www.site.xyz
|
||||
|
||||
# A suggested download is: https://github.com/StevenBlack/hosts
|
||||
#download=https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts
|
||||
|
||||
# Webserver
|
||||
# This will allow you to intercept and alter messages
|
||||
|
||||
# It is disabled by default
|
||||
#Webserver=true`
|
||||
|
||||
type Config struct {
|
||||
CfgLoc string
|
||||
Sites []string
|
||||
Downloads []string
|
||||
Whitelist []string
|
||||
System struct {
|
||||
OS string
|
||||
TmpDir string
|
||||
HostsLoc string
|
||||
CfgLoc string
|
||||
Var string
|
||||
}
|
||||
WebServer struct {
|
||||
Enabled bool
|
||||
}
|
||||
}
|
||||
|
||||
// Used to hold a list of functions to be run when building
|
||||
var configFuncs []func(*Config)
|
||||
|
||||
// Create initialized a config to be used the entire session
|
||||
func Create() (cfg Config) {
|
||||
for _,fp := range(configFuncs){
|
||||
fp(&cfg)
|
||||
}
|
||||
if (cfg.System.OS == ""){log.Fatal("Failed to detect the OS")}
|
||||
err, cfg := cfg.Update()
|
||||
|
||||
if (err != nil){log.Fatal("Failed to handle the config file: " + err.Error())}
|
||||
return
|
||||
}
|
||||
|
||||
// cfgparse recieves the location of the config file and returns a list of sites to add and content to download
|
||||
func (cfg Config) Update() (error, Config) {
|
||||
// Opening the config file
|
||||
l := (cfg.System.CfgLoc + "rhosts.cfg")
|
||||
var err error = nil
|
||||
log.Print("Opening: ", l)
|
||||
if _, err = os.Stat(cfg.System.CfgLoc); os.IsNotExist(err) {
|
||||
log.Print(cfg.System.CfgLoc + " Does not exist, attempting to create it")
|
||||
err = os.MkdirAll(cfg.System.CfgLoc, 0755)
|
||||
if err != nil {
|
||||
log.Fatal("Could not create " + cfg.System.CfgLoc)
|
||||
}
|
||||
}
|
||||
|
||||
// Create one if it doesn't exist
|
||||
// This is done after so that it doesn't read the default one
|
||||
// in the event one doesn't exist
|
||||
if _, err = os.Stat(l); os.IsNotExist(err) {
|
||||
log.Print(l + " does not exist, attempting to create a placeholder")
|
||||
err = os.WriteFile(l, []byte(CFG), 0644)
|
||||
if err != nil {
|
||||
log.Fatal("Unable to create file: " + l)
|
||||
}
|
||||
}
|
||||
file, err := os.Open(l)
|
||||
defer file.Close()
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
return err, cfg
|
||||
}
|
||||
filebuf := bufio.NewScanner(file)
|
||||
filebuf.Split(bufio.ScanLines)
|
||||
for i , res := 0,filebuf.Scan(); res; res = filebuf.Scan() {
|
||||
i++
|
||||
buf := filebuf.Text()
|
||||
if (cfgparseline(buf, &cfg) == true){
|
||||
|
||||
log.Fatal("Failed to read line: " + strconv.Itoa(i) + ": " + buf)
|
||||
}
|
||||
}
|
||||
err = filebuf.Err()
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
return err, cfg
|
||||
}
|
||||
return err, cfg
|
||||
}
|
||||
|
||||
// cfgparseline reads a single line of the config and returns the type and content of the line
|
||||
func cfgparseline(buf string, cfg *Config) (fail bool) {
|
||||
if len(buf) == 0 {
|
||||
return
|
||||
}
|
||||
switch buf[0] {
|
||||
case ' ':
|
||||
case '#':
|
||||
return
|
||||
case 'd':
|
||||
if (len(buf) > 10 && buf[0:9] == "download=") {
|
||||
cfg.Downloads = append(cfg.Downloads, buf[9:])
|
||||
} else {
|
||||
fail = true
|
||||
}
|
||||
case 's':
|
||||
if (len(buf) > 6 && buf[0:5] == "site=") {
|
||||
cfg.Sites = append(cfg.Sites, buf[5:])
|
||||
} else {
|
||||
fail = true
|
||||
}
|
||||
case 'w':
|
||||
if (len(buf) > 10 && buf[0:10] == "whitelist=") {
|
||||
cfg.Whitelist = append(cfg.Whitelist, buf[9:])
|
||||
} else if (len(buf) > 10 && buf[0:10] == "webserver=") {
|
||||
if (len(buf[10:]) >= 4 && buf[10:] == "true"){
|
||||
cfg.WebServer.Enabled = true
|
||||
}else if (len(buf[10:]) >= 5 && buf[10:] == "false"){
|
||||
cfg.WebServer.Enabled = false
|
||||
}else {fail = true}
|
||||
} else {
|
||||
fail = true
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
//go:build linux
|
||||
// +build linux
|
||||
|
||||
package cfg
|
||||
|
||||
func init() {
|
||||
fp := func(c *Config) {
|
||||
c.System.OS = "linux"
|
||||
c.System.TmpDir = "/tmp/"
|
||||
c.System.HostsLoc = "/etc/hosts"
|
||||
c.System.CfgLoc = "/etc/rhosts/"
|
||||
c.System.Var = "/var/lib/rhosts/"
|
||||
}
|
||||
|
||||
configFuncs = append(configFuncs, fp)
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package sys
|
||||
|
||||
func init() {
|
||||
|
||||
}
|
||||
|
||||
func init() {
|
||||
fp := func(c *Config) {
|
||||
c.System.os = "windows"
|
||||
c.System.tmpdir = "/tmp/"
|
||||
c.System.hostsloc = "/Windows/System32/drivers/etc/hosts"
|
||||
c.System.cfgloc = "/ProgramData/rhosts/"
|
||||
c.System.Var = "/ProgramData/rhosts/"
|
||||
}
|
||||
|
||||
configFuncs = append(configFuncs, fp)
|
||||
}
|
||||
|
|
@ -0,0 +1,384 @@
|
|||
package hosts
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"io"
|
||||
"jbreich/rhosts/cfg"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
// siteList holds the location of all the sites along with a list of their location
|
||||
type siteList struct {
|
||||
location string
|
||||
siteEntry []siteEntry
|
||||
}
|
||||
|
||||
// siteEntry holds a single entry and if it is a repeat
|
||||
type siteEntry struct {
|
||||
repeat bool
|
||||
site string
|
||||
}
|
||||
|
||||
type siteEntryPointer struct {
|
||||
r *bool
|
||||
s *string
|
||||
}
|
||||
|
||||
func Update() (err error) {
|
||||
config := cfg.Create()
|
||||
var siteBuff []siteList
|
||||
|
||||
err = error(nil)
|
||||
err = copystatichosts(config.System.TmpDir, config.System.HostsLoc)
|
||||
if err != nil {
|
||||
log.Print("Failed to copy static entries")
|
||||
return
|
||||
}
|
||||
defer os.Remove(config.System.TmpDir + "rhosts")
|
||||
err, siteBuff = downloadcontent(config.Downloads, config.System.TmpDir, config.System.HostsLoc)
|
||||
if err != nil {
|
||||
log.Print("Failed to download entries")
|
||||
return
|
||||
}
|
||||
err = writesites(config.Sites, config.System.TmpDir, &siteBuff)
|
||||
if err != nil {
|
||||
log.Print("Failed to failed to copy rhosts static entries")
|
||||
return
|
||||
}
|
||||
removeUnwanted(&siteBuff, &config.Whitelist)
|
||||
err = write2tmp(config.System.TmpDir, &siteBuff)
|
||||
if err != nil {
|
||||
log.Print("Failed to write sites to tmpfile")
|
||||
return
|
||||
}
|
||||
err = writetmp2hosts(config.System.HostsLoc, config.System.TmpDir)
|
||||
if err != nil {
|
||||
log.Print("Failed to copy to hosts file")
|
||||
return
|
||||
}
|
||||
log.Print("Finished updating host")
|
||||
return
|
||||
}
|
||||
|
||||
// copystatichosts copies the hosts not managed by rhosts from the hosts file to the start of the temporary file
|
||||
func copystatichosts(tmpdir, hostsloc string) error {
|
||||
fileloc := tmpdir + "rhosts"
|
||||
file, err := os.Create(fileloc)
|
||||
defer file.Close()
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
return err
|
||||
}
|
||||
filer, err := os.Open(hostsloc)
|
||||
defer filer.Close()
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
return err
|
||||
}
|
||||
filebuf := bufio.NewScanner(filer)
|
||||
filebuf.Split(bufio.ScanLines)
|
||||
for res := filebuf.Scan(); res; res = filebuf.Scan() {
|
||||
buff := filebuf.Text()
|
||||
if buff == "# rhosts begin" {
|
||||
break
|
||||
}
|
||||
_, err := file.WriteString(buff + "\n")
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
_, err = file.WriteString("# rhosts begin\n")
|
||||
err = filebuf.Err()
|
||||
return err
|
||||
}
|
||||
|
||||
// downloadcontent attempts to download the provided url and create a siteList. If the file fails to download it attempts to find an old copy from the hosts file.
|
||||
func downloadcontent(downloads []string, tmpdir string, hostsloc string) (err error, list []siteList) {
|
||||
for _, d := range downloads {
|
||||
var site siteList
|
||||
site.location = d
|
||||
log.Print("Downloading: ", d)
|
||||
response, err := http.Get(d)
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
log.Print("Looking for old record in hosts file")
|
||||
downloadoldlookup(hostsloc, d, &site)
|
||||
} else {
|
||||
defer response.Body.Close()
|
||||
scanner := bufio.NewScanner(response.Body)
|
||||
for scanner.Scan() {
|
||||
resp := checkDownloadLine(scanner.Text())
|
||||
if resp.site != "" {
|
||||
site.siteEntry = append(site.siteEntry, resp)
|
||||
}
|
||||
}
|
||||
}
|
||||
list = append(list, site)
|
||||
}
|
||||
return err, list
|
||||
}
|
||||
|
||||
// checkDownloadLine parses the download line into just the address that needs to be blocked
|
||||
func checkDownloadLine(line string) (address siteEntry) {
|
||||
var token []string
|
||||
address.repeat = false
|
||||
address.site = ""
|
||||
buff := ""
|
||||
lineLength := len(line) - 1
|
||||
for i, c := range line {
|
||||
if c != ' ' && i < lineLength {
|
||||
buff += string(c)
|
||||
} else if len(buff) > 0 {
|
||||
if i == lineLength {
|
||||
buff += string(c)
|
||||
}
|
||||
token = append(token, buff)
|
||||
buff = ""
|
||||
}
|
||||
}
|
||||
if len(token) == 0 {
|
||||
return
|
||||
}
|
||||
if token[0][0] == '#' {
|
||||
return
|
||||
}
|
||||
for _, t := range token {
|
||||
var period uint
|
||||
var failed bool
|
||||
period = 0
|
||||
failed = false
|
||||
for _, c := range t {
|
||||
switch c {
|
||||
case '.':
|
||||
period++
|
||||
case '#':
|
||||
return
|
||||
case ':':
|
||||
failed = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if period <= 2 && failed == false {
|
||||
address.site = t
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// downloadoldlookup attemps to find an entry in the hosts file and add it to the siteList
|
||||
func downloadoldlookup(hostsloc, d string, site *siteList) error {
|
||||
var err error = nil
|
||||
var state uint8 = 0
|
||||
|
||||
hostsf, err := os.Open(hostsloc)
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
return err
|
||||
}
|
||||
defer hostsf.Close()
|
||||
|
||||
fbuff := bufio.NewScanner(hostsf)
|
||||
fbuff.Split(bufio.ScanLines)
|
||||
for res := fbuff.Scan(); res; res = fbuff.Scan() {
|
||||
buff := fbuff.Text()
|
||||
switch state {
|
||||
case 0:
|
||||
if buff == "# rhosts download - "+d {
|
||||
log.Print("Found old record in hosts file:" + buff)
|
||||
state = 1
|
||||
}
|
||||
case 1:
|
||||
if len(buff) >= 9 && buff[0:8] == "# rhosts" {
|
||||
state = 2
|
||||
} else {
|
||||
siteBuff := checkDownloadLine(buff)
|
||||
if siteBuff.site != "" {
|
||||
site.siteEntry = append(site.siteEntry, siteBuff)
|
||||
}
|
||||
}
|
||||
case 3:
|
||||
return nil
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// writesites writes the list of sites from the config file to the local siteList
|
||||
func writesites(sites []string, tmpdir string, siteBuff *[]siteList) (err error) {
|
||||
var localList siteList
|
||||
localList.location = "local"
|
||||
err = nil
|
||||
fileloc := tmpdir + "rhosts"
|
||||
log.Print("Opening: " + fileloc)
|
||||
if len(sites) == 0 {
|
||||
return
|
||||
}
|
||||
for _, s := range sites {
|
||||
var site siteEntry
|
||||
site.repeat = false
|
||||
site.site = s
|
||||
localList.siteEntry = append(localList.siteEntry, site)
|
||||
}
|
||||
*siteBuff = append(*siteBuff, localList)
|
||||
return
|
||||
}
|
||||
|
||||
// removeUnwanted removes any duplicate or uneeded/unwanted addresses
|
||||
func removeUnwanted(siteBuff *[]siteList, whitelist *[]string) {
|
||||
// Words that will be remove automatically, they hold significant importance
|
||||
var safewords = []string{"localhost", "localhost.localdomain", "broadcasthost", "ip6-loopback", "ip6-localhost", "ip6-localnet", "ip6-mcastprefix", "ip6-allnodes", "ip6-allrouters", "ip6-allhosts", "local"}
|
||||
|
||||
// Counters for how many sites were not added because: downloads, safewords, whitelisted
|
||||
var c struct {
|
||||
d uint
|
||||
s uint
|
||||
w uint
|
||||
}
|
||||
c.d = 0
|
||||
c.s = 0
|
||||
c.w = 0
|
||||
|
||||
var entry []siteEntryPointer
|
||||
var entryBuff siteEntryPointer
|
||||
|
||||
// Add downloads to buffered list of sites
|
||||
for i := len((*siteBuff)) - 1; i > -1; i-- {
|
||||
for j := len((*siteBuff)[i].siteEntry) - 1; j > -1; j-- {
|
||||
entryBuff.r = &((*siteBuff)[i].siteEntry[j].repeat)
|
||||
entryBuff.s = &((*siteBuff)[i].siteEntry[j].site)
|
||||
entry = append(entry, entryBuff)
|
||||
}
|
||||
}
|
||||
|
||||
// Removing safewords and whitelisted words
|
||||
log.Print("Checking for safewords and whitelisted words")
|
||||
for i,e := range entry {
|
||||
if (*e.r == true) {
|
||||
continue
|
||||
}
|
||||
for _,w := range(safewords) {
|
||||
if *e.s == w {
|
||||
*(entry[i].r) = true
|
||||
c.s++
|
||||
break
|
||||
}
|
||||
}
|
||||
for _, w := range *whitelist {
|
||||
if *e.s == w {
|
||||
*(entry[i].r) = true
|
||||
c.w++
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Removing duplicates
|
||||
log.Print("Checking for duplicates")
|
||||
cores := runtime.NumCPU()
|
||||
lenEntry := len(entry)
|
||||
|
||||
if (cores <= lenEntry){
|
||||
fin := make(chan uint)
|
||||
for i := cores; i > 0 ; i-- {
|
||||
go removeDuplicate(entry[i-1:],cores, fin)
|
||||
}
|
||||
for ;cores > 0; cores-- {
|
||||
count :=<-fin
|
||||
c.d =+ count
|
||||
}
|
||||
} else {
|
||||
removeDuplicate(entry,1, nil)
|
||||
}
|
||||
|
||||
log.Printf("Total: %d\tDuplicates: %d\tSafeWords: %d\tWhitelisted: %d\n", lenEntry, c.d, c.s, c.w)
|
||||
}
|
||||
|
||||
// RemoveDuplicate removes duplicates from a range
|
||||
func removeDuplicate (entry []siteEntryPointer, freq int , done chan uint){
|
||||
var cd uint
|
||||
for i, e := range entry {
|
||||
if (i+1)%freq != 0 {
|
||||
continue
|
||||
}
|
||||
if *e.r == true || (i+1)%freq != 0 {
|
||||
continue
|
||||
}
|
||||
for j, n := range entry[i+1:] {
|
||||
if *e.r == true {
|
||||
continue
|
||||
}
|
||||
if *e.s == *n.s {
|
||||
*(entry[i+j].r) = true
|
||||
cd++
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
if (done != nil){
|
||||
done <- cd
|
||||
}
|
||||
}
|
||||
|
||||
// write2tmp write the siteBuff to the tempfile
|
||||
func write2tmp(tmpdir string, siteBuff *[]siteList) (err error) {
|
||||
err = nil
|
||||
tmploc := tmpdir + "rhosts"
|
||||
tmpf, err := os.OpenFile(tmploc, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0600)
|
||||
defer tmpf.Close()
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
return err
|
||||
}
|
||||
for _, location := range *siteBuff {
|
||||
if len(location.siteEntry) == 0 {
|
||||
continue
|
||||
}
|
||||
_, err := tmpf.WriteString("# rhosts download - " + location.location + "\n")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, site := range location.siteEntry {
|
||||
if site.repeat == false {
|
||||
_, err = tmpf.WriteString("0.0.0.0 " + site.site + "\n")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = tmpf.WriteString(":: " + site.site + "\n")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// writetmp2hosts overwrites the hostsfile with the tmp file
|
||||
func writetmp2hosts(hostsloc, tmpdir string) error {
|
||||
var err error = nil
|
||||
tmploc := tmpdir + "rhosts"
|
||||
|
||||
hosts, err := os.Create(hostsloc)
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
return err
|
||||
}
|
||||
tmp, err := os.Open(tmploc)
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
return err
|
||||
}
|
||||
_, err = io.Copy(hosts, tmp)
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
528
src/rhosts.go
528
src/rhosts.go
|
|
@ -17,33 +17,20 @@
|
|||
* along with rhosts. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
// rhosts - Program used to maintain a blocklist appended to a host file
|
||||
// rhosts - Program used to maintain a blocklist appended to a host file
|
||||
package main
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"os"
|
||||
"io"
|
||||
"bufio"
|
||||
"log"
|
||||
"net/http"
|
||||
"flag"
|
||||
"time"
|
||||
"fmt"
|
||||
"jbreich/rhosts/hosts"
|
||||
"jbreich/rhosts/serve"
|
||||
"log"
|
||||
"time"
|
||||
)
|
||||
// siteList holds the location of all the sites along with a list of their location
|
||||
type siteList struct {
|
||||
location string
|
||||
siteEntry []siteEntry
|
||||
}
|
||||
// siteEntry holds a single entry and if it is a repeat
|
||||
type siteEntry struct {
|
||||
repeat bool
|
||||
site string
|
||||
}
|
||||
|
||||
const GPL =`
|
||||
|
||||
const GPL = `
|
||||
rhosts maintains a blocklist and appends it to the system hosts file
|
||||
|
||||
Copyright (C) 2021 Justin Reichardt
|
||||
|
|
@ -61,19 +48,26 @@ const GPL =`
|
|||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
`
|
||||
const usage = `
|
||||
This program's settings are modified by the rhost.cfg file and by setting the following flags:
|
||||
|
||||
`
|
||||
|
||||
|
||||
func main() {
|
||||
tmpdir := ""
|
||||
hostsloc := ""
|
||||
cfgloc := ""
|
||||
var daemon bool=false
|
||||
var interval int=1440
|
||||
var versionflag bool=false
|
||||
var removetimestamp bool=false
|
||||
var siteBuff []siteList
|
||||
exit := make(chan bool)
|
||||
var webserver bool = false
|
||||
var daemon bool = false
|
||||
var interval int = 1440
|
||||
var versionflag bool = false
|
||||
var removetimestamp bool = false
|
||||
|
||||
flag.Usage = func() { // [4]
|
||||
fmt.Fprintf(flag.CommandLine.Output(), GPL + usage)
|
||||
flag.PrintDefaults()
|
||||
}
|
||||
// Parsing Flags
|
||||
flag.BoolVar(&webserver, "s", false, "Turn on the Webserver")
|
||||
flag.BoolVar(&daemon, "d", false, "Should this be run in daemon mode")
|
||||
flag.IntVar(&interval, "t", 1440, "Minutes until next run of daemon")
|
||||
flag.BoolVar(&versionflag, "version", false, "show version information")
|
||||
|
|
@ -89,474 +83,46 @@ func main() {
|
|||
// Check if timestamp should be removed
|
||||
if removetimestamp {
|
||||
log.SetFlags(0)
|
||||
}else{
|
||||
} else {
|
||||
// GPL information
|
||||
fmt.Println(GPL)
|
||||
}
|
||||
|
||||
if daemon {
|
||||
log.Print("daemon:" , daemon)
|
||||
log.Print("interval:",interval)
|
||||
log.Print("daemon:", daemon)
|
||||
log.Print("interval:", interval)
|
||||
}
|
||||
|
||||
sysdetect (&tmpdir, &hostsloc, &cfgloc)
|
||||
|
||||
for true {
|
||||
var sites, downloads, whitelist []string
|
||||
err := error(nil)
|
||||
err = cfgparse(&sites, &downloads, &whitelist, cfgloc)
|
||||
if (err != nil){
|
||||
log.Print("Failed to parse config file")
|
||||
continue
|
||||
}
|
||||
err = copystatichosts(tmpdir, hostsloc)
|
||||
if (err != nil){
|
||||
log.Print("Failed to copy static entries")
|
||||
continue
|
||||
}
|
||||
defer os.Remove(tmpdir + "rhosts")
|
||||
err, siteBuff = downloadcontent(downloads, tmpdir, hostsloc)
|
||||
if (err != nil){
|
||||
log.Print("Failed to download entries")
|
||||
continue
|
||||
}
|
||||
err = writesites(sites, tmpdir, &siteBuff)
|
||||
if (err != nil){
|
||||
log.Print("Failed to failed to copy rhosts static entries")
|
||||
continue
|
||||
}
|
||||
removeduplicates(&siteBuff, &whitelist)
|
||||
err = write2tmp(tmpdir, &siteBuff)
|
||||
if (err != nil){
|
||||
log.Print("Failed to write sites to tmpfile")
|
||||
continue
|
||||
}
|
||||
err = writetmp2hosts(hostsloc, tmpdir)
|
||||
if (err != nil){
|
||||
log.Print("Failed to copy to hosts file")
|
||||
continue
|
||||
}
|
||||
|
||||
log.Print("Finished updating host")
|
||||
if (daemon == true){
|
||||
i := time.Now().Add(time.Duration(interval) * time.Minute).Format(time.Layout)
|
||||
log.Printf("Sleeping for %d minutes", interval)
|
||||
log.Print("Should restart at: " + i)
|
||||
time.Sleep(time.Duration(interval) * time.Minute)
|
||||
}else{
|
||||
break
|
||||
}
|
||||
// Starting web server
|
||||
if (webserver == true){
|
||||
go serve.Start(exit)
|
||||
defer func(){<-exit}()
|
||||
}
|
||||
}
|
||||
|
||||
// sysdetect determines which OS it is running on and set the default locations
|
||||
func sysdetect (tmpdir, hostsloc, cfgloc *string) {
|
||||
// Detect OS and set params
|
||||
switch runtime.GOOS {
|
||||
case "windows":
|
||||
//log.Fatal("Windows is not supported")
|
||||
*tmpdir = "/tmp"
|
||||
*hostsloc = "/Windows/System32/drivers/etc/hosts"
|
||||
*cfgloc = "/ProgramData/rhosts/"
|
||||
case "linux":
|
||||
*tmpdir = "/tmp/"
|
||||
*hostsloc = "/etc/hosts"
|
||||
*cfgloc ="/etc/rhosts/"
|
||||
case "ios":
|
||||
log.Fatal("IOS is not supported")
|
||||
default:
|
||||
log.Fatal(runtime.GOOS," is not supported")
|
||||
}
|
||||
}
|
||||
|
||||
// cfgparse recieves the location of the config file and returns a list of sites to add and content to download
|
||||
func cfgparse (sites, downloads, whitelist *[]string, cfgloc string) (error){
|
||||
l := (cfgloc + "rhosts.cfg")
|
||||
var err error=nil
|
||||
log.Print("Opening: ", l)
|
||||
if _,err = os.Stat(cfgloc); os.IsNotExist(err) {
|
||||
log.Print(cfgloc + " Does not exist, attempting to create it")
|
||||
err = os.MkdirAll(cfgloc,0755)
|
||||
// Update the hosts file
|
||||
if daemon == false {
|
||||
err := hosts.Update()
|
||||
if err != nil {
|
||||
log.Fatal("Could not create " + cfgloc)
|
||||
}
|
||||
}
|
||||
if _,err = os.Stat(l); os.IsNotExist(err) {
|
||||
log.Print(l + " does not exist, attempting to create a placeholder")
|
||||
err = os.WriteFile(l,[]byte(CFG),0644)
|
||||
if err != nil {
|
||||
log.Fatal("Unable to create file: " + l)
|
||||
}
|
||||
}
|
||||
file, err := os.Open(l)
|
||||
defer file.Close()
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
return err
|
||||
}
|
||||
filebuf := bufio.NewScanner(file)
|
||||
filebuf.Split(bufio.ScanLines)
|
||||
for res := filebuf.Scan();res;res = filebuf.Scan() {
|
||||
state, body := cfgparseline(filebuf.Text())
|
||||
switch state {
|
||||
case 3:
|
||||
*sites =append(*sites,body)
|
||||
case 4:
|
||||
*downloads = append(*downloads,body)
|
||||
case 5:
|
||||
*whitelist = append(*whitelist,body)
|
||||
}
|
||||
}
|
||||
err = filebuf.Err()
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
return err
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// cfgparseline reads a single line of the config and returns the type and content of the line
|
||||
func cfgparseline(buf string) (uint8, string){
|
||||
// State options
|
||||
// 0 - Init
|
||||
// 1 - Error
|
||||
// 2 - Comment
|
||||
// 3 - Site
|
||||
// 4 - Download
|
||||
// 5 - Whitelist
|
||||
var state uint8= 0
|
||||
body :=buf[:]
|
||||
for i:=0; i<len(buf);i++ {
|
||||
//fmt.Printf("%c",buf[i])
|
||||
switch buf[i] {
|
||||
case ' ':
|
||||
case '#':
|
||||
state = 2
|
||||
case 'd':
|
||||
if (len(buf) < i+10) {
|
||||
state = 1
|
||||
break
|
||||
}
|
||||
if (buf[i:(i+9)] == "download=") {
|
||||
i +=9
|
||||
state = 4
|
||||
body = buf[i:]
|
||||
} else{
|
||||
state = 1
|
||||
}
|
||||
case 's':
|
||||
if (len(buf) < i+6) {
|
||||
state = 1
|
||||
break
|
||||
}
|
||||
if (buf[i:(i+5)] == "site=") {
|
||||
i +=5
|
||||
state = 3
|
||||
body = buf[i:]
|
||||
} else{
|
||||
state = 0
|
||||
}
|
||||
//compare buf[i:(i+3)] to "site"
|
||||
case 'w':
|
||||
if (len(buf) < i+10) {
|
||||
state = 1
|
||||
break
|
||||
}
|
||||
if (buf[i:(i+10)] == "whitelist=") {
|
||||
i +=10
|
||||
state = 5
|
||||
body = buf[i:]
|
||||
} else{
|
||||
state = 1
|
||||
}
|
||||
}
|
||||
if (state !=0){
|
||||
return state,body
|
||||
}
|
||||
}
|
||||
return state, body
|
||||
}
|
||||
|
||||
// copystatichosts copies the hosts not managed by rhosts from the hosts file to the start of the temporary file
|
||||
func copystatichosts(tmpdir, hostsloc string) error {
|
||||
fileloc := tmpdir + "rhosts"
|
||||
file, err := os.Create(fileloc)
|
||||
defer file.Close()
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
return err
|
||||
}
|
||||
filer, err := os.Open(hostsloc)
|
||||
defer filer.Close()
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
return err
|
||||
}
|
||||
filebuf := bufio.NewScanner(filer)
|
||||
filebuf.Split(bufio.ScanLines)
|
||||
for res := filebuf.Scan();res;res = filebuf.Scan() {
|
||||
buff := filebuf.Text()
|
||||
if (buff == "# rhosts begin"){
|
||||
break
|
||||
}
|
||||
_,err := file.WriteString(buff + "\n")
|
||||
if (err != nil) {
|
||||
log.Print(err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
_,err = file.WriteString("# rhosts begin\n")
|
||||
err = filebuf.Err()
|
||||
return err
|
||||
}
|
||||
// downloadcontent attempts to download the provided url and create a siteList. If the file fails to download it attempts to find an old copy from the hosts file.
|
||||
func downloadcontent(downloads []string, tmpdir string, hostsloc string) (err error, list []siteList){
|
||||
for _, d := range downloads {
|
||||
var site siteList
|
||||
site.location = d
|
||||
log.Print("Downloading: ",d)
|
||||
response, err := http.Get(d)
|
||||
if (err !=nil) {
|
||||
log.Print(err)
|
||||
log.Print("Looking for old record in hosts file")
|
||||
downloadoldlookup(hostsloc, d, &site)
|
||||
}else{
|
||||
defer response.Body.Close()
|
||||
scanner := bufio.NewScanner(response.Body)
|
||||
for scanner.Scan() {
|
||||
resp := checkDownloadLine(scanner.Text())
|
||||
if resp.site != "" {
|
||||
site.siteEntry = append(site.siteEntry, resp)
|
||||
}
|
||||
}
|
||||
}
|
||||
list = append(list, site)
|
||||
}
|
||||
return err, list
|
||||
}
|
||||
} else {
|
||||
|
||||
// checkDownloadLine parses the download line into just the address that needs to be blocked
|
||||
func checkDownloadLine (line string) (address siteEntry){
|
||||
var token []string
|
||||
address.repeat = false
|
||||
address.site = ""
|
||||
buff := ""
|
||||
lineLength := len(line) -1
|
||||
for i, c := range(line){
|
||||
if c != ' ' && i < lineLength {
|
||||
buff += string(c)
|
||||
}else if len(buff) > 0 {
|
||||
if i == lineLength{
|
||||
buff += string(c)
|
||||
}
|
||||
token = append(token,buff)
|
||||
buff = ""
|
||||
}
|
||||
}
|
||||
if len(token) == 0 {
|
||||
return
|
||||
}
|
||||
if token[0][0] == '#' {
|
||||
return
|
||||
}
|
||||
for _, t := range(token) {
|
||||
var period uint
|
||||
var failed bool
|
||||
period = 0
|
||||
failed = false
|
||||
for _, c := range(t) {
|
||||
switch c{
|
||||
case '.':
|
||||
period ++
|
||||
case '#':
|
||||
return
|
||||
case ':':
|
||||
failed = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if period <=2 && failed == false {
|
||||
address.site = t
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// downloadoldlookup attemps to find an entry in the hosts file and add it to the siteList
|
||||
func downloadoldlookup(hostsloc, d string, site *siteList) error {
|
||||
var err error = nil
|
||||
var state uint8 = 0
|
||||
|
||||
hostsf, err := os.Open(hostsloc)
|
||||
if (err != nil){
|
||||
log.Print(err)
|
||||
return err
|
||||
}
|
||||
defer hostsf.Close()
|
||||
|
||||
fbuff := bufio.NewScanner(hostsf)
|
||||
fbuff.Split(bufio.ScanLines)
|
||||
for res := fbuff.Scan();res;res = fbuff.Scan() {
|
||||
buff := fbuff.Text()
|
||||
switch state {
|
||||
case 0:
|
||||
if (buff == "# rhosts download - " + d){
|
||||
log.Print("Found old record in hosts file:" + buff)
|
||||
state =1
|
||||
}
|
||||
case 1:
|
||||
if (len(buff) >=9 && buff[0:8] == "# rhosts"){
|
||||
state = 2
|
||||
}else{
|
||||
siteBuff := checkDownloadLine(buff)
|
||||
if siteBuff.site != "" {
|
||||
site.siteEntry = append(site.siteEntry, siteBuff)
|
||||
}
|
||||
}
|
||||
case 3:
|
||||
return nil
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// writesites writes the list of sites from the config file to the local siteList
|
||||
func writesites(sites []string, tmpdir string, siteBuff *[]siteList) (err error) {
|
||||
var localList siteList
|
||||
localList.location = "local"
|
||||
err = nil
|
||||
fileloc := tmpdir + "rhosts"
|
||||
log.Print("Opening: " + fileloc)
|
||||
if len(sites) == 0 {
|
||||
return
|
||||
}
|
||||
for _,s := range sites {
|
||||
var site siteEntry
|
||||
site.repeat = false
|
||||
site.site = s
|
||||
localList.siteEntry = append(localList.siteEntry,site)
|
||||
}
|
||||
*siteBuff = append(*siteBuff,localList)
|
||||
return
|
||||
}
|
||||
// removeduplicates removes any duplicate or uneeded/unwanted addresses
|
||||
func removeduplicates(siteBuff *[]siteList, whitelist *[]string){
|
||||
var safewords = []string{"localhost", "localhost.localdomain", "broadcasthost", "ip6-loopback", "ip6-localhost", "ip6-localnet", "ip6-mcastprefix", "ip6-allnodes", "ip6-allrouters", "ip6-allhosts", "local"}
|
||||
var c struct {
|
||||
d uint
|
||||
s uint
|
||||
w uint
|
||||
}
|
||||
c.d = 0
|
||||
c.s = 0
|
||||
c.w = 0
|
||||
log.Print("Checking for duplicates")
|
||||
var entry []struct{
|
||||
r *bool
|
||||
s *string
|
||||
}
|
||||
var entryBuff struct{
|
||||
r *bool
|
||||
s *string
|
||||
}
|
||||
for i := len((*siteBuff))-1; i > -1; i --{
|
||||
for j := len((*siteBuff)[i].siteEntry)-1; j > -1; j -- {
|
||||
entryBuff.r = &((*siteBuff)[i].siteEntry[j].repeat)
|
||||
entryBuff.s = &((*siteBuff)[i].siteEntry[j].site)
|
||||
entry = append(entry,entryBuff)
|
||||
}
|
||||
}
|
||||
lenEntry := len(entry)
|
||||
for i,e := range(entry) {
|
||||
for _,w := range(safewords){
|
||||
if *e.s == w {
|
||||
*(entry[i].r) = true
|
||||
c.s ++
|
||||
break
|
||||
}
|
||||
}
|
||||
if *(entry[i].r) == true {
|
||||
continue
|
||||
}
|
||||
for _,w := range(*whitelist){
|
||||
if *e.s == w {
|
||||
*(entry[i].r) = true
|
||||
c.w ++
|
||||
break
|
||||
}
|
||||
}
|
||||
if *(entry[i].r) == true {
|
||||
continue
|
||||
}
|
||||
if i == lenEntry {
|
||||
break
|
||||
}
|
||||
for j,n := range(entry[i+1:]){
|
||||
if *e.s == *n.s {
|
||||
*(entry[i+j].r) = true
|
||||
c.d ++
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
log.Printf("Total: %d\tDuplicates: %d\tSafeWords: %d\tWhitelisted: %d\n", lenEntry, c.d, c.s, c.w)
|
||||
}
|
||||
// write2tmp write the siteBuff to the tempfile
|
||||
func write2tmp(tmpdir string, siteBuff *[]siteList) (err error) {
|
||||
err = nil
|
||||
tmploc := tmpdir+ "rhosts"
|
||||
tmpf, err := os.OpenFile(tmploc, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0600)
|
||||
defer tmpf.Close()
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
return err
|
||||
}
|
||||
for _,location := range(*siteBuff){
|
||||
if len(location.siteEntry) == 0 {
|
||||
continue
|
||||
}
|
||||
_,err := tmpf.WriteString("# rhosts download - " + location.location + "\n")
|
||||
for true {
|
||||
err := hosts.Update()
|
||||
if err != nil {
|
||||
return err
|
||||
log.Print(err)
|
||||
}
|
||||
for _,site := range(location.siteEntry){
|
||||
if site.repeat == false {
|
||||
_, err = tmpf.WriteString("0.0.0.0 " + site.site + "\n")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = tmpf.WriteString(":: " + site.site + "\n")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Check if daemon
|
||||
if daemon == false {
|
||||
break
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
i := time.Now().Add(time.Duration(interval) * time.Minute).Format(time.Layout)
|
||||
log.Printf("Sleeping for %d minutes", interval)
|
||||
log.Print("Should restart at: " + i)
|
||||
time.Sleep(time.Duration(interval) * time.Minute)
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
// writetmp2hosts overwrites the hostsfile with the tmp file
|
||||
func writetmp2hosts(hostsloc, tmpdir string) error {
|
||||
var err error = nil
|
||||
tmploc := tmpdir + "rhosts"
|
||||
|
||||
hosts, err := os.Create(hostsloc)
|
||||
if (err != nil){
|
||||
log.Print(err)
|
||||
return err
|
||||
}
|
||||
tmp, err := os.Open(tmploc)
|
||||
if (err != nil){
|
||||
log.Print(err)
|
||||
return err
|
||||
}
|
||||
_,err = io.Copy(hosts,tmp)
|
||||
if (err != nil){
|
||||
log.Print(err)
|
||||
}
|
||||
|
||||
|
||||
return err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,233 @@
|
|||
/*
|
||||
Certutils manages certificates.
|
||||
|
||||
To Create a Certificate start with:
|
||||
|
||||
var ca CA
|
||||
|
||||
From there you will want to edit:
|
||||
|
||||
- Location
|
||||
|
||||
- Name
|
||||
|
||||
- X509Cert
|
||||
|
||||
- Signer (If it is suppose to be signed by another CA, you must add it before initializing)
|
||||
|
||||
Then initialize it:
|
||||
|
||||
ca.Initialize
|
||||
|
||||
Create the files:
|
||||
|
||||
ca.WriteToFile()
|
||||
|
||||
|
||||
*/
|
||||
package certutils
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"log"
|
||||
"math/big"
|
||||
"os"
|
||||
"path"
|
||||
)
|
||||
|
||||
// I want to give Shane Utt credit for being the example for how this module creates and signs certs
|
||||
|
||||
/*
|
||||
* CA holds information regarding a key pair and certificate
|
||||
*/
|
||||
type CA struct {
|
||||
Location string // Location is the location of the keys
|
||||
Name string // This is the name of the certificate, it will append ".key" and ".crt" when writing them:w
|
||||
TLSConf *tls.Config
|
||||
CRTPEM []byte // This will hold the certificate pem byte file until it is writen to files
|
||||
PrivKeyPEM []byte // This will hold the private key pem byte file until it is writen to files
|
||||
X509Cert *x509.Certificate // Make sure to change this to match what you want your certificate to say
|
||||
Signer *CA // Signer is the address of who you want to sign this key
|
||||
keyPair *rsa.PrivateKey
|
||||
}
|
||||
|
||||
// If a location is given it will check for one at the location first.
|
||||
// If one does not exist it will create one using the provided certificate.
|
||||
// If one does exist it will check it against the certificate and decided if a new one needs to be made.
|
||||
// If a Certificate is not provided then the default template will be used.
|
||||
// The name it looks for is "ca": ca.key, ca.crt if no name is provided
|
||||
func (ca CA) Initialize() (CA, error) {
|
||||
var err error
|
||||
|
||||
// Check for name
|
||||
if ca.Name == "" {
|
||||
ca.Name = "ca"
|
||||
}
|
||||
|
||||
// Check if CA Exists
|
||||
var caBuf CA
|
||||
caBuf, err = fillFromFile(ca)
|
||||
if err == nil {
|
||||
ca = caBuf
|
||||
} else {
|
||||
log.Print(err)
|
||||
|
||||
// Check for certificate
|
||||
if ca.X509Cert == nil {
|
||||
return ca, errors.New("No x509 certificate provided")
|
||||
}
|
||||
|
||||
// If CA does not exist then create one
|
||||
ca, err = ca.Build()
|
||||
if err != nil {
|
||||
return ca, err
|
||||
}
|
||||
}
|
||||
|
||||
return ca, err
|
||||
|
||||
}
|
||||
|
||||
func fillFromFile(ca CA) (CA, error) {
|
||||
var err error
|
||||
|
||||
file := path.Join(ca.Location + ca.Name)
|
||||
_, err = os.Stat(file + ".crt")
|
||||
if err != nil {
|
||||
return ca, err
|
||||
}
|
||||
ca.CRTPEM, err = os.ReadFile(file + ".crt")
|
||||
if err != nil {
|
||||
return ca, err
|
||||
}
|
||||
|
||||
_, err = os.Stat(file + ".key")
|
||||
if err != nil {
|
||||
return ca, err
|
||||
}
|
||||
ca.PrivKeyPEM, err = os.ReadFile(file + ".key")
|
||||
if err != nil {
|
||||
return ca, err
|
||||
}
|
||||
|
||||
log.Print("Reading keys from file")
|
||||
|
||||
// Decode crt pem
|
||||
var p *pem.Block
|
||||
|
||||
p, _ = pem.Decode(ca.CRTPEM)
|
||||
if p == nil {
|
||||
return ca, errors.New("Failed to parse: " + file + ".crt")
|
||||
}
|
||||
|
||||
certBuf, err := x509.ParseCertificate(p.Bytes)
|
||||
if err != nil {
|
||||
return ca, err
|
||||
}
|
||||
// This is where it needs to check if they match, for now it will just assign it
|
||||
ca.X509Cert = certBuf
|
||||
|
||||
// Decode key pem
|
||||
p, _ = pem.Decode(ca.PrivKeyPEM)
|
||||
if p == nil {
|
||||
return ca, errors.New("Failed to parse: " + file + ".key")
|
||||
}
|
||||
ca.keyPair, err = x509.ParsePKCS1PrivateKey(p.Bytes)
|
||||
if err != nil {
|
||||
return ca, err
|
||||
}
|
||||
|
||||
return ca, err
|
||||
}
|
||||
|
||||
// WriteToFile will create a key and crt file using the Location and Name on the CA
|
||||
func (ca CA) WriteToFile() (err error) {
|
||||
var file string
|
||||
|
||||
file = path.Join(ca.Location, ca.Name+".crt")
|
||||
log.Print("Creating: " + file)
|
||||
err = os.WriteFile(file, ca.CRTPEM, 0660)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
file = path.Join(ca.Location, ca.Name+".key")
|
||||
log.Print("Creating: " + file)
|
||||
err = os.WriteFile(file, ca.PrivKeyPEM, 0660)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// This is run when initializing unless the files already exist. You can force the building process here afterwards if you want to use the current one's x509 certificate but regenerate everything else.
|
||||
func (ca CA) Build() (CA, error) {
|
||||
log.Print("Generating key" + ca.Name)
|
||||
var err error
|
||||
|
||||
// Changing the default serial number
|
||||
// For some reason it uses 2019 as the default, I need to watch this in the event it changes
|
||||
if ca.X509Cert.SerialNumber.Cmp(big.NewInt(2019)) == 0 {
|
||||
// generate a random serial number
|
||||
// Later should modify this to use issuerDN+serial
|
||||
serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
|
||||
serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
|
||||
if err != nil {
|
||||
return ca, err
|
||||
}
|
||||
|
||||
ca.X509Cert.SerialNumber = serialNumber
|
||||
}
|
||||
|
||||
// create our private and public key
|
||||
caKeyPair, err := rsa.GenerateKey(rand.Reader, 4096)
|
||||
if err != nil {
|
||||
return ca, err
|
||||
}
|
||||
ca.keyPair = caKeyPair
|
||||
|
||||
if ca.Signer == nil {
|
||||
ca.Signer = &ca
|
||||
}
|
||||
|
||||
// create the CA
|
||||
caBytes, err := x509.CreateCertificate(rand.Reader, ca.X509Cert, ca.Signer.X509Cert, &caKeyPair.PublicKey, ca.Signer.keyPair)
|
||||
if err != nil {
|
||||
return ca, err
|
||||
}
|
||||
|
||||
// pem encode
|
||||
crtPEM := new(bytes.Buffer)
|
||||
pem.Encode(crtPEM, &pem.Block{
|
||||
Type: "CERTIFICATE",
|
||||
Bytes: caBytes,
|
||||
})
|
||||
|
||||
privKeyPEM := new(bytes.Buffer)
|
||||
pem.Encode(privKeyPEM, &pem.Block{
|
||||
Type: "RSA PRIVATE KEY",
|
||||
Bytes: x509.MarshalPKCS1PrivateKey(caKeyPair),
|
||||
})
|
||||
|
||||
ca.CRTPEM = crtPEM.Bytes()
|
||||
ca.PrivKeyPEM = privKeyPEM.Bytes()
|
||||
|
||||
return ca, err
|
||||
}
|
||||
|
||||
func (ca CA) TlsConfigCreate() (*tls.Config, error) {
|
||||
certpool := x509.NewCertPool()
|
||||
if certpool.AppendCertsFromPEM(ca.CRTPEM) == false {
|
||||
return nil, errors.New("Failed to AppendCertsFromPEM" + ca.Name)
|
||||
}
|
||||
TLSConf := &tls.Config{
|
||||
RootCAs: certpool,
|
||||
}
|
||||
|
||||
return TLSConf, nil
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,79 @@
|
|||
package certutils
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"math/big"
|
||||
"time"
|
||||
"net"
|
||||
)
|
||||
|
||||
// Parent template for all further certificates
|
||||
var x509CATmpl = &x509.Certificate{
|
||||
SerialNumber: big.NewInt(2019),
|
||||
Subject: pkix.Name{
|
||||
Organization: []string{"Master CA of Localhost"},
|
||||
Country: []string{"XX"},
|
||||
Province: []string{""},
|
||||
Locality: []string{"Your computer"},
|
||||
StreetAddress: []string{""},
|
||||
PostalCode: []string{"5432"},
|
||||
},
|
||||
NotBefore: time.Now(),
|
||||
NotAfter: time.Now().AddDate(10, 0, 0),
|
||||
IsCA: true,
|
||||
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth},
|
||||
KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
|
||||
BasicConstraintsValid: true,
|
||||
}
|
||||
|
||||
var x509ServerTmpl = &x509.Certificate{
|
||||
SerialNumber: big.NewInt(2019),
|
||||
Subject: pkix.Name{
|
||||
Organization: []string{"The Website"},
|
||||
Country: []string{"US"},
|
||||
Province: []string{""},
|
||||
Locality: []string{"webserver"},
|
||||
StreetAddress: []string{"bus 1"},
|
||||
PostalCode: []string{"8"},
|
||||
},
|
||||
IPAddresses: []net.IP{net.IPv4(127, 0, 0, 1), net.IPv6loopback},
|
||||
NotBefore: time.Now(),
|
||||
NotAfter: time.Now().AddDate(10, 0, 0),
|
||||
SubjectKeyId: []byte{1, 2, 3, 4, 6},
|
||||
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth},
|
||||
KeyUsage: x509.KeyUsageDigitalSignature,
|
||||
}
|
||||
|
||||
|
||||
func TestWriteToFile (t *testing.T){
|
||||
var ca, server CA
|
||||
var err error
|
||||
|
||||
ca.Name = "TestCertificateAuthority"
|
||||
ca.X509Cert = x509CATmpl
|
||||
ca, err = ca.Initialize()
|
||||
if (err != nil){
|
||||
t.Error("Failed to genrate the TestCertificateAuthority certificate" + err.Error())
|
||||
}
|
||||
|
||||
|
||||
server.Name = "server"
|
||||
server.X509Cert = x509ServerTmpl
|
||||
server.Signer = &ca
|
||||
server, err = server.Initialize()
|
||||
if (err != nil){
|
||||
t.Error("Failed to generate the server certificate" + err.Error())
|
||||
}
|
||||
|
||||
err = ca.WriteToFile()
|
||||
if (err != nil){
|
||||
t.Error("Failed to write the TestCertificateAuthority files" + err.Error())
|
||||
}
|
||||
err = server.WriteToFile()
|
||||
if (err != nil){
|
||||
t.Error("Failed to write the sever portion" + err.Error())
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,109 @@
|
|||
// Provides the web server for rhosts to relay altered content
|
||||
package serve
|
||||
|
||||
import (
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"net/http"
|
||||
"jbreich/rhosts/serve/certutils"
|
||||
"jbreich/rhosts/cfg"
|
||||
"log"
|
||||
"os"
|
||||
"path"
|
||||
"math/big"
|
||||
"time"
|
||||
"net"
|
||||
)
|
||||
|
||||
func Start(exit chan bool) {
|
||||
config := cfg.Create()
|
||||
if config.WebServer.Enabled == false {
|
||||
log.Print("Webserver was disabled in the config file")
|
||||
exit <- true
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
go httpServer()
|
||||
go httpsServer(path.Join(config.System.Var, "certs"))
|
||||
}
|
||||
|
||||
func httpServer(){
|
||||
err := http.ListenAndServe("127.0.0.1:80", http.HandlerFunc(httpHandler))
|
||||
if (err != nil) {log.Fatal("Failed to start httls server")}
|
||||
}
|
||||
func httpsServer(certPath string) {
|
||||
var err error
|
||||
|
||||
// Create certificates if they do not exist
|
||||
// CA
|
||||
err = os.MkdirAll(certPath,0755)
|
||||
if (err != nil){log.Fatal("Could not create cert path: " + err.Error())}
|
||||
|
||||
var ca certutils.CA
|
||||
ca.Location = certPath
|
||||
ca.Name = "ca"
|
||||
ca.X509Cert = &x509.Certificate{
|
||||
SerialNumber: big.NewInt(2019),
|
||||
Subject: pkix.Name{
|
||||
Organization: []string{"Rhost"},
|
||||
Country: []string{""},
|
||||
Province: []string{""},
|
||||
Locality: []string{"Your computer"},
|
||||
StreetAddress: []string{""},
|
||||
PostalCode: []string{""},
|
||||
},
|
||||
NotBefore: time.Now(),
|
||||
NotAfter: time.Now().AddDate(10, 0, 0),
|
||||
IsCA: true,
|
||||
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth},
|
||||
KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
|
||||
BasicConstraintsValid: true,
|
||||
}
|
||||
|
||||
ca,err = ca.Initialize()
|
||||
if(err!=nil){log.Fatal("Failed to build the ca: " + err.Error())}
|
||||
err = ca.WriteToFile()
|
||||
if(err!=nil){log.Fatal("Failed to write the ca: " + err.Error())}
|
||||
|
||||
// Server Certificate
|
||||
var serverCa certutils.CA
|
||||
serverCa.Location = certPath
|
||||
serverCa.Name = "server"
|
||||
serverCa.Signer = &ca
|
||||
|
||||
serverCa.X509Cert = &x509.Certificate{
|
||||
SerialNumber: big.NewInt(2019),
|
||||
Subject: pkix.Name{
|
||||
Organization: []string{"The Website"},
|
||||
Country: []string{"US"},
|
||||
Province: []string{""},
|
||||
Locality: []string{"webserver"},
|
||||
StreetAddress: []string{""},
|
||||
PostalCode: []string{""},
|
||||
},
|
||||
IPAddresses: []net.IP{net.IPv4(127, 0, 0, 1), net.IPv6loopback},
|
||||
NotBefore: time.Now(),
|
||||
NotAfter: time.Now().AddDate(10, 0, 0),
|
||||
SubjectKeyId: []byte{1, 2, 3, 4, 6},
|
||||
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth},
|
||||
KeyUsage: x509.KeyUsageDigitalSignature,
|
||||
}
|
||||
|
||||
serverCa,err = serverCa.Initialize()
|
||||
if(err!=nil){log.Fatal("Failed to build the server certificate: " + err.Error())}
|
||||
serverCa.WriteToFile()
|
||||
if(err!=nil){log.Fatal("Failed to write the server certificate: " + err.Error())}
|
||||
|
||||
|
||||
// Starting the server
|
||||
err = http.ListenAndServeTLS("127.0.0.1:443", path.Join(certPath, "server.crt"), path.Join(certPath, "server.key"), http.HandlerFunc(httpHandler))
|
||||
if (err != nil) {log.Fatal("Failed to start httls server: " + err.Error())}
|
||||
return
|
||||
}
|
||||
|
||||
func httpHandler(w http.ResponseWriter, r *http.Request) {
|
||||
http.Error(w, "Test", 200)
|
||||
}
|
||||
Loading…
Reference in New Issue