315 lines
9.2 KiB
Go
315 lines
9.2 KiB
Go
// Copyright 2017-2021 DERO Project. All rights reserved.
|
|
// Use of this source code in any form is governed by RESEARCH license.
|
|
// license can be found in the LICENSE file.
|
|
// GPG: 0F39 E425 8C65 3947 702A 8234 08B2 0360 A03A 9DE8
|
|
//
|
|
//
|
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
|
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
|
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
|
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
|
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
|
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
package main
|
|
|
|
import "fmt"
|
|
import "net"
|
|
import "time"
|
|
import "io"
|
|
|
|
//import "io/ioutil"
|
|
//import "net/http"
|
|
import "context"
|
|
import "strings"
|
|
import "math/rand"
|
|
import "encoding/base64"
|
|
import "encoding/json"
|
|
import "runtime/debug"
|
|
import "encoding/binary"
|
|
|
|
//import "crypto/tls"
|
|
|
|
import "github.com/blang/semver/v4"
|
|
import "github.com/miekg/dns"
|
|
|
|
import "github.com/deroproject/derohe/config"
|
|
import "github.com/deroproject/derohe/globals"
|
|
|
|
/* this needs to be set on update.dero.io. as TXT record, in encoded form as base64
|
|
*
|
|
{ "version" : "1.0.2",
|
|
"message" : "\n\n\u001b[32m This is a mandatory update\u001b[0m",
|
|
"critical" : ""
|
|
}
|
|
|
|
base64 eyAidmVyc2lvbiIgOiAiMS4wLjIiLAogIm1lc3NhZ2UiIDogIlxuXG5cdTAwMWJbMzJtIFRoaXMgaXMgYSBtYW5kYXRvcnkgdXBkYXRlXHUwMDFiWzBtIiwgCiJjcml0aWNhbCIgOiAiIiAKfQ==
|
|
|
|
|
|
|
|
TXT record should be set as update=eyAidmVyc2lvbiIgOiAiMS4wLjIiLAogIm1lc3NhZ2UiIDogIlxuXG5cdTAwMWJbMzJtIFRoaXMgaXMgYSBtYW5kYXRvcnkgdXBkYXRlXHUwMDFiWzBtIiwgCiJjcml0aWNhbCIgOiAiIiAKfQ==
|
|
*/
|
|
|
|
func check_update_loop() {
|
|
|
|
for {
|
|
|
|
if config.DNS_NOTIFICATION_ENABLED {
|
|
|
|
globals.Logger.V(2).Info("Checking update..")
|
|
check_update()
|
|
}
|
|
time.Sleep(2 * 3600 * time.Second) // check every 2 hours
|
|
}
|
|
|
|
}
|
|
|
|
// wrapper to make requests using proxy
|
|
func dialContextwrapper(ctx context.Context, network, address string) (net.Conn, error) {
|
|
return globals.Dialer.Dial(network, address)
|
|
}
|
|
|
|
type socks_dialer net.Dialer
|
|
|
|
func (d *socks_dialer) Dial(network, address string) (net.Conn, error) {
|
|
return globals.Dialer.Dial(network, address)
|
|
}
|
|
|
|
func (d *socks_dialer) DialContext(ctx context.Context, network, address string) (net.Conn, error) {
|
|
return globals.Dialer.Dial(network, address)
|
|
}
|
|
|
|
func dial_random_read_response(in []byte) (out []byte, err error) {
|
|
defer func() {
|
|
if r := recover(); r != nil {
|
|
logger.V(2).Error(nil, "Recovered while checking updates", "r", r, "stack", debug.Stack())
|
|
}
|
|
}()
|
|
|
|
// since we may be connecting through socks, grab the remote ip for our purpose rightnow
|
|
//conn, err := globals.Dialer.Dial("tcp", "208.67.222.222:53")
|
|
//conn, err := net.Dial("tcp", "8.8.8.8:53")
|
|
random_feeder := rand.New(globals.NewCryptoRandSource()) // use crypto secure resource
|
|
server_address := config.DNS_servers[random_feeder.Intn(len(config.DNS_servers))] // choose a random server cryptographically
|
|
conn, err := net.Dial("tcp", server_address)
|
|
|
|
//conn, err := tls.Dial("tcp", remote_ip.String(),&tls.Config{InsecureSkipVerify: true})
|
|
if err != nil {
|
|
logger.V(2).Error(err, "Dial failed ")
|
|
return
|
|
}
|
|
|
|
defer conn.Close() // close connection at end
|
|
|
|
// upgrade connection TO TLS ( tls.Dial does NOT support proxy)
|
|
//conn = tls.Client(conn, &tls.Config{InsecureSkipVerify: true})
|
|
|
|
//rlog.Tracef(1, "Sending %d bytes", len(in))
|
|
|
|
var buf [2]byte
|
|
binary.BigEndian.PutUint16(buf[:], uint16(len(in)))
|
|
conn.Write(buf[:]) // write length in bigendian format
|
|
|
|
conn.Write(in) // write data
|
|
|
|
// now we must wait for response to arrive
|
|
var frame_length_buf [2]byte
|
|
|
|
conn.SetReadDeadline(time.Now().Add(20 * time.Second))
|
|
nbyte, err := io.ReadFull(conn, frame_length_buf[:])
|
|
if err != nil || nbyte != 2 {
|
|
// error while reading from connection we must disconnect it
|
|
logger.V(2).Error(err, "Could not read DNS length prefix")
|
|
return
|
|
}
|
|
|
|
frame_length := binary.BigEndian.Uint16(frame_length_buf[:])
|
|
if frame_length == 0 {
|
|
// most probably memory DDOS attack, kill the connection
|
|
logger.V(2).Error(nil, "Frame length is too small")
|
|
return
|
|
}
|
|
|
|
out = make([]byte, frame_length)
|
|
|
|
conn.SetReadDeadline(time.Now().Add(20 * time.Second))
|
|
data_size, err := io.ReadFull(conn, out)
|
|
if err != nil || data_size <= 0 || uint16(data_size) != frame_length {
|
|
// error while reading from connection we must kiil it
|
|
//rlog.Warnf("Could not read DNS data size read %d, frame length %d err %s", data_size, frame_length, err)
|
|
logger.V(2).Error(err, "Could not read DNS data")
|
|
return
|
|
|
|
}
|
|
out = out[:frame_length]
|
|
|
|
return
|
|
}
|
|
|
|
func check_update() {
|
|
|
|
// add panic handler, in case DNS acts rogue and tries to attack
|
|
defer func() {
|
|
if r := recover(); r != nil {
|
|
logger.V(2).Error(nil, "Recovered while checking updates", r, "r", "stack", debug.Stack())
|
|
}
|
|
}()
|
|
|
|
if !config.DNS_NOTIFICATION_ENABLED { // if DNS notifications are disabled bail out
|
|
return
|
|
}
|
|
|
|
/* var u update_message
|
|
u.Version = "2.0.0"
|
|
u.Message = "critical msg txt\x1b[35m should \n be in RED"
|
|
|
|
globals.Logger.Infof("testing %s",u.Message)
|
|
|
|
j,err := json.Marshal(u)
|
|
globals.Logger.Infof("json format %s err %s",j,err)
|
|
*/
|
|
/*extract_parse_version("update=eyAidmVyc2lvbiIgOiAiMS4xLjAiLCAibWVzc2FnZSIgOiAiXG5cblx1MDAxYlszMm0gVGhpcyBpcyBhIG1hbmRhdG9yeSB1cGdyYWRlIHBsZWFzZSB1cGdyYWRlIGZyb20geHl6IFx1MDAxYlswbSIsICJjcml0aWNhbCIgOiAiIiB9")
|
|
|
|
return
|
|
*/
|
|
|
|
m1 := new(dns.Msg)
|
|
// m1.SetEdns0(65000, true), dnssec probably leaks current timestamp, it's disabled until more invetigation
|
|
m1.Id = dns.Id()
|
|
m1.RecursionDesired = true
|
|
m1.Question = make([]dns.Question, 1)
|
|
m1.Question[0] = dns.Question{Name: config.DNS_UPDATE_CHECK, Qtype: dns.TypeTXT, Qclass: dns.ClassINET}
|
|
|
|
packed, err := m1.Pack()
|
|
if err != nil {
|
|
globals.Logger.V(2).Error(err, "Error which packing DNS query for program update")
|
|
return
|
|
}
|
|
|
|
/*
|
|
|
|
// setup a http client
|
|
httpTransport := &http.Transport{}
|
|
httpClient := &http.Client{Transport: httpTransport}
|
|
// set our socks5 as the dialer
|
|
httpTransport.Dial = globals.Dialer.Dial
|
|
|
|
|
|
|
|
packed_base64:= base64.RawURLEncoding.EncodeToString(packed)
|
|
response, err := httpClient.Get("https://1.1.1.1/dns-query?ct=application/dns-udpwireformat&dns="+packed_base64)
|
|
|
|
_ = packed_base64
|
|
|
|
if err != nil {
|
|
rlog.Warnf("error making DOH request err %s",err)
|
|
return
|
|
}
|
|
|
|
defer response.Body.Close()
|
|
contents, err := ioutil.ReadAll(response.Body)
|
|
if err != nil {
|
|
rlog.Warnf("error reading DOH response err %s",err)
|
|
return
|
|
}
|
|
*/
|
|
|
|
contents, err := dial_random_read_response(packed)
|
|
if err != nil {
|
|
logger.V(2).Error(err, "error reading response from DNS server")
|
|
return
|
|
|
|
}
|
|
|
|
//rlog.Debugf("DNS response length from DNS server %d bytes", len(contents))
|
|
|
|
err = m1.Unpack(contents)
|
|
if err != nil {
|
|
logger.V(2).Error(err, "error decoding DOH response")
|
|
return
|
|
|
|
}
|
|
|
|
for i := range m1.Answer {
|
|
if t, ok := m1.Answer[i].(*dns.TXT); ok {
|
|
|
|
// replace any spaces so as records could be joined
|
|
|
|
logger.V(2).Info("Processing record ", "record", t.Txt)
|
|
joined := strings.Join(t.Txt, "")
|
|
extract_parse_version(joined)
|
|
|
|
}
|
|
}
|
|
|
|
//globals.Logger.Infof("response %+v err ",m1,err)
|
|
|
|
}
|
|
|
|
type update_message struct {
|
|
Version string `json:"version"`
|
|
Message string `json:"message"`
|
|
Critical string `json:"critical"` // always broadcasted, without checks for version
|
|
}
|
|
|
|
// our version are TXT record of following format
|
|
// version=base64 encoded json
|
|
func extract_parse_version(str string) {
|
|
|
|
strl := strings.ToLower(str)
|
|
if !strings.HasPrefix(strl, "update=") {
|
|
logger.V(2).Info("Skipping record", "record", str)
|
|
return
|
|
}
|
|
|
|
parts := strings.SplitN(str, "=", 2)
|
|
if len(parts) != 2 {
|
|
return
|
|
}
|
|
|
|
data, err := base64.StdEncoding.DecodeString(parts[1])
|
|
if err != nil {
|
|
logger.V(2).Error(err, "Could NOT decode base64 update message", "data", parts[1])
|
|
return
|
|
}
|
|
|
|
var u update_message
|
|
err = json.Unmarshal(data, &u)
|
|
|
|
//globals.Logger.Infof("data %+v", u)
|
|
|
|
if err != nil {
|
|
logger.V(2).Error(err, "Could NOT decode json update message")
|
|
return
|
|
}
|
|
|
|
uversion, err := semver.ParseTolerant(u.Version)
|
|
if err != nil {
|
|
logger.V(2).Error(err, "Could NOT update version")
|
|
}
|
|
|
|
current_version := config.Version
|
|
current_version.Pre = current_version.Pre[:0]
|
|
current_version.Build = current_version.Build[:0]
|
|
|
|
// give warning to update the daemon
|
|
if u.Message != "" && err == nil { // check semver
|
|
if current_version.LT(uversion) {
|
|
if current_version.Major != uversion.Major { // if major version is different give extract warning
|
|
logger.Info("\033[31m CRITICAL MAJOR update, please upgrade ASAP.\033[0m")
|
|
}
|
|
|
|
logger.Info(fmt.Sprintf("%s", u.Message)) // give the version upgrade message
|
|
logger.Info(fmt.Sprintf("\033[33mCurrent Version %s \033[32m-> Upgrade Version %s\033[0m ", current_version.String(), uversion.String()))
|
|
}
|
|
}
|
|
|
|
if u.Critical != "" { // give the critical upgrade message
|
|
logger.Info(fmt.Sprintf("%s", u.Critical))
|
|
}
|
|
|
|
}
|