@@ -3,19 +3,24 @@ package main
3
3
import (
4
4
"bufio"
5
5
"encoding/binary"
6
+ "encoding/json"
6
7
"flag"
7
8
"fmt"
8
9
"gopkg.in/yaml.v2"
10
+ "io"
11
+ "io/ioutil"
9
12
"log"
10
13
"net"
14
+ "net/http"
11
15
"os"
16
+ "os/exec"
17
+ "path/filepath"
18
+ "runtime"
12
19
"strconv"
13
20
"strings"
14
21
"time"
15
22
)
16
23
17
- const version = "0.0.1"
18
-
19
24
const relayHost = "delthas.fr:14761"
20
25
21
26
const defaultPort = 41254
@@ -228,23 +233,174 @@ func server(port int) {
228
233
}
229
234
}
230
235
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
+
231
369
func main () {
232
- fmt .Println ("ProxyPunch v" + version )
370
+ if ProgramVersion == "" {
371
+ ProgramVersion = "[Custom Build]"
372
+ }
373
+ fmt .Println ("ProxyPunch " + ProgramVersion + " by delthas" )
233
374
fmt .Println ()
234
375
376
+ if runtime .GOOS == "windows" {
377
+ // cleanup old update file, ignore error
378
+ os .Remove ("proxypunch_old.exe" )
379
+ }
380
+
235
381
var mode string
236
382
var host string
237
383
var port int
238
384
var noSave bool
385
+ var noUpdate bool
239
386
var configFile string
240
387
241
388
flag .StringVar (& mode , "mode" , "" , "connect mode: server, client" )
242
389
flag .StringVar (& host , "host" , "" , "remote host for client mode: ipv4 or ipv6 or hostname" )
243
390
flag .IntVar (& port , "port" , 0 , "port for client or server mode" )
244
391
flag .BoolVar (& noSave , "nosave" , false , "disable saving configuration to file" )
392
+ flag .BoolVar (& noUpdate , "noupdate" , false , "disable automatic update" )
245
393
flag .StringVar (& configFile , "config" , "proxypunch.yml" , "load configuration from file" )
246
394
flag .Parse ()
247
395
396
+ scanner := bufio .NewScanner (os .Stdin )
397
+
398
+ if ! noUpdate && ProgramArch != "" && ProgramVersion != "[Custom Build]" {
399
+ if update (scanner ) {
400
+ return
401
+ }
402
+ }
403
+
248
404
var config Config
249
405
250
406
noConfig := (mode == "server" && port != 0 ) || (mode == "client" && host != "" && port != 0 )
@@ -277,8 +433,6 @@ func main() {
277
433
saveHost := host == ""
278
434
savePort := port == 0
279
435
280
- scanner := bufio .NewScanner (os .Stdin )
281
-
282
436
for mode != "s" && mode != "server" && mode != "c" && mode != "client" {
283
437
if config .Mode != "" {
284
438
fmt .Println ("Mode? s(erver) / c(lient) [" + config .Mode + "]" )
0 commit comments