Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update dmsgweb implementation #264

Merged
merged 4 commits into from
May 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
237 changes: 196 additions & 41 deletions cmd/dmsgweb/commands/dmsgweb.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,14 @@ import (
"os/signal"
"path/filepath"
"regexp"
"runtime"
"strconv"
"strings"
"sync"
"syscall"
"time"

"github.com/bitfield/script"
"github.com/confiant-inc/go-socks5"
"github.com/gin-gonic/gin"
"github.com/skycoin/skywire-utilities/pkg/buildinfo"
Expand All @@ -32,17 +35,6 @@ import (
"github.com/skycoin/dmsg/pkg/dmsghttp"
)

// RootCmd contains commands that interact with the config of local skywire-visor
var genKeysCmd = &cobra.Command{
Use: "gen-keys",
Short: "generate public / secret keypair",
Run: func(cmd *cobra.Command, args []string) {
pk, sk := cipher.GenerateKeyPair()
fmt.Println(pk)
fmt.Println(sk)
},
}

type customResolver struct{}

func (r *customResolver) Resolve(ctx context.Context, name string) (context.Context, net.IP, error) {
Expand All @@ -55,7 +47,7 @@ func (r *customResolver) Resolve(ctx context.Context, name string) (context.Cont
return ctx, nil, fmt.Errorf("failed to parse IP address")
}
// Modify the context to include the desired port
ctx = context.WithValue(ctx, "port", webPort) //nolint
ctx = context.WithValue(ctx, "port", strconv.Itoa(webPort)) //nolint
return ctx, ip, nil
}
// Use default name resolution for other domains
Expand All @@ -70,27 +62,37 @@ var (
sk cipher.SecKey
dmsgWebLog *logging.Logger
logLvl string
webPort string
proxyPort string
webPort int
proxyPort uint
addProxy string
resolveDmsgAddr string
wg sync.WaitGroup
isEnvs bool
)

const envname = "DMSGWEB"

var envfile = os.Getenv(envname)

func init() {
RootCmd.AddCommand(genKeysCmd)

RootCmd.Flags().StringVarP(&filterDomainSuffix, "filter", "f", ".dmsg", "domain suffix to filter")
RootCmd.Flags().StringVarP(&proxyPort, "socks", "q", "4445", "port to serve the socks5 proxy")
RootCmd.Flags().StringVarP(&addProxy, "proxy", "r", "", "configure additional socks5 proxy for dmsgweb (i.e. 127.0.0.1:1080)")
RootCmd.Flags().StringVarP(&webPort, "port", "p", "8080", "port to serve the web application")
RootCmd.Flags().StringVarP(&resolveDmsgAddr, "resolve", "t", "", "resolve the specified dmsg address:port on the local port & disable proxy")
RootCmd.Flags().StringVarP(&dmsgDisc, "dmsg-disc", "d", "", "dmsg discovery url default:\n"+skyenv.DmsgDiscAddr)
RootCmd.Flags().IntVarP(&dmsgSessions, "sess", "e", 1, "number of dmsg servers to connect to")
RootCmd.Flags().UintVarP(&proxyPort, "socks", "q", scriptExecUint("${PROXYPORT:-4445}"), "port to serve the socks5 proxy")
RootCmd.Flags().StringVarP(&addProxy, "proxy", "r", scriptExecString("${ADDPROXY}"), "configure additional socks5 proxy for dmsgweb (i.e. 127.0.0.1:1080)")
RootCmd.Flags().IntVarP(&webPort, "port", "p", scriptExecInt("${WEBPORT:-8080}"), "port to serve the web application")
RootCmd.Flags().StringVarP(&resolveDmsgAddr, "resolve", "t", scriptExecString("${RESOLVEPK}"), "resolve the specified dmsg address:port on the local port & disable proxy")
RootCmd.Flags().StringVarP(&dmsgDisc, "dmsg-disc", "d", skyenv.DmsgDiscAddr, "dmsg discovery url")
RootCmd.Flags().IntVarP(&dmsgSessions, "sess", "e", scriptExecInt("${DMSGSESSIONS:-1}"), "number of dmsg servers to connect to")
RootCmd.Flags().StringVarP(&logLvl, "loglvl", "l", "", "[ debug | warn | error | fatal | panic | trace | info ]\033[0m")
if os.Getenv("DMSGGET_SK") != "" {
sk.Set(os.Getenv("DMSGGET_SK")) //nolint
if os.Getenv("DMSGWEB_SK") != "" {
sk.Set(os.Getenv("DMSGWEB_SK")) //nolint
}
if scriptExecString("${DMSGWEB_SK}") != "" {
sk.Set(scriptExecString("${DMSGWEB_SK}")) //nolint
}
RootCmd.Flags().VarP(&sk, "sk", "s", "a random key is generated if unspecified\n\r")
RootCmd.Flags().BoolVarP(&isEnvs, "envs", "z", false, "show example .conf file")

}

// RootCmd contains the root command for dmsgweb
Expand All @@ -103,13 +105,30 @@ var RootCmd = &cobra.Command{
┌┬┐┌┬┐┌─┐┌─┐┬ ┬┌─┐┌┐
│││││└─┐│ ┬│││├┤ ├┴┐
─┴┘┴ ┴└─┘└─┘└┴┘└─┘└─┘
DMSG resolving proxy & browser client - access websites over dmsg`,
DMSG resolving proxy & browser client - access websites over dmsg` + func() string {
if _, err := os.Stat(envfile); err == nil {
return `
dmsgweb env file detected: ` + envfile
}
return `
.conf file may also be specified with
` + envname + `=/path/to/dmsgweb.conf skywire dmsg web`
}(),
SilenceErrors: true,
SilenceUsage: true,
DisableSuggestions: true,
DisableFlagsInUseLine: true,
Version: buildinfo.Version(),
Run: func(cmd *cobra.Command, _ []string) {
if isEnvs {
if runtime.GOOS == "windows" {
envfile = envfileWindows
} else {
envfile = envfileLinux
}
fmt.Println(envfile)
os.Exit(0)
}
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt, syscall.SIGTERM) //nolint
go func() {
Expand Down Expand Up @@ -168,7 +187,7 @@ DMSG resolving proxy & browser client - access websites over dmsg`,
if match {
port, ok := ctx.Value("port").(string)
if !ok {
port = webPort
port = strconv.Itoa(webPort)
}
addr = "localhost:" + port
} else {
Expand All @@ -187,7 +206,7 @@ DMSG resolving proxy & browser client - access websites over dmsg`,
}

// Start the SOCKS5 server
socksAddr := "127.0.0.1:" + proxyPort
socksAddr := fmt.Sprintf("127.0.0.1:%v", proxyPort)
log.Printf("SOCKS5 proxy server started on %s", socksAddr)

server, err := socks5.New(conf)
Expand Down Expand Up @@ -227,36 +246,25 @@ DMSG resolving proxy & browser client - access websites over dmsg`,
}
urlStr = fmt.Sprintf("dmsg://%s:%s%s", strings.TrimRight(hostParts[0], filterDomainSuffix), dmsgp, c.Param("path"))
}

maxSize := int64(1024)

req, err := http.NewRequest(http.MethodGet, urlStr, nil)
if err != nil {
c.String(http.StatusInternalServerError, "Failed to create HTTP request")
return
}

resp, err := httpC.Do(req)
if err != nil {
c.String(http.StatusInternalServerError, "Failed to connect to HTTP server")
return
}
defer resp.Body.Close() //nolint

if maxSize > 0 {
if resp.ContentLength > maxSize*1024 {
c.String(http.StatusRequestEntityTooLarge, "Requested file size exceeds the allowed limit")
return
}
}
c.Status(http.StatusOK)
io.Copy(c.Writer, resp.Body) //nolint
})
wg.Add(1)
go func() {
dmsgWebLog.Debug("Serving http on " + webPort)
r.Run(":" + webPort) //nolint
dmsgWebLog.Debug("Stopped serving http on " + webPort)
dmsgWebLog.Debug(fmt.Sprintf("Serving http on: http://127.0.0.1:%v", webPort))
r.Run(":" + strconv.Itoa(webPort)) //nolint
dmsgWebLog.Debug(fmt.Sprintf("Stopped serving http on: http://127.0.0.1:%v", webPort))
wg.Done()
}()
wg.Wait()
Expand Down Expand Up @@ -304,7 +312,7 @@ func loggingMiddleware() gin.HandlerFunc {
methodColor := getMethodColor(method)
// Print the logging in a custom format which includes the publickeyfrom c.Request.RemoteAddr ex.:
// [DMSGHTTP] 2023/05/18 - 19:43:15 | 200 | 10.80885ms | | 02b5ee5333aa6b7f5fc623b7d5f35f505cb7f974e98a70751cf41962f84c8c4637:49153 | GET /node-info.json
fmt.Printf("[DMSGHTTP] %s |%s %3d %s| %13v | %15s | %72s |%s %-7s %s %s\n",
fmt.Printf("[DMSGWEB] %s |%s %3d %s| %13v | %15s | %72s |%s %-7s %s %s\n",
time.Now().Format("2006/01/02 - 15:04:05"),
statusCodeBackgroundColor,
statusCode,
Expand Down Expand Up @@ -374,3 +382,150 @@ func Execute() {
log.Fatal("Failed to execute command: ", err)
}
}

func scriptExecString(s string) string {
if runtime.GOOS == "windows" {
var variable, defaultvalue string
if strings.Contains(s, ":-") {
parts := strings.SplitN(s, ":-", 2)
variable = parts[0] + "}"
defaultvalue = strings.TrimRight(parts[1], "}")
} else {
variable = s
defaultvalue = ""
}
out, err := script.Exec(fmt.Sprintf(`powershell -c '$SKYENV = "%s"; if ($SKYENV -ne "" -and (Test-Path $SKYENV)) { . $SKYENV }; echo %s"`, envfile, variable)).String()
if err == nil {
if (out == "") || (out == variable) {
return defaultvalue
}
return strings.TrimRight(out, "\n")
}
return defaultvalue
}
z, err := script.Exec(fmt.Sprintf(`sh -c 'SKYENV=%s ; if [[ $SKYENV != "" ]] && [[ -f $SKYENV ]] ; then source $SKYENV ; fi ; printf "%s"'`, envfile, s)).String()
if err == nil {
return strings.TrimSpace(z)
}
return ""
}

func scriptExecInt(s string) int {
if runtime.GOOS == "windows" {
var variable string
if strings.Contains(s, ":-") {
parts := strings.SplitN(s, ":-", 2)
variable = parts[0] + "}"
} else {
variable = s
}
out, err := script.Exec(fmt.Sprintf(`powershell -c '$SKYENV = "%s"; if ($SKYENV -ne "" -and (Test-Path $SKYENV)) { . $SKYENV }; echo %s"`, envfile, variable)).String()
if err == nil {
if (out == "") || (out == variable) {
return 0
}
i, err := strconv.Atoi(strings.TrimSpace(strings.TrimRight(out, "\n")))
if err == nil {
return i
}
return 0
}
return 0
}
z, err := script.Exec(fmt.Sprintf(`sh -c 'SKYENV=%s ; if [[ $SKYENV != "" ]] && [[ -f $SKYENV ]] ; then source $SKYENV ; fi ; printf "%s"'`, envfile, s)).String()
if err == nil {
if z == "" {
return 0
}
i, err := strconv.Atoi(z)
if err == nil {
return i
}
}
return 0
}
func scriptExecUint(s string) uint {
if runtime.GOOS == "windows" {
var variable string
if strings.Contains(s, ":-") {
parts := strings.SplitN(s, ":-", 2)
variable = parts[0] + "}"
} else {
variable = s
}
out, err := script.Exec(fmt.Sprintf(`powershell -c '$SKYENV = "%s"; if ($SKYENV -ne "" -and (Test-Path $SKYENV)) { . $SKYENV }; echo %s"`, envfile, variable)).String()
if err == nil {
if (out == "") || (out == variable) {
return 0
}
i, err := strconv.Atoi(strings.TrimSpace(strings.TrimRight(out, "\n")))
if err == nil {
return uint(i)
}
return 0
}
return 0
}
z, err := script.Exec(fmt.Sprintf(`sh -c 'SKYENV=%s ; if [[ $SKYENV != "" ]] && [[ -f $SKYENV ]] ; then source $SKYENV ; fi ; printf "%s"'`, envfile, s)).String()
if err == nil {
if z == "" {
return 0
}
i, err := strconv.Atoi(z)
if err == nil {
return uint(i)
}
}
return uint(0)
}

const envfileLinux = `
#########################################################################
# DMSGWEB CONFIG TEMPLATE
# Defaults shown
# Uncomment to change default value
#########################################################################

#-- Set port for proxy interface
#PROXYPORT=4445

#-- Configure additional proxy for dmsgvlc to use
#ADDPROXY='127.0.0.1:1080'

#-- Web Interface Port
#WEBPORT=8080

#-- Resove a specific PK to the web port (also disables proxy)
#RESOLVEPK=''

#-- Number of dmsg servers to connect to (0 unlimits)
#DMSGSESSIONS=1

#-- Set secret key
#DMSGWEB_SK=''
`
const envfileWindows = `
#########################################################################
# DMSGWEB CONFIG TEMPLATE
# Defaults shown
# Uncomment to change default value
#########################################################################

#-- Set port for proxy interface
#$PROXYPORT=4445

#-- Configure additional proxy for dmsgvlc to use
#$ADDPROXY='127.0.0.1:1080'

#-- Web Interface Port
#$WEBPORT=8080

#-- Resove a specific PK to the web port (also disables proxy)
#$RESOLVEPK=''

#-- Number of dmsg servers to connect to (0 unlimits)
#$DMSGSESSIONS=1

#-- Set secret key
#$DMSGWEB_SK=''
`
11 changes: 6 additions & 5 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@ toolchain go1.21.4
require (
github.com/ActiveState/termtest/conpty v0.5.0
github.com/VictoriaMetrics/metrics v1.18.1
github.com/bitfield/script v0.22.1
github.com/confiant-inc/go-socks5 v0.0.0-20210816151940-c1124825b1d6
github.com/creack/pty v1.1.15
github.com/creack/pty v1.1.18
github.com/gin-gonic/gin v1.9.1
github.com/go-chi/chi/v5 v5.0.11
github.com/go-redis/redis/v8 v8.11.5
Expand All @@ -23,7 +24,7 @@ require (
github.com/spf13/cobra v1.4.0
github.com/stretchr/testify v1.8.3
golang.org/x/net v0.10.0
golang.org/x/sys v0.8.0
golang.org/x/sys v0.10.0
golang.org/x/term v0.8.0
nhooyr.io/websocket v1.8.2
)
Expand All @@ -42,11 +43,11 @@ require (
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.14.0 // indirect
github.com/goccy/go-json v0.10.2 // indirect
github.com/google/go-cmp v0.5.7 // indirect
github.com/inconshreveable/mousetrap v1.0.0 // indirect
github.com/itchyny/gojq v0.12.13 // indirect
github.com/itchyny/timefmt-go v0.1.5 // indirect
github.com/klauspost/compress v1.11.0 // indirect
github.com/klauspost/cpuid/v2 v2.2.4 // indirect
github.com/kr/pretty v0.3.0 // indirect
github.com/leodido/go-urn v1.2.4 // indirect
github.com/mattn/go-colorable v0.1.12 // indirect
github.com/mattn/go-isatty v0.0.19 // indirect
Expand All @@ -55,7 +56,6 @@ require (
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/pelletier/go-toml/v2 v2.0.8 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/rogpeppe/go-internal v1.8.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.2.11 // indirect
Expand All @@ -67,6 +67,7 @@ require (
google.golang.org/protobuf v1.30.0 // indirect
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
mvdan.cc/sh/v3 v3.7.0 // indirect
)

// Uncomment for tests with alternate branches of 'skywire-utilities'
Expand Down
Loading
Loading