github.com/deroproject/derosuite@v2.1.6-1.0.20200307070847-0f2e589c7a2b+incompatible/cmd/derod/update.go (about) 1 package main 2 3 //import "fmt" 4 import "net" 5 import "time" 6 import "io" 7 8 //import "io/ioutil" 9 //import "net/http" 10 import "context" 11 import "strings" 12 import "math/rand" 13 import "encoding/base64" 14 import "encoding/json" 15 import "runtime/debug" 16 import "encoding/binary" 17 18 //import "crypto/tls" 19 20 import "github.com/blang/semver" 21 import "github.com/miekg/dns" 22 import "github.com/romana/rlog" 23 24 import "github.com/deroproject/derosuite/config" 25 import "github.com/deroproject/derosuite/globals" 26 27 /* this needs to be set on update.dero.io. as TXT record, in encoded form as base64 28 * 29 { "version" : "1.0.2", 30 "message" : "\n\n\u001b[32m This is a mandatory update\u001b[0m", 31 "critical" : "" 32 } 33 34 base64 eyAidmVyc2lvbiIgOiAiMS4wLjIiLAogIm1lc3NhZ2UiIDogIlxuXG5cdTAwMWJbMzJtIFRoaXMgaXMgYSBtYW5kYXRvcnkgdXBkYXRlXHUwMDFiWzBtIiwgCiJjcml0aWNhbCIgOiAiIiAKfQ== 35 36 37 38 TXT record should be set as update=eyAidmVyc2lvbiIgOiAiMS4wLjIiLAogIm1lc3NhZ2UiIDogIlxuXG5cdTAwMWJbMzJtIFRoaXMgaXMgYSBtYW5kYXRvcnkgdXBkYXRlXHUwMDFiWzBtIiwgCiJjcml0aWNhbCIgOiAiIiAKfQ== 39 */ 40 41 func check_update_loop() { 42 43 for { 44 45 if config.DNS_NOTIFICATION_ENABLED { 46 47 globals.Logger.Debugf("Checking update..") 48 check_update() 49 } 50 time.Sleep(2 * 3600 * time.Second) // check every 2 hours 51 } 52 53 } 54 55 // wrapper to make requests using proxy 56 func dialContextwrapper(ctx context.Context, network, address string) (net.Conn, error) { 57 return globals.Dialer.Dial(network, address) 58 } 59 60 type socks_dialer net.Dialer 61 62 func (d *socks_dialer) Dial(network, address string) (net.Conn, error) { 63 globals.Logger.Infof("Using our dial") 64 return globals.Dialer.Dial(network, address) 65 } 66 67 func (d *socks_dialer) DialContext(ctx context.Context, network, address string) (net.Conn, error) { 68 globals.Logger.Infof("Using our context dial") 69 return globals.Dialer.Dial(network, address) 70 } 71 72 func dial_random_read_response(in []byte) (out []byte, err error) { 73 defer func() { 74 if r := recover(); r != nil { 75 rlog.Warnf("Recovered while checking updates, Stack trace below", r) 76 rlog.Warnf("Stack trace \n%s", debug.Stack()) 77 } 78 }() 79 80 // since we may be connecting through socks, grab the remote ip for our purpose rightnow 81 //conn, err := globals.Dialer.Dial("tcp", "208.67.222.222:53") 82 //conn, err := net.Dial("tcp", "8.8.8.8:53") 83 random_feeder := rand.New(globals.NewCryptoRandSource()) // use crypto secure resource 84 server_address := config.DNS_servers[random_feeder.Intn(len(config.DNS_servers))] // choose a random server cryptographically 85 conn, err := net.Dial("tcp", server_address) 86 87 //conn, err := tls.Dial("tcp", remote_ip.String(),&tls.Config{InsecureSkipVerify: true}) 88 if err != nil { 89 rlog.Warnf("Dial failed err %s", err.Error()) 90 return 91 } 92 93 defer conn.Close() // close connection at end 94 95 // upgrade connection TO TLS ( tls.Dial does NOT support proxy) 96 //conn = tls.Client(conn, &tls.Config{InsecureSkipVerify: true}) 97 98 rlog.Tracef(1, "Sending %d bytes", len(in)) 99 100 var buf [2]byte 101 binary.BigEndian.PutUint16(buf[:], uint16(len(in))) 102 conn.Write(buf[:]) // write length in bigendian format 103 104 conn.Write(in) // write data 105 106 // now we must wait for response to arrive 107 var frame_length_buf [2]byte 108 109 conn.SetReadDeadline(time.Now().Add(20 * time.Second)) 110 nbyte, err := io.ReadFull(conn, frame_length_buf[:]) 111 if err != nil || nbyte != 2 { 112 // error while reading from connection we must disconnect it 113 rlog.Warnf("Could not read DNS length prefix err %s", err) 114 return 115 } 116 117 frame_length := binary.BigEndian.Uint16(frame_length_buf[:]) 118 if frame_length == 0 { 119 // most probably memory DDOS attack, kill the connection 120 rlog.Warnf("Frame length is too small") 121 return 122 } 123 124 out = make([]byte, frame_length) 125 126 conn.SetReadDeadline(time.Now().Add(20 * time.Second)) 127 data_size, err := io.ReadFull(conn, out) 128 if err != nil || data_size <= 0 || uint16(data_size) != frame_length { 129 // error while reading from connection we must kiil it 130 rlog.Warnf("Could not read DNS data size read %d, frame length %d err %s", data_size, frame_length, err) 131 return 132 133 } 134 out = out[:frame_length] 135 136 return 137 } 138 139 func check_update() { 140 141 // add panic handler, in case DNS acts rogue and tries to attack 142 defer func() { 143 if r := recover(); r != nil { 144 rlog.Warnf("Recovered while checking updates, Stack trace below", r) 145 rlog.Warnf("Stack trace \n%s", debug.Stack()) 146 } 147 }() 148 149 if !config.DNS_NOTIFICATION_ENABLED { // if DNS notifications are disabled bail out 150 return 151 } 152 153 /* var u update_message 154 u.Version = "2.0.0" 155 u.Message = "critical msg txt\x1b[35m should \n be in RED" 156 157 globals.Logger.Infof("testing %s",u.Message) 158 159 j,err := json.Marshal(u) 160 globals.Logger.Infof("json format %s err %s",j,err) 161 */ 162 /*extract_parse_version("update=eyAidmVyc2lvbiIgOiAiMS4xLjAiLCAibWVzc2FnZSIgOiAiXG5cblx1MDAxYlszMm0gVGhpcyBpcyBhIG1hbmRhdG9yeSB1cGdyYWRlIHBsZWFzZSB1cGdyYWRlIGZyb20geHl6IFx1MDAxYlswbSIsICJjcml0aWNhbCIgOiAiIiB9") 163 164 return 165 */ 166 167 m1 := new(dns.Msg) 168 // m1.SetEdns0(65000, true), dnssec probably leaks current timestamp, it's disabled until more invetigation 169 m1.Id = dns.Id() 170 m1.RecursionDesired = true 171 m1.Question = make([]dns.Question, 1) 172 m1.Question[0] = dns.Question{config.DNS_UPDATE_CHECK, dns.TypeTXT, dns.ClassINET} 173 174 packed, err := m1.Pack() 175 if err != nil { 176 globals.Logger.Warnf("Error which packing DNS query for program update err %s", err) 177 return 178 } 179 180 /* 181 182 // setup a http client 183 httpTransport := &http.Transport{} 184 httpClient := &http.Client{Transport: httpTransport} 185 // set our socks5 as the dialer 186 httpTransport.Dial = globals.Dialer.Dial 187 188 189 190 packed_base64:= base64.RawURLEncoding.EncodeToString(packed) 191 response, err := httpClient.Get("https://1.1.1.1/dns-query?ct=application/dns-udpwireformat&dns="+packed_base64) 192 193 _ = packed_base64 194 195 if err != nil { 196 rlog.Warnf("error making DOH request err %s",err) 197 return 198 } 199 200 defer response.Body.Close() 201 contents, err := ioutil.ReadAll(response.Body) 202 if err != nil { 203 rlog.Warnf("error reading DOH response err %s",err) 204 return 205 } 206 */ 207 208 contents, err := dial_random_read_response(packed) 209 if err != nil { 210 rlog.Warnf("error reading response from DNS server err %s", err) 211 return 212 213 } 214 215 rlog.Debugf("DNS response length from DNS server %d bytes", len(contents)) 216 217 err = m1.Unpack(contents) 218 if err != nil { 219 rlog.Warnf("error decoding DOH response err %s", err) 220 return 221 222 } 223 224 for i := range m1.Answer { 225 if t, ok := m1.Answer[i].(*dns.TXT); ok { 226 227 // replace any spaces so as records could be joined 228 229 rlog.Tracef(1, "Process record %+v", t.Txt) 230 joined := strings.Join(t.Txt, "") 231 extract_parse_version(joined) 232 233 } 234 } 235 236 //globals.Logger.Infof("response %+v err ",m1,err) 237 238 } 239 240 type update_message struct { 241 Version string `json:"version"` 242 Message string `json:"message"` 243 Critical string `json:"critical"` // always broadcasted, without checks for version 244 } 245 246 // our version are TXT record of following format 247 // version=base64 encoded json 248 func extract_parse_version(str string) { 249 250 strl := strings.ToLower(str) 251 if !strings.HasPrefix(strl, "update=") { 252 rlog.Tracef(1, "Skipping record %s", str) 253 return 254 } 255 256 parts := strings.SplitN(str, "=", 2) 257 if len(parts) != 2 { 258 return 259 } 260 rlog.Tracef(1, "parts %s", parts[1]) 261 262 data, err := base64.StdEncoding.DecodeString(parts[1]) 263 if err != nil { 264 rlog.Tracef(1, "Could NOT decode base64 update message %s", err) 265 return 266 } 267 268 var u update_message 269 err = json.Unmarshal(data, &u) 270 271 //globals.Logger.Infof("data %+v", u) 272 273 if err != nil { 274 rlog.Tracef(1, "Could NOT decode json update message %s", err) 275 return 276 } 277 278 uversion, err := semver.ParseTolerant(u.Version) 279 if err != nil { 280 rlog.Tracef(1, "Could NOT update version %s", err) 281 } 282 283 current_version := config.Version 284 current_version.Pre = current_version.Pre[:0] 285 current_version.Build = current_version.Build[:0] 286 287 // give warning to update the daemon 288 if u.Message != "" && err == nil { // check semver 289 if current_version.LT(uversion) { 290 if current_version.Major != uversion.Major { // if major version is different give extract warning 291 globals.Logger.Infof("\033[31m CRITICAL MAJOR update, please upgrade ASAP.\033[0m") 292 } 293 294 globals.Logger.Infof("%s", u.Message) // give the version upgrade message 295 globals.Logger.Infof("\033[33mCurrent Version %s \033[32m-> Upgrade Version %s\033[0m ", current_version.String(), uversion.String()) 296 } 297 } 298 299 if u.Critical != "" { // give the critical upgrade message 300 globals.Logger.Infof("%s", u.Critical) 301 } 302 303 }