@@ -3,19 +3,24 @@ package main
33import (
44 "bufio"
55 "encoding/binary"
6+ "encoding/json"
67 "flag"
78 "fmt"
89 "gopkg.in/yaml.v2"
10+ "io"
11+ "io/ioutil"
912 "log"
1013 "net"
14+ "net/http"
1115 "os"
16+ "os/exec"
17+ "path/filepath"
18+ "runtime"
1219 "strconv"
1320 "strings"
1421 "time"
1522)
1623
17- const version = "0.0.1"
18-
1924const relayHost = "delthas.fr:14761"
2025
2126const defaultPort = 41254
@@ -228,23 +233,174 @@ func server(port int) {
228233 }
229234}
230235
236+ func update (scanner * bufio.Scanner ) bool {
237+ httpClient := http.Client {Timeout : 2 * time .Second }
238+ r , err := httpClient .Get ("https://api.github.com/repos/delthas/proxypunch/releases" )
239+ if err != nil {
240+ // throw error even if the user is just disconnected from the internet
241+ fmt .Fprintln (os .Stderr , "Error while looking for updates: " + err .Error ())
242+ return false
243+ }
244+ var releases []struct {
245+ TagName string `json:"tag_name"`
246+ Name string `json:"name"`
247+ Assets []struct {
248+ Name string `json:"name"`
249+ DownloadUrl string `json:"browser_download_url"`
250+ } `json:"assets"`
251+ }
252+ decoder := json .NewDecoder (r .Body )
253+ err = decoder .Decode (& releases )
254+ r .Body .Close ()
255+ if err != nil {
256+ fmt .Fprintln (os .Stderr , "Error while processing updates list: " + err .Error ())
257+ return false
258+ }
259+ for _ , v := range releases {
260+ if v .TagName == ProgramVersion {
261+ return false
262+ }
263+ for _ , asset := range v .Assets {
264+ if strings .Contains (asset .Name , ProgramArch ) {
265+ update := ""
266+ for update != "y" && update != "yes" && update != "n" && update != "no" {
267+ fmt .Println ("proxypunch update " + v .Name + " is available! Download and update now? y(es) / n(o) [yes]" )
268+ if ! scanner .Scan () {
269+ return false
270+ }
271+ update = strings .ToLower (scanner .Text ())
272+ if update == "" {
273+ update = "y"
274+ }
275+ }
276+ if update != "y" && update != "yes" {
277+ return false
278+ }
279+ r , err = httpClient .Get (asset .DownloadUrl )
280+ if err != nil {
281+ // throw error even if the user is just disconnected from the internet
282+ fmt .Fprintln (os .Stderr , "Error while downloading update (http get): " + err .Error ())
283+ return false
284+ }
285+ f , err := ioutil .TempFile ("" , "" )
286+ if err != nil {
287+ r .Body .Close ()
288+ // throw error even if the user is just disconnected from the internet
289+ fmt .Fprintln (os .Stderr , "Error while downloading update (file open): " + err .Error ())
290+ return false
291+ }
292+ _ , err = io .Copy (f , r .Body )
293+ r .Body .Close ()
294+ f .Close ()
295+ if err != nil {
296+ // throw error even if the user is just disconnected from the internet
297+ fmt .Fprintln (os .Stderr , "Error while downloading update (io copy): " + err .Error ())
298+ return false
299+ }
300+
301+ exe , err := os .Executable ()
302+ if err != nil {
303+ fmt .Fprintln (os .Stderr , "Error while downloading update (exe path get): " + err .Error ())
304+ return false
305+ }
306+ exe , err = filepath .EvalSymlinks (exe )
307+ if err != nil {
308+ fmt .Fprintln (os .Stderr , "Error while downloading update (exe path eval): " + err .Error ())
309+ return false
310+ }
311+
312+ var perm os.FileMode
313+ if info , err := os .Stat (exe ); err != nil {
314+ perm = info .Mode ()
315+ } else {
316+ perm = 0777
317+ }
318+
319+ if runtime .GOOS == "windows" {
320+ err = os .Rename (exe , "proxypunch_old.exe" )
321+ if err != nil {
322+ fmt .Fprintln (os .Stderr , "Error while downloading update (move current file): " + err .Error ())
323+ return false
324+ }
325+ } else {
326+ err = os .Remove (exe )
327+ if err != nil {
328+ fmt .Fprintln (os .Stderr , "Error while downloading update (unlink current file): " + err .Error ())
329+ return false
330+ }
331+ }
332+
333+ w , err := os .OpenFile (exe , os .O_RDWR | os .O_CREATE | os .O_TRUNC , perm )
334+ if err != nil {
335+ fmt .Fprintln (os .Stderr , "Error while downloading update (create new file): " + err .Error ())
336+ return false
337+ }
338+
339+ r , err := os .Open (f .Name ())
340+ if err != nil {
341+ w .Close ()
342+ fmt .Fprintln (os .Stderr , "Error while downloading update (open update file): " + err .Error ())
343+ return false
344+ }
345+
346+ _ , err = io .Copy (w , r )
347+ r .Close ()
348+ w .Close ()
349+ if err != nil {
350+ fmt .Fprintln (os .Stderr , "Error while downloading update (copy update file): " + err .Error ())
351+ return false
352+ }
353+
354+ cmd := exec .Command (exe , os .Args [1 :]... )
355+ cmd .Stdin = os .Stdin
356+ cmd .Stdout = os .Stdout
357+ cmd .Stderr = os .Stderr
358+ cmd .Run ()
359+ return true
360+ }
361+ }
362+ }
363+ return false
364+ }
365+
366+ var ProgramVersion string
367+ var ProgramArch string
368+
231369func main () {
232- fmt .Println ("ProxyPunch v" + version )
370+ if ProgramVersion == "" {
371+ ProgramVersion = "[Custom Build]"
372+ }
373+ fmt .Println ("ProxyPunch " + ProgramVersion + " by delthas" )
233374 fmt .Println ()
234375
376+ if runtime .GOOS == "windows" {
377+ // cleanup old update file, ignore error
378+ os .Remove ("proxypunch_old.exe" )
379+ }
380+
235381 var mode string
236382 var host string
237383 var port int
238384 var noSave bool
385+ var noUpdate bool
239386 var configFile string
240387
241388 flag .StringVar (& mode , "mode" , "" , "connect mode: server, client" )
242389 flag .StringVar (& host , "host" , "" , "remote host for client mode: ipv4 or ipv6 or hostname" )
243390 flag .IntVar (& port , "port" , 0 , "port for client or server mode" )
244391 flag .BoolVar (& noSave , "nosave" , false , "disable saving configuration to file" )
392+ flag .BoolVar (& noUpdate , "noupdate" , false , "disable automatic update" )
245393 flag .StringVar (& configFile , "config" , "proxypunch.yml" , "load configuration from file" )
246394 flag .Parse ()
247395
396+ scanner := bufio .NewScanner (os .Stdin )
397+
398+ if ! noUpdate && ProgramArch != "" && ProgramVersion != "[Custom Build]" {
399+ if update (scanner ) {
400+ return
401+ }
402+ }
403+
248404 var config Config
249405
250406 noConfig := (mode == "server" && port != 0 ) || (mode == "client" && host != "" && port != 0 )
@@ -277,8 +433,6 @@ func main() {
277433 saveHost := host == ""
278434 savePort := port == 0
279435
280- scanner := bufio .NewScanner (os .Stdin )
281-
282436 for mode != "s" && mode != "server" && mode != "c" && mode != "client" {
283437 if config .Mode != "" {
284438 fmt .Println ("Mode? s(erver) / c(lient) [" + config .Mode + "]" )
0 commit comments