diff --git a/.gitignore b/.gitignore index c074bb3..28f274e 100644 --- a/.gitignore +++ b/.gitignore @@ -12,6 +12,13 @@ build/windows-custom/NicelandVPN-windows.exe build/darwin-custom/NicelandVPN.app/Contents/MacOS/* build/darwin-custom/NicelandVPN.app/Contents/_CodeSignature/* +cmd/tui/*.exe +cmd/tui/niceland-static-tui +cmd/tui/tui + +cmd/cli/cli +cmd/cli/*.exe + node_modules frontend/dist files diff --git a/cmd/cli/main.go b/cmd/cli/main.go new file mode 100644 index 0000000..d3128e7 --- /dev/null +++ b/cmd/cli/main.go @@ -0,0 +1,139 @@ +package main + +import ( + "fmt" + "log" + "os" + "runtime/debug" + "time" + + "github.com/charmbracelet/bubbles/textinput" + tea "github.com/charmbracelet/bubbletea" + "github.com/muesli/termenv" + "github.com/tunnels-is/nicelandvpn-desktop/cmd/termlib" + "github.com/tunnels-is/nicelandvpn-desktop/core" +) + +const ( + VERSION = "1.1.3" + PRODUCTION = true + ENABLE_INSTERFACE = true +) + +var ( + FLAG_COMMAND string + FLAG_USER string + FLAG_PASSWORD string + MONITOR = make(chan int, 200) + TUI *tea.Program + + user *core.User + output = termenv.NewOutput(os.Stdout) + color = output.ForegroundColor() + bgcolor = output.BackgroundColor() + resetString = output.String("... exiting") + + userLoginInputs = make([]textinput.Model, 4) +) + +func main() { + defer func() { + // This will reset the forground and background of the terminal when exiting + resetString.Foreground(color) + resetString.Background(bgcolor) + fmt.Print("\033[H\033[2J") + fmt.Print("\033[H\033[2J") + fmt.Print("\033[H\033[2J") + fmt.Println(resetString) + }() + + core.PRODUCTION = PRODUCTION + core.ENABLE_INSTERFACE = ENABLE_INSTERFACE + core.GLOBAL_STATE.Version = VERSION + + // log.Println(os.Args) + if len(os.Args) < 2 { + os.Exit(1) + } + + switch os.Args[1] { + case "connect": + Connect() + case "getApiKey": + GetAPIKey() + case "createConfig": + CreateDummyConfig() + default: + os.Exit(1) + } + +} + +func GetAPIKey() { + s := termlib.NewSpinner() + go s.Start() + + core.C = new(core.Config) + core.C.DebugLogging = true + core.InitPaths() + core.CreateBaseFolder() + core.InitLogfile() + go core.StartLogQueueProcessor(MONITOR) + err := core.RefreshRouterList() + time.Sleep(2 * time.Second) + s.Stop() + if err != nil { + core.CreateErrorLog("", "Unable to find the best router for your connection: ", err) + os.Exit(1) + } + + fmt.Print("\033[H\033[2J") + termlib.Login(userLoginInputs) + log.Println("USER INPUT:", userLoginInputs[0].Value()) + user = termlib.SendLoginRequest(userLoginInputs) + if user != nil { + log.Println("API KEY:", user.APIKey) + } else { + log.Println("Invalid login..") + } +} + +func CreateDummyConfig() { + +} +func Connect() { + + go core.StartService(MONITOR) + RoutineMonitor() +} + +func RoutineMonitor() { + defer func() { + if r := recover(); r != nil { + core.CreateErrorLog("", r, string(debug.Stack())) + go RoutineMonitor() + } + }() + + for { + select { + default: + time.Sleep(500 * time.Millisecond) + case ID := <-MONITOR: + if ID == 1 { + go core.StateMaintenance(MONITOR) + } else if ID == 2 { + go core.ReadFromRouterSocket(MONITOR) + } else if ID == 3 { + // TUI ONLY .. does not fire on wails GUI + // go TimedUIUpdate(MONITOR) + } else if ID == 4 { + go core.ReadFromLocalSocket(MONITOR) + } else if ID == 6 { + go core.CalculateBandwidth(MONITOR) + } else if ID == 8 { + go core.StartLogQueueProcessor(MONITOR) + } + } + } +} diff --git a/cmd/tui/loginform.go b/cmd/termlib/login.go similarity index 79% rename from cmd/tui/loginform.go rename to cmd/termlib/login.go index 5f5e6b5..9b4824d 100644 --- a/cmd/tui/loginform.go +++ b/cmd/termlib/login.go @@ -1,12 +1,10 @@ -package main +package termlib import ( "encoding/json" "fmt" - "os" "strings" - "github.com/charmbracelet/bubbles/cursor" "github.com/charmbracelet/bubbles/textinput" tea "github.com/charmbracelet/bubbletea" "github.com/tunnels-is/nicelandvpn-desktop/core" @@ -15,26 +13,25 @@ import ( type loginForm struct { focusIndex int inputs []textinput.Model - cursorMode cursor.Mode } -func intialModel() loginForm { +func intialModel(userInputs []textinput.Model) loginForm { m := loginForm{ - inputs: make([]textinput.Model, 4), + inputs: userInputs, } var t textinput.Model for i := range m.inputs { t = textinput.New() - t.Cursor.Style = cursorStyle + t.Cursor.Style = CursorStyle t.CharLimit = 32 switch i { case 0: t.Placeholder = "Email" t.Focus() - t.PromptStyle = focusedStyle - t.TextStyle = focusedStyle + t.PromptStyle = FocusedStyle + t.TextStyle = FocusedStyle case 1: t.Placeholder = "Password" t.EchoMode = textinput.EchoPassword @@ -60,15 +57,14 @@ func (m loginForm) Update(msg tea.Msg) (tea.Model, tea.Cmd) { case tea.KeyMsg: switch msg.String() { case "ctrl+c": - // core.CleanupOnClose() - sendLoginRequest(m.inputs) // I guess this is one way to exit without going into the TUI + // sendLoginRequest(m.inputs) // I guess this is one way to exit without going into the TUI return m, tea.Quit case "tab", "shift-tab", "enter", "up", "down": s := msg.String() // if hit enter while the submit button was focused if s == "enter" && m.focusIndex == len(m.inputs) { - sendLoginRequest(m.inputs) + // sendLoginRequest(m.inputs) return m, tea.Quit } @@ -87,13 +83,13 @@ func (m loginForm) Update(msg tea.Msg) (tea.Model, tea.Cmd) { for i := 0; i <= len(m.inputs)-1; i++ { if i == m.focusIndex { cmds[i] = m.inputs[i].Focus() - m.inputs[i].PromptStyle = focusedStyle - m.inputs[i].TextStyle = focusedStyle + m.inputs[i].PromptStyle = FocusedStyle + m.inputs[i].TextStyle = FocusedStyle continue } m.inputs[i].Blur() - m.inputs[i].PromptStyle = noStyle - m.inputs[i].TextStyle = noStyle + m.inputs[i].PromptStyle = NoStyle + m.inputs[i].TextStyle = NoStyle } return m, tea.Batch(cmds...) } @@ -122,25 +118,25 @@ func (m loginForm) View() string { } } - button := &blurredButton + button := &BlurredButton if m.focusIndex == len(m.inputs) { - button = &focusedButton + button = &FocusedButton } fmt.Fprintf(&b, "\n\n%s\n\n", *button) return b.String() } -func login() { - _, err := tea.NewProgram(intialModel()).Run() +func Login(userInputs []textinput.Model) { + _, err := tea.NewProgram(intialModel(userInputs)).Run() if err != nil { fmt.Printf("Could not start the login form: %s\n", err) - core.CleanupOnClose() - os.Exit(1) + return } + } -func sendLoginRequest(creds []textinput.Model) { +func SendLoginRequest(creds []textinput.Model) (user *core.User) { var FR core.FORWARD_REQUEST // fill the login form @@ -163,14 +159,17 @@ func sendLoginRequest(creds []textinput.Model) { fmt.Println("\nCode: ", code) fmt.Println("Log in error: ", err) core.CleanupOnClose() - os.Exit(1) + return } + user = new(core.User) // unfold it in the user global err = json.Unmarshal(respBytes, &user) if err != nil { fmt.Println("Response error: ", err) core.CleanupOnClose() - os.Exit(1) + return } + + return } diff --git a/cmd/termlib/spinner.go b/cmd/termlib/spinner.go new file mode 100644 index 0000000..81fbfca --- /dev/null +++ b/cmd/termlib/spinner.go @@ -0,0 +1,78 @@ +package termlib + +import ( + "fmt" + + "github.com/charmbracelet/bubbles/spinner" + tea "github.com/charmbracelet/bubbletea" + "github.com/charmbracelet/lipgloss" +) + +var ( + // Available spinners + spinners = []spinner.Spinner{ + spinner.Line, + spinner.Dot, + spinner.MiniDot, + spinner.Jump, + spinner.Pulse, + spinner.Points, + spinner.Globe, + spinner.Moon, + spinner.Monkey, + } + + textStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("252")).Render + spinnerStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("69")) +) + +type model struct { + spinner spinner.Model + Program *tea.Program +} + +func NewSpinner() (m *model) { + m = new(model) + return m +} + +func (m *model) Start() { + m.ResetSpinner() + m.Program = tea.NewProgram(m) + + if _, err := m.Program.Run(); err != nil { + fmt.Println("could not run program:", err) + } +} + +func (m *model) Stop() { + m.Program.Quit() +} + +func (m model) Init() tea.Cmd { + return m.spinner.Tick +} + +func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { + switch msg := msg.(type) { + case tea.QuitMsg: + return m, tea.Quit + case spinner.TickMsg: + var cmd tea.Cmd + m.spinner, cmd = m.spinner.Update(msg) + return m, cmd + default: + return m, nil + } +} + +func (m *model) ResetSpinner() { + m.spinner = spinner.New() + m.spinner.Style = spinnerStyle + m.spinner.Spinner = spinners[6] +} + +func (m model) View() (s string) { + s += fmt.Sprintf("\n %s%s%s\n\n", m.spinner.View(), " ", textStyle("Spinning...")) + return +} diff --git a/cmd/termlib/styles.go b/cmd/termlib/styles.go new file mode 100644 index 0000000..85aab29 --- /dev/null +++ b/cmd/termlib/styles.go @@ -0,0 +1,81 @@ +package termlib + +import ( + "fmt" + + "github.com/charmbracelet/bubbles/table" + "github.com/charmbracelet/lipgloss" +) + +// colors taken from frontend/src/assets/style/variables.scss +var ( + MainBg = lipgloss.Color("#141414") + BodyBg = lipgloss.Color("#202324") + BodyDarkBg = lipgloss.Color("#0A0B0E") + + Teal = lipgloss.Color("#28ad85") + TealBorder = lipgloss.Color("#3AF4BD") + TealHover = lipgloss.Color("#20C997") + + Orange = lipgloss.Color("#FF922D") + OrangeBorder = lipgloss.Color("#EF7503") + OrangeHover = lipgloss.Color("#EF7503") + + LogError = lipgloss.Color("#FF0000") + LogWarning = lipgloss.Color("#FFFF00") + + Lightblue = lipgloss.Color("#20bec9") + Red = lipgloss.Color("#FF5858") + White = lipgloss.Color("#FFFFFF") + Black = lipgloss.Color("#000000") + + SuccessColor = lipgloss.Color("#0AB60A") + ErrorColor = lipgloss.Color("#E70808") +) + +// tab style +var ( + DocStyle = lipgloss.NewStyle().Padding(1, 1, 1, 1) + HighlightColor = TealHover + SelectionColor = Orange + InactiveTabStyle = lipgloss.NewStyle().Border(lipgloss.RoundedBorder(), true, true, false, true).UnsetBorderBottom().BorderForeground(HighlightColor) + ActiveTabStyle = lipgloss.NewStyle().Border(lipgloss.RoundedBorder(), true, true, false, true).UnsetBorderBottom().BorderForeground(HighlightColor).Background(SelectionColor).Foreground(Black) + WindowStyle = lipgloss.NewStyle().BorderForeground(HighlightColor).Padding(0).Border(lipgloss.NormalBorder()) +) + +// generic content style +var BaseStyle = lipgloss.NewStyle(). + BorderStyle(lipgloss.NormalBorder()). + BorderForeground(White). + Padding(0, 1) + +// table style +var TableStyle = table.Styles{ + Header: lipgloss.NewStyle().Padding(0, 1).BorderStyle(lipgloss.NormalBorder()).BorderForeground(Teal).BorderBottom(true).Bold(true).Width(26), + Selected: lipgloss.NewStyle().Foreground(Black).Background(TealBorder).Bold(true), + Cell: lipgloss.NewStyle().Padding(0, 1).Width(26), +} + +// login from styles +var ( + FocusedStyle = lipgloss.NewStyle().Foreground(Orange) + BlurredStyle = lipgloss.NewStyle().Foreground(Teal) + CursorStyle = FocusedStyle.Copy() + NoStyle = lipgloss.NewStyle() + HelpStyle = BlurredStyle.Copy() + CursorModeHelpStyle = lipgloss.NewStyle().Foreground(TealHover) + + FocusedButton = FocusedStyle.Copy().Render("[ Submit ]") + BlurredButton = fmt.Sprintf("[ %s ]", BlurredStyle.Render("Submit")) +) + +// status line +var StatusStyle = lipgloss.NewStyle().Foreground(OrangeHover).Padding(0, 1).Bold(true) +var StatsStyle = lipgloss.NewStyle().Foreground(OrangeHover).Padding(0).Bold(true) + +// Stats table style +var DetailedStatsStyle = table.Styles{ + Header: lipgloss.NewStyle().Padding(0, 1).BorderStyle(lipgloss.NormalBorder()).BorderForeground(Teal).BorderBottom(true).Bold(true).Width(28).Foreground(Teal), + Selected: lipgloss.NewStyle().Foreground(White), + Cell: lipgloss.NewStyle().Padding(0, 1).Width(28), +} diff --git a/cmd/tui/globals.go b/cmd/tui/globals.go index c886237..e626c38 100644 --- a/cmd/tui/globals.go +++ b/cmd/tui/globals.go @@ -1,45 +1,14 @@ package main import ( - "time" - "github.com/tunnels-is/nicelandvpn-desktop/core" - "go.mongodb.org/mongo-driver/bson/primitive" ) var ( - user *User + user *core.User PAFR core.FORWARD_REQUEST ) -// Device token struct need for the login respons from user scruct -type DEVICE_TOKEN struct { - DT string `bson:"DT"` - N string `bson:"N"` - Created time.Time `bson:"C"` -} - -// use struct you get from the login request -type User struct { - ID primitive.ObjectID `json:"_id,omitempty" bson:"_id,omitempty"` - APIKey string `bson:"AK" json:"APIKey"` - Email string `bson:"E"` - TwoFactorEnabled bool `json:"TwoFactorEnabled" bson:"TFE"` - Disabled bool `bson:"D" json:"Disabled"` - Tokens []*DEVICE_TOKEN `json:"Tokens" bson:"T"` - DeviceToken *DEVICE_TOKEN `json:",omitempty" bson:"-"` - - CashCode int `bson:"CSC" json:"CashCode"` - Affiliate string `bson:"AF"` - SubLevel int `bson:"SUL"` - SubExpiration time.Time `bson:"SE"` - TrialStarted time.Time `bson:"TrialStarted" json:"TrialStarted"` - - CancelSub bool `json:"CancelSub" bson:"CS"` - - Version string `json:"Version" bson:"-"` -} - var ( app_state_str = [10]string{ "VPN List Update", "Ready to Connect", "Version", "VPN Tunnel Ready", diff --git a/cmd/tui/little_helpers.go b/cmd/tui/little_helpers.go index bbc00fb..8623a9e 100644 --- a/cmd/tui/little_helpers.go +++ b/cmd/tui/little_helpers.go @@ -23,7 +23,7 @@ func TimedUIUpdate(MONITOR chan int) { core.GetRoutersAndAccessPoints(&PAFR) } - core.PrepareState() + core.PrepareState() TUI.Send(&tea.KeyMsg{ Type: 0, diff --git a/cmd/tui/main.go b/cmd/tui/main.go index 6a6a535..59a14e6 100644 --- a/cmd/tui/main.go +++ b/cmd/tui/main.go @@ -4,6 +4,7 @@ import ( "runtime/debug" "time" + "github.com/charmbracelet/bubbles/textinput" tea "github.com/charmbracelet/bubbletea" "github.com/tunnels-is/nicelandvpn-desktop/core" ) @@ -15,8 +16,9 @@ const ( ) var ( - MONITOR = make(chan int, 200) - TUI *tea.Program + MONITOR = make(chan int, 200) + TUI *tea.Program + userLoginInputs = make([]textinput.Model, 4) ) func main() { diff --git a/cmd/tui/styles.go b/cmd/tui/styles.go deleted file mode 100644 index b0eacd6..0000000 --- a/cmd/tui/styles.go +++ /dev/null @@ -1,81 +0,0 @@ -package main - -import ( - "fmt" - - "github.com/charmbracelet/bubbles/table" - "github.com/charmbracelet/lipgloss" -) - -// colors taken from frontend/src/assets/style/variables.scss -var ( - main_bg = lipgloss.Color("#141414") - body_bg = lipgloss.Color("#202324") - body_dark_bg = lipgloss.Color("#0A0B0E") - - teal = lipgloss.Color("#28ad85") - teal_border = lipgloss.Color("#3AF4BD") - teal_hover = lipgloss.Color("#20C997") - - orange = lipgloss.Color("#FF922D") - orange_border = lipgloss.Color("#EF7503") - orange_hover = lipgloss.Color("#EF7503") - - log_error = lipgloss.Color("#FF0000") - log_warning = lipgloss.Color("#FFFF00") - - lightblue = lipgloss.Color("#20bec9") - red = lipgloss.Color("#FF5858") - white = lipgloss.Color("#FFFFFF") - black = lipgloss.Color("#000000") - - success_color = lipgloss.Color("#0AB60A") - error_color = lipgloss.Color("#E70808") -) - -// tab style -var ( - docStyle = lipgloss.NewStyle().Padding(1, 1, 1, 1) - highlightColor = teal_hover - selectionColor = orange - inactiveTabStyle = lipgloss.NewStyle().Border(lipgloss.RoundedBorder(), true, true, false, true).UnsetBorderBottom().BorderForeground(highlightColor) - activeTabStyle = lipgloss.NewStyle().Border(lipgloss.RoundedBorder(), true, true, false, true).UnsetBorderBottom().BorderForeground(highlightColor).Background(selectionColor).Foreground(black) - windowStyle = lipgloss.NewStyle().BorderForeground(highlightColor).Padding(0).Border(lipgloss.NormalBorder()) -) - -// generic content style -var baseStyle = lipgloss.NewStyle(). - BorderStyle(lipgloss.NormalBorder()). - BorderForeground(white). - Padding(0, 1) - -// table style -var table_style = table.Styles{ - Header: lipgloss.NewStyle().Padding(0, 1).BorderStyle(lipgloss.NormalBorder()).BorderForeground(teal).BorderBottom(true).Bold(true).Width(26), - Selected: lipgloss.NewStyle().Foreground(black).Background(teal_border).Bold(true), - Cell: lipgloss.NewStyle().Padding(0, 1).Width(26), -} - -// login from styles -var ( - focusedStyle = lipgloss.NewStyle().Foreground(orange) - blurredStyle = lipgloss.NewStyle().Foreground(teal) - cursorStyle = focusedStyle.Copy() - noStyle = lipgloss.NewStyle() - helpStyle = blurredStyle.Copy() - cursorModeHelpStyle = lipgloss.NewStyle().Foreground(teal_hover) - - focusedButton = focusedStyle.Copy().Render("[ Submit ]") - blurredButton = fmt.Sprintf("[ %s ]", blurredStyle.Render("Submit")) -) - -// status line -var statusStyle = lipgloss.NewStyle().Foreground(orange_hover).Padding(0, 1).Bold(true) -var statsStyle = lipgloss.NewStyle().Foreground(orange_hover).Padding(0).Bold(true) - -// Stats table style -var detailedStatsStyle = table.Styles{ - Header: lipgloss.NewStyle().Padding(0, 1).BorderStyle(lipgloss.NormalBorder()).BorderForeground(teal).BorderBottom(true).Bold(true).Width(28).Foreground(teal), - Selected: lipgloss.NewStyle().Foreground(white), - Cell: lipgloss.NewStyle().Padding(0, 1).Width(28), -} diff --git a/cmd/tui/tui.go b/cmd/tui/tui.go index b29460f..d20afdd 100644 --- a/cmd/tui/tui.go +++ b/cmd/tui/tui.go @@ -13,6 +13,7 @@ import ( "github.com/charmbracelet/bubbles/viewport" tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/lipgloss" + "github.com/tunnels-is/nicelandvpn-desktop/cmd/termlib" "github.com/tunnels-is/nicelandvpn-desktop/core" ) @@ -25,8 +26,6 @@ type model struct { logsViewport viewport.Model stats []table.Model logs []string - ready bool - status []string keys keyMap help help.Model // setting I have no idea how to handle them yet... @@ -182,9 +181,9 @@ func (m model) View() string { isActive := i == m.activeTab if isActive { - style = activeTabStyle.Copy() + style = termlib.ActiveTabStyle.Copy() } else { - style = inactiveTabStyle.Copy() + style = termlib.InactiveTabStyle.Copy() } renderedTabs = append(renderedTabs, style.Render(t)) @@ -199,19 +198,19 @@ func (m model) View() string { var tabContent string switch m.activeTab { case 0: - tabContent = baseStyle.Render(m.serverTable.View()) + tabContent = termlib.BaseStyle.Render(m.serverTable.View()) case 1: - tabContent = baseStyle.Render(m.routerTable.View()) + tabContent = termlib.BaseStyle.Render(m.routerTable.View()) case 2: - tabContent = baseStyle.Render(m.logsViewport.View()) + tabContent = termlib.BaseStyle.Render(m.logsViewport.View()) case 3: // Breaks if terminal columns < 134 ??? Also if for some reason you change the number the form of the stats your will have to change this too tabContent = lipgloss.JoinHorizontal( - lipgloss.Left, baseStyle.Render(lipgloss.JoinVertical(lipgloss.Left, m.stats[0].View(), "\n", m.stats[2].View())), - baseStyle.Render(lipgloss.JoinVertical(lipgloss.Left, m.stats[1].View(), strings.Repeat("\n", 8), m.stats[3].View()))) + lipgloss.Left, termlib.BaseStyle.Render(lipgloss.JoinVertical(lipgloss.Left, m.stats[0].View(), "\n", m.stats[2].View())), + termlib.BaseStyle.Render(lipgloss.JoinVertical(lipgloss.Left, m.stats[1].View(), strings.Repeat("\n", 8), m.stats[3].View()))) default: - tabContent = baseStyle.Render("Not implemented yet!") + tabContent = termlib.BaseStyle.Render("Not implemented yet!") } - doc.WriteString(windowStyle.Render(tabContent)) + doc.WriteString(termlib.WindowStyle.Render(tabContent)) doc.WriteString("\n") // Status line at the bottom @@ -227,12 +226,12 @@ func (m model) View() string { status = lipgloss.JoinHorizontal(lipgloss.Left, status, sep, stats) hlpView := m.help.View(m.keys) if m.help.ShowAll { - doc.WriteString(lipgloss.JoinVertical(lipgloss.Left, statusStyle.Render(status), hlpView)) + doc.WriteString(lipgloss.JoinVertical(lipgloss.Left, termlib.StatusStyle.Render(status), hlpView)) } else { - doc.WriteString(lipgloss.JoinHorizontal(lipgloss.Left, statusStyle.Render(status), sep, hlpView)) + doc.WriteString(lipgloss.JoinHorizontal(lipgloss.Left, termlib.StatusStyle.Render(status), sep, hlpView)) } - return docStyle.Render(doc.String()) + return termlib.DocStyle.Render(doc.String()) } func StartTui() { @@ -240,7 +239,8 @@ func StartTui() { // I do not think I can have 2 completely different models // in bubbletea this is the only way I could figure out // how to do it... - login() + termlib.Login(userLoginInputs) + user = termlib.SendLoginRequest(userLoginInputs) // Initial VPNs and Routers tables // I thought it's a good idea to have the @@ -256,7 +256,7 @@ func StartTui() { } if PAFR.JSONData != nil { - core.GetRoutersAndAccessPoints(&PAFR) + _, _, _ = core.GetRoutersAndAccessPoints(&PAFR) } // Configure tabs and their number @@ -298,13 +298,13 @@ func StartTui() { table.WithColumns(s_col), table.WithRows(s_row), ) - s_t.SetStyles(table_style) + s_t.SetStyles(termlib.TableStyle) r_t := table.New( table.WithColumns(r_col), table.WithRows(r_row), ) - r_t.SetStyles(table_style) + r_t.SetStyles(termlib.TableStyle) detes := detailedStatsInit() // Initial tables finished --- @@ -312,7 +312,7 @@ func StartTui() { // Initialize the viewport for the logs vp := viewport.New(50, 22) vp.SetContent("Loading...") - vp.Style = baseStyle.UnsetBorderStyle() + vp.Style = termlib.BaseStyle.UnsetBorderStyle() // make the model and give some starting values m := model{tabs: tabs, serverTable: s_t, routerTable: r_t, logsViewport: vp, stats: detes} @@ -452,7 +452,7 @@ func detailedStatsInit() []table.Model { table.WithHeight(10), ) - as_t.SetStyles(detailedStatsStyle) + as_t.SetStyles(termlib.DetailedStatsStyle) as_t.Blur() // Interface table @@ -478,7 +478,7 @@ func detailedStatsInit() []table.Model { table.WithHeight(3), ) - i_t.SetStyles(detailedStatsStyle) + i_t.SetStyles(termlib.DetailedStatsStyle) i_t.Blur() // Connection table @@ -504,7 +504,7 @@ func detailedStatsInit() []table.Model { table.WithHeight(3), ) - c_t.SetStyles(detailedStatsStyle) + c_t.SetStyles(termlib.DetailedStatsStyle) c_t.Blur() // Network Stats table @@ -532,7 +532,7 @@ func detailedStatsInit() []table.Model { table.WithHeight(5), ) - n_t.SetStyles(detailedStatsStyle) + n_t.SetStyles(termlib.DetailedStatsStyle) n_t.Blur() return []table.Model{as_t, i_t, c_t, n_t} diff --git a/core/adapter_darwin.go b/core/adapter_darwin.go index ff2bcef..e2834d8 100644 --- a/core/adapter_darwin.go +++ b/core/adapter_darwin.go @@ -30,7 +30,6 @@ func GetIPv6Settings(PotentialDefault *CONNECTION_SETTINGS) { } func GetDnsSettings(PotentialDefault *CONNECTION_SETTINGS) { - dnsout, err := exec.Command("networksetup", "-getdnsservers", PotentialDefault.IFName).Output() if err != nil { CreateErrorLog("", "Unable to find DNS settings >>", err) @@ -55,6 +54,7 @@ func GetDnsSettings(PotentialDefault *CONNECTION_SETTINGS) { func ChangeDNS() { defer RecoverAndLogToFile() } + func ChangeDNSWhileConnected() error { defer RecoverAndLogToFile() return nil @@ -70,7 +70,6 @@ func InitializeTunnelAdapter() error { } func EnablePacketRouting() error { - DisableIPv6() CreateLog("connect", "Creating default route") @@ -120,14 +119,12 @@ func RestoreOriginalDefaultRoute() (err error) { } func RestoreIPv6() { - if !C.DisableIPv6OnConnect { CreateLog("connect", "IPv6 settings unchanged") return } if GLOBAL_STATE.DefaultInterface.IP6Method == "Manual" { - } else if GLOBAL_STATE.DefaultInterface.IP6Method == "Automatic" { _, err := exec.Command("networksetup", "-setv6automatic", GLOBAL_STATE.DefaultInterface.IFName).Output() @@ -144,7 +141,6 @@ func RestoreIPv6() { } func DisableIPv6() { - if !C.DisableIPv6OnConnect { CreateLog("connect", "IPv6 settings unchanged") return @@ -162,8 +158,7 @@ func ResetAfterFailedConnectionAttempt() { RestoreIPv6() } -func RestoreDNS() { - +func RestoreDNS(force false) { } func VerifyAndBackupSettings(PotentialDefault *CONNECTION_SETTINGS) (err error) { @@ -171,7 +166,6 @@ func VerifyAndBackupSettings(PotentialDefault *CONNECTION_SETTINGS) (err error) } func FindDefaultInterfaceAndGateway() (PotentialDefault *CONNECTION_SETTINGS, err error) { - cmd := exec.Command("netstat", "-nr", "-f", "inet") routeList, err := cmd.CombinedOutput() if err != nil { @@ -253,7 +247,6 @@ func FindDefaultInterfaceAndGateway() (PotentialDefault *CONNECTION_SETTINGS, er } func LaunchPreperation() (err error) { - A.Interface, err = water.New(water.Config{ DeviceType: water.TUN, }) @@ -283,9 +276,7 @@ func LaunchPreperation() (err error) { } func SetInterfaceStateToUp(name string) error { - ipOut, err := exec.Command("ifconfig", A.Interface.Name(), "10.4.3.2", "10.4.3.1", "up").Output() - if err != nil { CreateErrorLog("", err, "unable to bring up tunnel adapter ", "STDOUT", string(ipOut)) return err @@ -344,7 +335,6 @@ func DeleteRoute(IP string, ignoreActiveRouterIP bool) (err error) { } func FindDefaultInterfaceAndGatewayDuringStartup() (err error) { - PotentialDefault, err := FindDefaultInterfaceAndGateway() if err != nil { CreateErrorLog("", "Could not find default interface and gateway >> ", err) @@ -373,7 +363,6 @@ func FindDefaultInterfaceAndGatewayDuringStartup() (err error) { } func RestoreSettingsFromFile() { - } func PrintInterfaces() (error, []byte) { @@ -393,7 +382,7 @@ func PrintRouters() (error, []byte) { } func PrintDNS() (error, []byte) { - var out = make([]byte, 0) + out := make([]byte, 0) dnsout, err := exec.Command("networksetup", "-getdnsservers", GLOBAL_STATE.DefaultInterface.IFName).Output() if err != nil { out = append(out, []byte("Error: "+err.Error())...) diff --git a/core/adapter_unix.go b/core/adapter_unix.go index 729655e..179d3de 100644 --- a/core/adapter_unix.go +++ b/core/adapter_unix.go @@ -26,6 +26,7 @@ func (A *Adapter) Close() (err error) { } return } + func (A *Adapter) Uninstall() (err error) { return } @@ -53,7 +54,6 @@ func VerifyAndBackupSettings(PotentialDefault *CONNECTION_SETTINGS) (err error) } func FindDefaultInterfaceAndGatewayDuringStartup() (err error) { - PotentialDefault, err := FindDefaultInterfaceAndGateway() if err != nil { CreateErrorLog("", "Could not find default interface and gateway >> ", err) @@ -122,7 +122,7 @@ func ChangeDNS() error { return nil } -func RestoreDNS() { +func RestoreDNS(force bool) { } func ChangeDNSWhileConnected() error { @@ -156,7 +156,6 @@ func EnablePacketRouting() (err error) { } func InitializeTunnelInterface() (err error) { - err = AdjustRoutersForTunneling() if err != nil { CreateErrorLog("", "Unable to fix route metrics: ", err) @@ -172,7 +171,6 @@ func InitializeTunnelInterface() (err error) { if v.Name == TUNNEL_ADAPTER_NAME { interfaceAlreadyExists = true } - } if !interfaceAlreadyExists { @@ -234,7 +232,6 @@ func SetInterfaceStateToUp() (err error) { } func SetInterfaceStateToDown() (err error) { - ipOut, err := exec.Command("ip", "link", "set", "dev", TUNNEL_ADAPTER_NAME, "down").Output() if err != nil { CreateErrorLog("", "IP || unable to bring the tunnel interface down (link down) || msg: ", err, " || output: ", string(ipOut)) @@ -254,7 +251,7 @@ func AdjustRoutersForTunneling() (err error) { return err } split := strings.Split(string(out), "\n") - var DefaultRoutes = make(map[string]string) + DefaultRoutes := make(map[string]string) for _, v := range split { if strings.Contains(v, "default") { // log.Println(v) @@ -324,7 +321,6 @@ func InitializeTunnelAdapter() (err error) { } func DeleteTunnelInterfaceRoutes(IP string) (err error) { - out, err := exec.Command("ip", "route", "del", IP, "via", TUNNEL_ADAPTER_ADDRESS, "metric", "0").Output() if err != nil { CreateErrorLog("", "IP || Unable to delete route: ", IP, " || Gateway: ", TUNNEL_ADAPTER_ADDRESS, " || msg: ", err, " || output: ", string(out)) @@ -335,7 +331,6 @@ func DeleteTunnelInterfaceRoutes(IP string) (err error) { } func AddRouteToTunnelInterface(IP string) (err error) { - out, err := exec.Command("ip", "route", "add", IP, "via", TUNNEL_ADAPTER_ADDRESS, "metric", "0").Output() if err != nil { @@ -364,7 +359,6 @@ func AddRoute(IP string) (err error) { } func DeleteRoute(IP string, ignoreActiveRouter bool) (err error) { - if GLOBAL_STATE.DefaultInterface == nil { CreateLog("", "Not deleting route, no default interface") return errors.New("no default interface") @@ -386,7 +380,6 @@ func DeleteRoute(IP string, ignoreActiveRouter bool) (err error) { } func FindDefaultInterfaceAndGateway() (POTENTIAL_DEFAULT *CONNECTION_SETTINGS, err error) { - INTERFACE_SETTINGS := FindAllInterfaces() var out []byte @@ -483,7 +476,6 @@ func RestoreIPv6() { } return - } func DisableIPv6() error { diff --git a/core/adapter_windows.go b/core/adapter_windows.go index daf1a9e..bd3abea 100644 --- a/core/adapter_windows.go +++ b/core/adapter_windows.go @@ -249,7 +249,6 @@ func (A *Adapter) Close() (err error) { } func (A *Adapter) Start(cap uint32) (err error) { - r1, _, err1 := syscall.SyscallN(procWintunStartSession.Addr(), uintptr(A.AdapterHandle.handle), uintptr(cap)) if r1 == 0 { err = err1 @@ -408,7 +407,6 @@ func AdapterCleanup(AH *AdapterHandle) { } func initialize_tunnel_adapter() (err error) { - A.Name = TUNNEL_ADAPTER_NAME A.TunHandle = windows.InvalidHandle A.Events = make(chan Event, 10) @@ -427,9 +425,9 @@ func initialize_tunnel_adapter() (err error) { // TRY GENERATING A STATIC UID // A.GUID = new(windows.GUID) - //https://github.com/microsoft/go-winio/blob/main/pkg/guid/guid.go + // https://github.com/microsoft/go-winio/blob/main/pkg/guid/guid.go - //https://github.com/WireGuard/wintun/blob/master/README.md#wintuncreateadapter + // https://github.com/WireGuard/wintun/blob/master/README.md#wintuncreateadapter // A.wt = (*Adapter)(&A.Finalizer) @@ -450,7 +448,7 @@ func initialize_tunnel_adapter() (err error) { CreateLog("", "Starting buffer with 8MB capacity") runtime.SetFinalizer(&A.AdapterHandle, AdapterCleanup) - //0x4000000 + // 0x4000000 err = A.Start(0x4000000) if err != nil { CreateLog("", "Error starting buffer for adapter reader", err) @@ -459,11 +457,12 @@ func initialize_tunnel_adapter() (err error) { return } -var OUTPacket []byte -var OUTErr error +var ( + OUTPacket []byte + OUTErr error +) func VerifyAndBackupSettings(PotentialDefault *CONNECTION_SETTINGS) (err error) { - GetDnsSettings(PotentialDefault) GetIPv6Settings(PotentialDefault) @@ -476,7 +475,6 @@ func VerifyAndBackupSettings(PotentialDefault *CONNECTION_SETTINGS) (err error) } func FindDefaultInterfaceAndGatewayDuringStartup() (err error) { - PotentialDefault, err := FindDefaultInterfaceAndGateway() if err != nil { CreateErrorLog("", "Could not find default interface and gateway >> ", err) @@ -499,7 +497,6 @@ func FindDefaultInterfaceAndGatewayDuringStartup() (err error) { } func GetDnsSettings(PotentialDefault *CONNECTION_SETTINGS) { - cmd := exec.Command("netsh", "interface", "ipv4", "show", "dnsservers", `name=`+PotentialDefault.IFName) cmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: true} out, err := cmd.CombinedOutput() @@ -544,11 +541,9 @@ func GetDnsSettings(PotentialDefault *CONNECTION_SETTINGS) { } } } - } func RestoreSettingsFromFile(PotentialDefault *CONNECTION_SETTINGS) { - CreateLog("", "ADAPTER: ", PotentialDefault) CreateLog("", "RESTORING SETTINGS FROM FILE") @@ -581,9 +576,8 @@ func RestoreSettingsFromFile(PotentialDefault *CONNECTION_SETTINGS) { CreateErrorLog("", "Backup file contained broken DNS") } - RestoreDNS() + RestoreDNS(false) RestoreIPv6() - } func GetIPv6Settings(PotentialDefault *CONNECTION_SETTINGS) { @@ -619,7 +613,6 @@ func GetIPv6Settings(PotentialDefault *CONNECTION_SETTINGS) { CreateLog("", "IPv6 BACKUP: ", PotentialDefault.IFName, " || IPv6: ", true) } } - } func RestoreIPv6() { @@ -646,18 +639,20 @@ func RestoreIPv6() { CreateLog("", "IPv6 Restored on interface: ", GLOBAL_STATE.DefaultInterface.IFName) } - } func ResetAfterFailedConnectionAttempt() { CreateLog("connect", "Connection attempt failed, reseting network configurations") _ = DisableAdapter() RestoreIPv6() - RestoreDNS() + RestoreDNS(false) } -func RestoreDNS() error { +func RestoreDNS(force bool) error { defer RecoverAndLogToFile() + if !C.CustomDNS && !force { + return nil + } if GLOBAL_STATE.DefaultInterface == nil { CreateErrorLog("", "Unable to restore DNS, no interface backup settings found") @@ -736,7 +731,6 @@ func DisableIPv6() error { CreateLog("connect", "Finished disabling IPv6 // time: ", fmt.Sprintf("%.0f", math.Abs(time.Since(start).Seconds())), " seconds") return nil - } func AddRoute(IP string) (err error) { @@ -811,7 +805,6 @@ func DisableAdapter() (err error) { cmd := exec.Command("netsh", "interface", "ipv4", "delete", "address", `name="`+TUNNEL_ADAPTER_NAME+`"`, "addr=", TUNNEL_ADAPTER_ADDRESS, "gateway=", "All") cmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: true} out, err := cmd.Output() - if err != nil { CreateErrorLog("", "Error disabling adapter || msg: ", err, " || output: ", string(out)) return err @@ -845,9 +838,11 @@ func EnablePacketRouting() (err error) { return } - err = ChangeDNS() - if err != nil { - return + if C.CustomDNS { + err = ChangeDNS() + if err != nil { + return + } } return } @@ -898,7 +893,6 @@ func ClearDNS(Interface string) error { } CreateLog("file", "DNS cleared on interface: ", Interface) return nil - } func SetDNS(Interface, IP string, index string) error { @@ -907,7 +901,6 @@ func SetDNS(Interface, IP string, index string) error { cmd := exec.Command("netsh", "interface", "ipv4", "add", "dnsservers", `name=`+Interface, "address="+IP, "index="+index) cmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: true} out, err := cmd.Output() - if err != nil { CreateErrorLog("", "NETSH || Error setting DNS: ", IP, " || interface: ", Interface, " || msg: ", err, " || output: ", string(out)) return err @@ -1051,7 +1044,6 @@ func PrintDNS() ([]byte, error) { // WINDOWS DLL STUFF func CloseAllOpenSockets() error { - if !C.CloseConnectionsOnConnect { CreateLog("", "Leaving open sockets intact") return nil diff --git a/core/api.go b/core/api.go index d09116b..790858a 100644 --- a/core/api.go +++ b/core/api.go @@ -32,7 +32,6 @@ func LocalhostCustomDialer(ctx context.Context, network, addr string) (net.Conn, } func OpenProxyTunnelToRouter(ctx context.Context) (TCP_CONN net.Conn, err error) { - TCP_CONN, err = net.Dial("tcp", GLOBAL_STATE.ActiveRouter.IP+":443") if err != nil { CreateErrorLog("", "Could not dial router: ", GLOBAL_STATE.ActiveRouter.IP, err) @@ -58,7 +57,7 @@ func CleanupWithStateLock() { _ = SetInterfaceStateToDown() RestoreIPv6() - RestoreDNS() + RestoreDNS(false) InstantlyClearPortMaps() SetGlobalStateAsDisconnected() @@ -116,7 +115,6 @@ func SwitchRouter(Tag string) (code int, err error) { } return 200, nil - } func SendRawBytesToLocalhostProxy(method string, route string, data []byte, timeoutMS int) ([]byte, int, error) { @@ -290,7 +288,6 @@ func SendRequestToControllerProxy(method string, route string, data interface{}, var LAST_PRIVATE_ACCESS_POINT_UPDATE = time.Now() func GetPrivateAccessPoints(FR *FORWARD_REQUEST) (interface{}, int, error) { - if GLOBAL_STATE.ActiveRouter == nil { return nil, 500, errors.New("active router not found, please wait a moment") } @@ -299,7 +296,6 @@ func GetPrivateAccessPoints(FR *FORWARD_REQUEST) (interface{}, int, error) { } func LoadRoutersUnAuthenticated() (interface{}, int, error) { - log.Println("GET ROUTERS UN_AHUTH") GLOBAL_STATE.Routers = nil GLOBAL_STATE.Routers = make([]*ROUTER, 0) @@ -387,7 +383,7 @@ func GetRoutersAndAccessPoints(FR *FORWARD_REQUEST) (interface{}, int, error) { PrivateAccessPoints := make([]*AccessPoint, 0) if code == 200 { - CreateLog("", "RESPONSE:", string(responseBytes)) + // CreateLog("", "RESPONSE:", string(responseBytes)) err = json.Unmarshal(responseBytes, &PrivateAccessPoints) if err != nil { CreateErrorLog("", "Unable to unmarshal private device list: ", err) @@ -513,11 +509,9 @@ func GetRoutersAndAccessPoints(FR *FORWARD_REQUEST) (interface{}, int, error) { return false } if GLOBAL_STATE.Routers[a].Score == GLOBAL_STATE.Routers[b].Score { - if GLOBAL_STATE.Routers[a].MS < GLOBAL_STATE.Routers[b].MS { return true } - } return GLOBAL_STATE.Routers[a].Score > GLOBAL_STATE.Routers[b].Score @@ -575,7 +569,6 @@ func GetRoutersAndAccessPoints(FR *FORWARD_REQUEST) (interface{}, int, error) { if GLOBAL_STATE.AccessPoints[a].Router.MS < GLOBAL_STATE.AccessPoints[b].Router.MS { return true } - } return GLOBAL_STATE.AccessPoints[a].Router.Score > GLOBAL_STATE.AccessPoints[b].Router.Score }) @@ -591,7 +584,6 @@ func GetRoutersAndAccessPoints(FR *FORWARD_REQUEST) (interface{}, int, error) { if GLOBAL_STATE.PrivateAccessPoints[a].Router.MS < GLOBAL_STATE.PrivateAccessPoints[b].Router.MS { return true } - } return GLOBAL_STATE.PrivateAccessPoints[a].Router.Score > GLOBAL_STATE.PrivateAccessPoints[b].Router.Score }) @@ -722,6 +714,11 @@ func SetConfig(SF *CONFIG_FORM) error { return errors.New("unable to change config while nicelandVPN is exiting") } + if (C.CustomDNS != SF.CustomDNS) && GLOBAL_STATE.Connected { + CreateLog("loader", "unable to change custom DNS state while connected") + return errors.New("unable to change custom DNS state while connected") + } + if SF.Version != "" { C.Version = SF.Version } @@ -732,6 +729,7 @@ func SetConfig(SF *CONFIG_FORM) error { C.KillSwitch = SF.KillSwitch C.DisableIPv6OnConnect = SF.DisableIPv6OnConnect C.CloseConnectionsOnConnect = SF.CloseConnectionsOnConnect + C.CustomDNS = SF.CustomDNS // if SF.PrevSession != nil { // C.PrevSession = SF.PrevSession @@ -837,7 +835,6 @@ func PrepareState() { // } // } // GLOBAL_STATE.ActiveAccessPoint = GetActiveAccessPointFromActiveSession() - } func GetActiveAccessPointFromActiveSession() *AccessPoint { @@ -1011,10 +1008,9 @@ func ConnectToAccessPoint(NS *CONTROLLER_SESSION_REQUEST, startRouting bool) (S var CCDec []byte CC_DATA := new(OTK_REQUEST) - var FINAL_OTK = new(OTK) - var FINAL_OTKR = new(OTK_REQUEST) + FINAL_OTK := new(OTK) + FINAL_OTKR := new(OTK_REQUEST) defer func() { - if S != nil { S.PrivateKey = nil } diff --git a/core/main.go b/core/main.go index 87725b4..1132d51 100644 --- a/core/main.go +++ b/core/main.go @@ -7,10 +7,10 @@ import ( "errors" "io" "net" + "net/http" "os" "time" - "net/http" _ "net/http/pprof" "github.com/go-ping/ping" @@ -170,7 +170,6 @@ func StateMaintenance(MONITOR chan int) { BUFFER_ERROR = false _ = AutoReconnect() } - } func AutoReconnect() (connected bool) { @@ -258,7 +257,6 @@ func LoadConfig() { var config *os.File var err error defer func() { - if config != nil { _ = config.Close() } @@ -289,6 +287,8 @@ func LoadConfig() { NC.AutoReconnect = true NC.KillSwitch = false NC.DisableIPv6OnConnect = true + NC.LogBlockedDomains = true + NC.CustomDNS = false var cb []byte cb, err = json.Marshal(NC) @@ -305,7 +305,7 @@ func LoadConfig() { return } - err = os.Chmod(GLOBAL_STATE.ConfigPath, 0777) + err = os.Chmod(GLOBAL_STATE.ConfigPath, 0o777) if err != nil { GLOBAL_STATE.ClientStartupError = true CreateErrorLog("", "Unable to change ownership of log file: ", err) @@ -353,7 +353,7 @@ func LoadDNSWhitelist() (err error) { return nil } - WFile, err := os.OpenFile(C.DomainWhitelist, os.O_RDWR|os.O_CREATE, 0777) + WFile, err := os.OpenFile(C.DomainWhitelist, os.O_RDWR|os.O_CREATE, 0o777) if err != nil { return err } @@ -494,7 +494,6 @@ func ParseRoutersFromRawDataToMemory(lines [][]byte) (count int) { } return - } func DownloadRoutersFromOnlineSource() ([][]byte, error) { @@ -657,14 +656,13 @@ func BackupSettingsToFile(NewDefault *CONNECTION_SETTINGS) { return } - err = os.Chmod(GLOBAL_STATE.LogFileName, 0777) + err = os.Chmod(GLOBAL_STATE.LogFileName, 0o777) if err != nil { CreateErrorLog("", "Unable to change ownership of log file: ", err) return } defer func() { - if backupFile != nil { _ = backupFile.Close() } @@ -682,7 +680,6 @@ func BackupSettingsToFile(NewDefault *CONNECTION_SETTINGS) { CreateErrorLog("", "Unable to write backup settings to file: ", err) return } - } func InterfaceMaintenenceAndBackup() { diff --git a/core/packet.go b/core/packet.go index 06c125b..7ba4a37 100644 --- a/core/packet.go +++ b/core/packet.go @@ -1,510 +1,509 @@ -package core - -import ( - "encoding/binary" - "net" - - "github.com/miekg/dns" -) - -var ( - PREV_DNS_IP [4]byte - IS_UNIX bool = false -) - -var ( - EP_Version byte - EP_Protocol byte - - EP_DstIP [4]byte - - EP_IPv4HeaderLength byte - EP_IPv4Header []byte - EP_TPHeader []byte - - EP_SrcPort [2]byte - EP_DstPort [2]byte - EP_MappedPort *RP - - EP_NAT_IP [4]byte - EP_NAT_OK bool - - EP_RST byte - - EP_DNS_Response []byte - EP_DNS_OK bool - EP_DNS_Port_Placeholder [2]byte - EP_DNS_Packet []byte - - // This IP gets over-written on connect - EP_VPNSrcIP [4]byte -) - -func ProcessEgressPacket(p *[]byte) (sendRemote bool, sendLocal bool) { - - packet := *p - - EP_Version = packet[0] >> 4 - if EP_Version != 4 { - return false, false - } - - EP_Protocol = packet[9] - if EP_Protocol != 6 && EP_Protocol != 17 { - return false, false - } - - // Get the full IPv4Header length in bytes - EP_IPv4HeaderLength = (packet[0] << 4 >> 4) * 32 / 8 - - EP_IPv4Header = packet[:EP_IPv4HeaderLength] - EP_TPHeader = packet[EP_IPv4HeaderLength:] - - // DROP RST packets - if EP_Protocol == 6 { - EP_RST = EP_TPHeader[13] & 0x7 >> 2 - // fmt.Printf("%08b - RST:%08b\n", EP_TPHeader[13], EP_RST) - if EP_RST == 1 { - // log.Println("RST PACKET") - return false, false - } - } - - EP_DstIP[0] = packet[16] - EP_DstIP[1] = packet[17] - EP_DstIP[2] = packet[18] - EP_DstIP[3] = packet[19] - - // This drops NETBIOS DNS packets to the VPN interface - if EP_DstIP == [4]byte{10, 4, 3, 255} { - return false, false - } - - EP_SrcPort[0] = EP_TPHeader[0] - EP_SrcPort[1] = EP_TPHeader[1] - - EP_DstPort[0] = EP_TPHeader[2] - EP_DstPort[1] = EP_TPHeader[3] - - // CUSTOM DNS - // https://stackoverflow.com/questions/7565300/identifying-dns-packets - if EP_Protocol == 17 { - if IsDNSQuery(EP_TPHeader[8:]) { - // log.Println("DNS FOUND!!!!!!") - - // log.Println("UDP HEADER:", EP_TPHeader[:8]) - // log.Println("UDP DATA:", EP_TPHeader[8:]) - // log.Println("UDP HEADER:", EP_TPHeader[:8], EP_DstIP, EP_DstPort) - EP_DNS_Response, EP_DNS_OK = ProcessEgressDNSQuery(EP_TPHeader[8:]) - if EP_DNS_OK { - // Replace Source IP - EP_IPv4Header[12] = EP_IPv4Header[16] - EP_IPv4Header[13] = EP_IPv4Header[17] - EP_IPv4Header[14] = EP_IPv4Header[18] - EP_IPv4Header[15] = EP_IPv4Header[19] - - // Replace Destination IP - EP_IPv4Header[16] = IP_InterfaceIP[0] - EP_IPv4Header[17] = IP_InterfaceIP[1] - EP_IPv4Header[18] = IP_InterfaceIP[2] - EP_IPv4Header[19] = IP_InterfaceIP[3] - - // Replace Source Port - EP_DNS_Port_Placeholder[0] = EP_TPHeader[0] - EP_DNS_Port_Placeholder[1] = EP_TPHeader[1] - - EP_TPHeader[0] = EP_TPHeader[2] - EP_TPHeader[1] = EP_TPHeader[3] - - EP_TPHeader[2] = EP_DNS_Port_Placeholder[0] - EP_TPHeader[3] = EP_DNS_Port_Placeholder[1] - - /// - EP_DNS_Packet = append(packet[:EP_IPv4HeaderLength+8], EP_DNS_Response...) - // Modify the total Length of the IP Header - binary.BigEndian.PutUint16(EP_DNS_Packet[2:4], uint16(int(EP_IPv4HeaderLength)+8+len(EP_DNS_Response))) - - // Modify the length of the Transport Header - binary.BigEndian.PutUint16(EP_DNS_Packet[EP_IPv4HeaderLength+4:EP_IPv4HeaderLength+6], uint16(len(EP_DNS_Response))+8) - - RecalculateAndReplaceIPv4HeaderChecksum(EP_DNS_Packet[:EP_IPv4HeaderLength]) - RecalculateAndReplaceTransportChecksum(EP_DNS_Packet[:EP_IPv4HeaderLength], EP_DNS_Packet[EP_IPv4HeaderLength:]) - - *p = EP_DNS_Packet - - return false, true - } else { - - if IS_UNIX { - PREV_DNS_IP[0] = EP_IPv4Header[16] - PREV_DNS_IP[1] = EP_IPv4Header[17] - PREV_DNS_IP[2] = EP_IPv4Header[18] - PREV_DNS_IP[3] = EP_IPv4Header[19] - - EP_IPv4Header[16] = C.DNS1Bytes[0] - EP_IPv4Header[17] = C.DNS1Bytes[1] - EP_IPv4Header[18] = C.DNS1Bytes[2] - EP_IPv4Header[19] = C.DNS1Bytes[3] - } - - } - - } - } - - if EP_Protocol == 6 { - - EP_MappedPort = CreateOrGetPortMapping(&TCP_o0, EP_DstIP, EP_SrcPort, EP_DstPort) - if EP_MappedPort == nil { - // log.Println("NO TCP PORT MAPPING", EP_DstIP, EP_SrcPort, EP_DstPort) - return false, false - } - - } else if EP_Protocol == 17 { - - EP_MappedPort = CreateOrGetPortMapping(&UDP_o0, EP_DstIP, EP_SrcPort, EP_DstPort) - if EP_MappedPort == nil { - // log.Println("NO UDP PORT MAPPING", EP_DstIP, EP_SrcPort, EP_DstPort) - return false, false - } - - } - - EP_NAT_IP, EP_NAT_OK = AS.AP.NAT_CACHE[EP_DstIP] - if EP_NAT_OK { - // log.Println("FOUND NAT", EP_DstIP, EP_NAT_IP) - EP_IPv4Header[16] = EP_NAT_IP[0] - EP_IPv4Header[17] = EP_NAT_IP[1] - EP_IPv4Header[18] = EP_NAT_IP[2] - EP_IPv4Header[19] = EP_NAT_IP[3] - } - - EP_TPHeader[0] = EP_MappedPort.Mapped[0] - EP_TPHeader[1] = EP_MappedPort.Mapped[1] - - EP_IPv4Header[12] = EP_VPNSrcIP[0] - EP_IPv4Header[13] = EP_VPNSrcIP[1] - EP_IPv4Header[14] = EP_VPNSrcIP[2] - EP_IPv4Header[15] = EP_VPNSrcIP[3] - - RecalculateAndReplaceIPv4HeaderChecksum(EP_IPv4Header) - RecalculateAndReplaceTransportChecksum(EP_IPv4Header, EP_TPHeader) - - return true, false -} - -var ( - IP_Version byte - IP_Protocol byte - - IP_DstIP [4]byte - IP_SrcIP [4]byte - - IP_IPv4HeaderLength byte - IP_IPv4Header []byte - IP_TPHeader []byte - - IP_SrcPort [2]byte - IP_DstPort [2]byte - IP_MappedPort *RP - - IP_NAT_IP [4]byte - IP_NAT_OK bool - - // This IP gets over-written on connect - // IP_VPNSrcIP [4]byte - IP_InterfaceIP [4]byte -) - -func ProcessIngressPacket(packet []byte) bool { - - IP_SrcIP[0] = packet[12] - IP_SrcIP[1] = packet[13] - IP_SrcIP[2] = packet[14] - IP_SrcIP[3] = packet[15] - - IP_Protocol = packet[9] - - IP_IPv4HeaderLength = (packet[0] << 4 >> 4) * 32 / 8 - IP_IPv4Header = packet[:IP_IPv4HeaderLength] - IP_TPHeader = packet[IP_IPv4HeaderLength:] - - IP_DstPort[0] = IP_TPHeader[2] - IP_DstPort[1] = IP_TPHeader[3] - - IP_NAT_IP, IP_NAT_OK = AS.AP.REVERSE_NAT_CACHE[IP_SrcIP] - if IP_NAT_OK { - // log.Println("FOUND INGRESS NAT", IP_SrcIP, IP_NAT_IP) - IP_IPv4Header[12] = IP_NAT_IP[0] - IP_IPv4Header[13] = IP_NAT_IP[1] - IP_IPv4Header[14] = IP_NAT_IP[2] - IP_IPv4Header[15] = IP_NAT_IP[3] - - IP_SrcIP[0] = IP_NAT_IP[0] - IP_SrcIP[1] = IP_NAT_IP[1] - IP_SrcIP[2] = IP_NAT_IP[2] - IP_SrcIP[3] = IP_NAT_IP[3] - } - - if IP_Protocol == 6 { - - IP_MappedPort = GetIngressPortMapping(&TCP_o0, IP_SrcIP, IP_DstPort) - if IP_MappedPort == nil { - // log.Println("NO PORT MAPPING", IP_SrcIP, binary.BigEndian.Uint16(IP_DstPort[:])) - return false - } - - } else if IP_Protocol == 17 { - - IP_MappedPort = GetIngressPortMapping(&UDP_o0, IP_SrcIP, IP_DstPort) - if IP_MappedPort == nil { - // log.Println("NO PORT MAPPING", IP_SrcIP, binary.BigEndian.Uint16(IP_DstPort[:])) - return false - } - - } - - IP_TPHeader[2] = IP_MappedPort.Local[0] - IP_TPHeader[3] = IP_MappedPort.Local[1] - - IP_IPv4Header[16] = IP_InterfaceIP[0] - IP_IPv4Header[17] = IP_InterfaceIP[1] - IP_IPv4Header[18] = IP_InterfaceIP[2] - IP_IPv4Header[19] = IP_InterfaceIP[3] - - if EP_Protocol == 17 { - if IP_SrcIP == C.DNS1Bytes && IS_UNIX { - // if IsDNSQuery(EP_TPHeader[8:]) && IS_UNIX { - IP_IPv4Header[12] = PREV_DNS_IP[0] - IP_IPv4Header[13] = PREV_DNS_IP[1] - IP_IPv4Header[14] = PREV_DNS_IP[2] - IP_IPv4Header[15] = PREV_DNS_IP[3] - } - } - - RecalculateAndReplaceIPv4HeaderChecksum(IP_IPv4Header) - RecalculateAndReplaceTransportChecksum(IP_IPv4Header, IP_TPHeader) - - return true -} - -func IsDNSQuery(UDPData []byte) bool { - - if len(UDPData) < 12 { - // log.Println("NOT ENOUGH UDP DATA") - return false - } - - // QR == 0 when making a DNS Query - QR := UDPData[2] >> 7 - if QR != 0 { - return false - } - - // AN Count is always 0 for queries - if UDPData[6] != 0 || UDPData[7] != 0 { - // log.Println("AN COUNT OFF", UDPData[6:8]) - return false - } - - // NS Count is always 0 for queries - if UDPData[8] != 0 || UDPData[9] != 0 { - // log.Println("NS COUNT OFF", UDPData[8:10]) - return false - } - - return true -} -func ProcessEgressDNSQuery(UDPData []byte) (DNSResponse []byte, shouldProcess bool) { - - q := new(dns.Msg) - q.Unpack(UDPData) - - x := new(dns.Msg) - x.SetReply(q) - x.Authoritative = true - x.Compress = true - - isCustomDNS := false - for i := range x.Question { - - if x.Question[i].Qtype == dns.TypeA { - domain := x.Question[i].Name[0 : len(x.Question[i].Name)-1] - - _, ok := GLOBAL_BLOCK_LIST[domain] - // CreateLog("", "DNS Q: ", domain, len(GLOBAL_BLOCK_LIST), ok) - if ok { - - CreateLog("", "Domain blocked:", domain) - isCustomDNS = true - x.Answer = append(x.Answer, &dns.A{ - Hdr: dns.RR_Header{ - Class: dns.TypeA, - Rrtype: dns.ClassINET, - Name: x.Question[i].Name, - Ttl: 5, - }, - A: net.ParseIP("127.0.0.1"), - }) - - } else { - - IPS, CNAME := DNSAMapping(domain) - if CNAME != "" { - - isCustomDNS = true - x.Answer = append(x.Answer, &dns.CNAME{ - Hdr: dns.RR_Header{ - Class: dns.ClassNONE, - Rrtype: dns.TypeCNAME, - Name: x.Question[i].Name, - Ttl: 5, - }, - Target: CNAME + ".", - }) - - } else if IPS != nil { - isCustomDNS = true - - for ii := range IPS { - x.Answer = append(x.Answer, &dns.A{ - Hdr: dns.RR_Header{ - Class: dns.TypeA, - Rrtype: dns.ClassINET, - Name: x.Question[i].Name, - Ttl: 5, - }, - A: IPS[ii].To4(), - }) - } - } - - } - - } else if x.Question[i].Qtype == dns.TypeTXT { - - TXTS := DNSTXTMapping(x.Question[i].Name[0 : len(x.Question[i].Name)-1]) - if TXTS != nil { - isCustomDNS = true - for ii := range TXTS { - x.Answer = append(x.Answer, &dns.TXT{ - Hdr: dns.RR_Header{ - Class: dns.ClassNONE, - Rrtype: dns.TypeTXT, - Name: x.Question[i].Name, - Ttl: 30, - }, - Txt: []string{TXTS[ii]}, - }) - } - } - - } else if x.Question[i].Qtype == dns.TypeCNAME { - - CNAME := DNSCNameMapping(x.Question[i].Name[0 : len(x.Question[i].Name)-1]) - if CNAME != "" { - isCustomDNS = true - x.Answer = append(x.Answer, &dns.CNAME{ - Hdr: dns.RR_Header{ - Class: dns.ClassNONE, - Rrtype: dns.TypeCNAME, - Name: x.Question[i].Name, - Ttl: 30, - }, - Target: CNAME + ".", - }) - } - - } - - } - - if isCustomDNS { - - var err error - DNSResponse, err = x.Pack() - if err != nil { - // log.Println("UNABLE TO PICK DNS RESPONSE: ", err) - return - } - - shouldProcess = true - return - } - - return -} - -func ProcessIngressDNSQuery(TPHeader []byte) bool { - - return true -} - -func RecalculateAndReplaceIPv4HeaderChecksum(bytes []byte) { - // Clear checksum bytes - bytes[10] = 0 - bytes[11] = 0 - - // Compute checksum - var csum uint32 - for i := 0; i < len(bytes); i += 2 { - csum += uint32(bytes[i]) << 8 - csum += uint32(bytes[i+1]) - } - for { - // Break when sum is less or equals to 0xFFFF - if csum <= 65535 { - break - } - // Add carry to the sum - csum = (csum >> 16) + uint32(uint16(csum)) - } - - // Flip all the bits and replace checksum - binary.BigEndian.PutUint16(bytes[10:12], ^uint16(csum)) - return -} - -func RecalculateAndReplaceTransportChecksum(IPv4Header []byte, TPPacket []byte) { - - if IPv4Header[9] == 6 { - TPPacket[16] = 0 - TPPacket[17] = 0 - } else if IPv4Header[9] == 17 { - TPPacket[6] = 0 - TPPacket[7] = 0 - } - - var csum uint32 - csum += (uint32(IPv4Header[12]) + uint32(IPv4Header[14])) << 8 - csum += uint32(IPv4Header[13]) + uint32(IPv4Header[15]) - csum += (uint32(IPv4Header[16]) + uint32(IPv4Header[18])) << 8 - csum += uint32(IPv4Header[17]) + uint32(IPv4Header[19]) - csum += uint32(uint8(IPv4Header[9])) - tcpLength := uint32(len(TPPacket)) - - csum += tcpLength & 0xffff - csum += tcpLength >> 16 - - length := len(TPPacket) - 1 - for i := 0; i < length; i += 2 { - // For our test packet, doing this manually is about 25% faster - // (740 ns vs. 1000ns) than doing it by calling binary.BigEndian.Uint16. - csum += uint32(TPPacket[i]) << 8 - csum += uint32(TPPacket[i+1]) - } - if len(TPPacket)%2 == 1 { - csum += uint32(TPPacket[length]) << 8 - } - for csum > 0xffff { - csum = (csum >> 16) + (csum & 0xffff) - } - - if IPv4Header[9] == 6 { - binary.BigEndian.PutUint16(TPPacket[16:18], ^uint16(csum)) - } else if IPv4Header[9] == 17 { - binary.BigEndian.PutUint16(TPPacket[6:8], ^uint16(csum)) - } - - return -} +package core + +import ( + "encoding/binary" + "net" + + "github.com/miekg/dns" +) + +var ( + PREV_DNS_IP [4]byte + IS_UNIX bool = false +) + +var ( + EP_Version byte + EP_Protocol byte + + EP_DstIP [4]byte + + EP_IPv4HeaderLength byte + EP_IPv4Header []byte + EP_TPHeader []byte + + EP_SrcPort [2]byte + EP_DstPort [2]byte + EP_MappedPort *RP + + EP_NAT_IP [4]byte + EP_NAT_OK bool + + EP_RST byte + + EP_DNS_Response []byte + EP_DNS_OK bool + EP_DNS_Port_Placeholder [2]byte + EP_DNS_Packet []byte + + // This IP gets over-written on connect + EP_VPNSrcIP [4]byte + + EP_NEW_RST int +) + +func ProcessEgressPacket(p *[]byte) (sendRemote bool, sendLocal bool) { + packet := *p + + EP_Version = packet[0] >> 4 + if EP_Version != 4 { + return false, false + } + + EP_Protocol = packet[9] + if EP_Protocol != 6 && EP_Protocol != 17 { + return false, false + } + + // Get the full IPv4Header length in bytes + EP_IPv4HeaderLength = (packet[0] << 4 >> 4) * 32 / 8 + + EP_IPv4Header = packet[:EP_IPv4HeaderLength] + EP_TPHeader = packet[EP_IPv4HeaderLength:] + + // DROP RST packets + if EP_Protocol == 6 { + EP_RST = EP_TPHeader[13] & 0x7 >> 2 + if EP_RST == 1 { + EP_NEW_RST = int(EP_TPHeader[13]) + EP_NEW_RST |= int(0b00010100) + EP_TPHeader[13] = byte(EP_NEW_RST) + // fmt.Printf("%08b - RST:%08b\n", EP_TPHeader[13], EP_RST) + // fmt.Printf("POST TRANSFORM: %08b\n", EP_TPHeader[13]) + // log.Println("RST PACKET") + // return false, false + } + } + + EP_DstIP[0] = packet[16] + EP_DstIP[1] = packet[17] + EP_DstIP[2] = packet[18] + EP_DstIP[3] = packet[19] + + // This drops NETBIOS DNS packets to the VPN interface + if EP_DstIP == [4]byte{10, 4, 3, 255} { + return false, false + } + + EP_SrcPort[0] = EP_TPHeader[0] + EP_SrcPort[1] = EP_TPHeader[1] + + EP_DstPort[0] = EP_TPHeader[2] + EP_DstPort[1] = EP_TPHeader[3] + + // CUSTOM DNS + // https://stackoverflow.com/questions/7565300/identifying-dns-packets + if EP_Protocol == 17 { + if IsDNSQuery(EP_TPHeader[8:]) { + // log.Println("DNS FOUND!!!!!!") + + // log.Println("UDP HEADER:", EP_TPHeader[:8]) + // log.Println("UDP DATA:", EP_TPHeader[8:]) + // log.Println("UDP HEADER:", EP_TPHeader[:8], EP_DstIP, EP_DstPort) + EP_DNS_Response, EP_DNS_OK = ProcessEgressDNSQuery(EP_TPHeader[8:]) + if EP_DNS_OK { + // Replace Source IP + EP_IPv4Header[12] = EP_IPv4Header[16] + EP_IPv4Header[13] = EP_IPv4Header[17] + EP_IPv4Header[14] = EP_IPv4Header[18] + EP_IPv4Header[15] = EP_IPv4Header[19] + + // Replace Destination IP + EP_IPv4Header[16] = IP_InterfaceIP[0] + EP_IPv4Header[17] = IP_InterfaceIP[1] + EP_IPv4Header[18] = IP_InterfaceIP[2] + EP_IPv4Header[19] = IP_InterfaceIP[3] + + // Replace Source Port + EP_DNS_Port_Placeholder[0] = EP_TPHeader[0] + EP_DNS_Port_Placeholder[1] = EP_TPHeader[1] + + EP_TPHeader[0] = EP_TPHeader[2] + EP_TPHeader[1] = EP_TPHeader[3] + + EP_TPHeader[2] = EP_DNS_Port_Placeholder[0] + EP_TPHeader[3] = EP_DNS_Port_Placeholder[1] + + /// + EP_DNS_Packet = append(packet[:EP_IPv4HeaderLength+8], EP_DNS_Response...) + // Modify the total Length of the IP Header + binary.BigEndian.PutUint16(EP_DNS_Packet[2:4], uint16(int(EP_IPv4HeaderLength)+8+len(EP_DNS_Response))) + + // Modify the length of the Transport Header + binary.BigEndian.PutUint16(EP_DNS_Packet[EP_IPv4HeaderLength+4:EP_IPv4HeaderLength+6], uint16(len(EP_DNS_Response))+8) + + RecalculateAndReplaceIPv4HeaderChecksum(EP_DNS_Packet[:EP_IPv4HeaderLength]) + RecalculateAndReplaceTransportChecksum(EP_DNS_Packet[:EP_IPv4HeaderLength], EP_DNS_Packet[EP_IPv4HeaderLength:]) + + *p = EP_DNS_Packet + + return false, true + } else { + if IS_UNIX { + PREV_DNS_IP[0] = EP_IPv4Header[16] + PREV_DNS_IP[1] = EP_IPv4Header[17] + PREV_DNS_IP[2] = EP_IPv4Header[18] + PREV_DNS_IP[3] = EP_IPv4Header[19] + + EP_IPv4Header[16] = C.DNS1Bytes[0] + EP_IPv4Header[17] = C.DNS1Bytes[1] + EP_IPv4Header[18] = C.DNS1Bytes[2] + EP_IPv4Header[19] = C.DNS1Bytes[3] + } + } + + } + } + + if EP_Protocol == 6 { + + EP_MappedPort = CreateOrGetPortMapping(&TCP_o0, EP_DstIP, EP_SrcPort, EP_DstPort) + if EP_MappedPort == nil { + // log.Println("NO TCP PORT MAPPING", EP_DstIP, EP_SrcPort, EP_DstPort) + return false, false + } + + } else if EP_Protocol == 17 { + + EP_MappedPort = CreateOrGetPortMapping(&UDP_o0, EP_DstIP, EP_SrcPort, EP_DstPort) + if EP_MappedPort == nil { + // log.Println("NO UDP PORT MAPPING", EP_DstIP, EP_SrcPort, EP_DstPort) + return false, false + } + + } + + EP_NAT_IP, EP_NAT_OK = AS.AP.NAT_CACHE[EP_DstIP] + if EP_NAT_OK { + // log.Println("FOUND NAT", EP_DstIP, EP_NAT_IP) + EP_IPv4Header[16] = EP_NAT_IP[0] + EP_IPv4Header[17] = EP_NAT_IP[1] + EP_IPv4Header[18] = EP_NAT_IP[2] + EP_IPv4Header[19] = EP_NAT_IP[3] + } + + EP_TPHeader[0] = EP_MappedPort.Mapped[0] + EP_TPHeader[1] = EP_MappedPort.Mapped[1] + + EP_IPv4Header[12] = EP_VPNSrcIP[0] + EP_IPv4Header[13] = EP_VPNSrcIP[1] + EP_IPv4Header[14] = EP_VPNSrcIP[2] + EP_IPv4Header[15] = EP_VPNSrcIP[3] + + RecalculateAndReplaceIPv4HeaderChecksum(EP_IPv4Header) + RecalculateAndReplaceTransportChecksum(EP_IPv4Header, EP_TPHeader) + + return true, false +} + +var ( + IP_Version byte + IP_Protocol byte + + IP_DstIP [4]byte + IP_SrcIP [4]byte + + IP_IPv4HeaderLength byte + IP_IPv4Header []byte + IP_TPHeader []byte + + IP_SrcPort [2]byte + IP_DstPort [2]byte + IP_MappedPort *RP + + IP_NAT_IP [4]byte + IP_NAT_OK bool + + // This IP gets over-written on connect + // IP_VPNSrcIP [4]byte + IP_InterfaceIP [4]byte +) + +func ProcessIngressPacket(packet []byte) bool { + IP_SrcIP[0] = packet[12] + IP_SrcIP[1] = packet[13] + IP_SrcIP[2] = packet[14] + IP_SrcIP[3] = packet[15] + + IP_Protocol = packet[9] + + IP_IPv4HeaderLength = (packet[0] << 4 >> 4) * 32 / 8 + IP_IPv4Header = packet[:IP_IPv4HeaderLength] + IP_TPHeader = packet[IP_IPv4HeaderLength:] + + IP_DstPort[0] = IP_TPHeader[2] + IP_DstPort[1] = IP_TPHeader[3] + + IP_NAT_IP, IP_NAT_OK = AS.AP.REVERSE_NAT_CACHE[IP_SrcIP] + if IP_NAT_OK { + // log.Println("FOUND INGRESS NAT", IP_SrcIP, IP_NAT_IP) + IP_IPv4Header[12] = IP_NAT_IP[0] + IP_IPv4Header[13] = IP_NAT_IP[1] + IP_IPv4Header[14] = IP_NAT_IP[2] + IP_IPv4Header[15] = IP_NAT_IP[3] + + IP_SrcIP[0] = IP_NAT_IP[0] + IP_SrcIP[1] = IP_NAT_IP[1] + IP_SrcIP[2] = IP_NAT_IP[2] + IP_SrcIP[3] = IP_NAT_IP[3] + } + + if IP_Protocol == 6 { + + IP_MappedPort = GetIngressPortMapping(&TCP_o0, IP_SrcIP, IP_DstPort) + if IP_MappedPort == nil { + // log.Println("NO PORT MAPPING", IP_SrcIP, binary.BigEndian.Uint16(IP_DstPort[:])) + return false + } + + } else if IP_Protocol == 17 { + + IP_MappedPort = GetIngressPortMapping(&UDP_o0, IP_SrcIP, IP_DstPort) + if IP_MappedPort == nil { + // log.Println("NO PORT MAPPING", IP_SrcIP, binary.BigEndian.Uint16(IP_DstPort[:])) + return false + } + + } + + IP_TPHeader[2] = IP_MappedPort.Local[0] + IP_TPHeader[3] = IP_MappedPort.Local[1] + + IP_IPv4Header[16] = IP_InterfaceIP[0] + IP_IPv4Header[17] = IP_InterfaceIP[1] + IP_IPv4Header[18] = IP_InterfaceIP[2] + IP_IPv4Header[19] = IP_InterfaceIP[3] + + if EP_Protocol == 17 { + if IP_SrcIP == C.DNS1Bytes && IS_UNIX { + // if IsDNSQuery(EP_TPHeader[8:]) && IS_UNIX { + IP_IPv4Header[12] = PREV_DNS_IP[0] + IP_IPv4Header[13] = PREV_DNS_IP[1] + IP_IPv4Header[14] = PREV_DNS_IP[2] + IP_IPv4Header[15] = PREV_DNS_IP[3] + } + } + + RecalculateAndReplaceIPv4HeaderChecksum(IP_IPv4Header) + RecalculateAndReplaceTransportChecksum(IP_IPv4Header, IP_TPHeader) + + return true +} + +func IsDNSQuery(UDPData []byte) bool { + if len(UDPData) < 12 { + // log.Println("NOT ENOUGH UDP DATA") + return false + } + + // QR == 0 when making a DNS Query + QR := UDPData[2] >> 7 + if QR != 0 { + return false + } + + // AN Count is always 0 for queries + if UDPData[6] != 0 || UDPData[7] != 0 { + // log.Println("AN COUNT OFF", UDPData[6:8]) + return false + } + + // NS Count is always 0 for queries + if UDPData[8] != 0 || UDPData[9] != 0 { + // log.Println("NS COUNT OFF", UDPData[8:10]) + return false + } + + return true +} + +func ProcessEgressDNSQuery(UDPData []byte) (DNSResponse []byte, shouldProcess bool) { + q := new(dns.Msg) + _ = q.Unpack(UDPData) + + x := new(dns.Msg) + x.SetReply(q) + x.Authoritative = true + x.Compress = true + + isCustomDNS := false + for i := range x.Question { + if x.Question[i].Qtype == dns.TypeA { + domain := x.Question[i].Name[0 : len(x.Question[i].Name)-1] + + _, ok := GLOBAL_BLOCK_LIST[domain] + // CreateLog("", "DNS Q: ", domain, len(GLOBAL_BLOCK_LIST), ok) + if ok { + + if GLOBAL_STATE.C.LogBlockedDomains { + CreateLog("", "Domain blocked:", domain) + } + isCustomDNS = true + x.Answer = append(x.Answer, &dns.A{ + Hdr: dns.RR_Header{ + Class: dns.TypeA, + Rrtype: dns.ClassINET, + Name: x.Question[i].Name, + Ttl: 5, + }, + A: net.ParseIP("127.0.0.1"), + }) + + } else { + + IPS, CNAME := DNSAMapping(domain) + if CNAME != "" { + + isCustomDNS = true + x.Answer = append(x.Answer, &dns.CNAME{ + Hdr: dns.RR_Header{ + Class: dns.ClassNONE, + Rrtype: dns.TypeCNAME, + Name: x.Question[i].Name, + Ttl: 5, + }, + Target: CNAME + ".", + }) + + } else if IPS != nil { + isCustomDNS = true + + for ii := range IPS { + x.Answer = append(x.Answer, &dns.A{ + Hdr: dns.RR_Header{ + Class: dns.TypeA, + Rrtype: dns.ClassINET, + Name: x.Question[i].Name, + Ttl: 5, + }, + A: IPS[ii].To4(), + }) + } + } + + } + + } else if x.Question[i].Qtype == dns.TypeTXT { + + TXTS := DNSTXTMapping(x.Question[i].Name[0 : len(x.Question[i].Name)-1]) + if TXTS != nil { + isCustomDNS = true + for ii := range TXTS { + x.Answer = append(x.Answer, &dns.TXT{ + Hdr: dns.RR_Header{ + Class: dns.ClassNONE, + Rrtype: dns.TypeTXT, + Name: x.Question[i].Name, + Ttl: 30, + }, + Txt: []string{TXTS[ii]}, + }) + } + } + + } else if x.Question[i].Qtype == dns.TypeCNAME { + + CNAME := DNSCNameMapping(x.Question[i].Name[0 : len(x.Question[i].Name)-1]) + if CNAME != "" { + isCustomDNS = true + x.Answer = append(x.Answer, &dns.CNAME{ + Hdr: dns.RR_Header{ + Class: dns.ClassNONE, + Rrtype: dns.TypeCNAME, + Name: x.Question[i].Name, + Ttl: 30, + }, + Target: CNAME + ".", + }) + } + + } + } + + if isCustomDNS { + + var err error + DNSResponse, err = x.Pack() + if err != nil { + // log.Println("UNABLE TO PICK DNS RESPONSE: ", err) + return + } + + shouldProcess = true + return + } + + return +} + +func ProcessIngressDNSQuery(TPHeader []byte) bool { + return true +} + +func RecalculateAndReplaceIPv4HeaderChecksum(bytes []byte) { + // Clear checksum bytes + bytes[10] = 0 + bytes[11] = 0 + + // Compute checksum + var csum uint32 + for i := 0; i < len(bytes); i += 2 { + csum += uint32(bytes[i]) << 8 + csum += uint32(bytes[i+1]) + } + for { + // Break when sum is less or equals to 0xFFFF + if csum <= 65535 { + break + } + // Add carry to the sum + csum = (csum >> 16) + uint32(uint16(csum)) + } + + // Flip all the bits and replace checksum + binary.BigEndian.PutUint16(bytes[10:12], ^uint16(csum)) + return +} + +func RecalculateAndReplaceTransportChecksum(IPv4Header []byte, TPPacket []byte) { + if IPv4Header[9] == 6 { + TPPacket[16] = 0 + TPPacket[17] = 0 + } else if IPv4Header[9] == 17 { + TPPacket[6] = 0 + TPPacket[7] = 0 + } + + var csum uint32 + csum += (uint32(IPv4Header[12]) + uint32(IPv4Header[14])) << 8 + csum += uint32(IPv4Header[13]) + uint32(IPv4Header[15]) + csum += (uint32(IPv4Header[16]) + uint32(IPv4Header[18])) << 8 + csum += uint32(IPv4Header[17]) + uint32(IPv4Header[19]) + csum += uint32(uint8(IPv4Header[9])) + tcpLength := uint32(len(TPPacket)) + + csum += tcpLength & 0xffff + csum += tcpLength >> 16 + + length := len(TPPacket) - 1 + for i := 0; i < length; i += 2 { + // For our test packet, doing this manually is about 25% faster + // (740 ns vs. 1000ns) than doing it by calling binary.BigEndian.Uint16. + csum += uint32(TPPacket[i]) << 8 + csum += uint32(TPPacket[i+1]) + } + if len(TPPacket)%2 == 1 { + csum += uint32(TPPacket[length]) << 8 + } + for csum > 0xffff { + csum = (csum >> 16) + (csum & 0xffff) + } + + if IPv4Header[9] == 6 { + binary.BigEndian.PutUint16(TPPacket[16:18], ^uint16(csum)) + } else if IPv4Header[9] == 17 { + binary.BigEndian.PutUint16(TPPacket[6:8], ^uint16(csum)) + } + + return +} diff --git a/core/structs_globals.go b/core/structs_globals.go index be942b9..5ce4a7e 100644 --- a/core/structs_globals.go +++ b/core/structs_globals.go @@ -18,38 +18,50 @@ var PRODUCTION = false var ENABLE_INSTERFACE = false -var A = new(Adapter) -var AS = new(AdapterSettings) -var C = new(Config) -var GLOBAL_STATE = new(State) +var ( + A = new(Adapter) + AS = new(AdapterSettings) + C = new(Config) + GLOBAL_STATE = new(State) +) var letterRunes = []rune("ABCDEFGHIJKLMNOPQRSTUVWXYZ234567") // var LastRouterPing = time.Now() var LastConnectionAttemp = time.Now() -var BUFFER_ERROR bool -var IGNORE_NEXT_BUFFER_ERROR bool +var ( + BUFFER_ERROR bool + IGNORE_NEXT_BUFFER_ERROR bool +) var STATE_LOCK = sync.Mutex{} -var TUNNEL_ADAPTER_NAME = "NicelandVPN" -var TUNNEL_ADAPTER_ADDRESS = "10.4.3.2" -var TUNNEL_ADAPTER_ADDRESS_IP = net.IP{10, 4, 3, 2} +var ( + TUNNEL_ADAPTER_NAME = "NicelandVPN" + TUNNEL_ADAPTER_ADDRESS = "10.4.3.2" + TUNNEL_ADAPTER_ADDRESS_IP = net.IP{10, 4, 3, 2} +) var MAC_CONNECTION_SETTINGS *CONNECTION_SETTINGS -var CURRENT_UBBS int = 0 -var CURRENT_DBBS int = 0 -var EGRESS_PACKETS uint64 = 0 -var INGRESS_PACKETS uint64 = 0 +var ( + CURRENT_UBBS int = 0 + CURRENT_DBBS int = 0 + EGRESS_PACKETS uint64 = 0 + INGRESS_PACKETS uint64 = 0 +) // NETWORKING STUFF -var TCP_MAP = make(map[[4]byte]*IP) -var TCP_MAP_LOCK = sync.RWMutex{} +var ( + TCP_MAP = make(map[[4]byte]*IP) + TCP_MAP_LOCK = sync.RWMutex{} +) -var UDP_MAP = make(map[[4]byte]*IP) -var UDP_MAP_LOCK = sync.RWMutex{} +var ( + UDP_MAP = make(map[[4]byte]*IP) + UDP_MAP_LOCK = sync.RWMutex{} +) var DNSWhitelist = make(map[string]bool) @@ -67,14 +79,15 @@ type RemotePort struct { LastActivity time.Time } -var L = new(Logs) -var LogQueue = make(chan LogItem, 10000) -var TAG_ERROR = "ERROR" -var TAG_GENERAL = "GENERAL" -var LogFile *os.File +var ( + L = new(Logs) + LogQueue = make(chan LogItem, 10000) + TAG_ERROR = "ERROR" + TAG_GENERAL = "GENERAL" + LogFile *os.File +) -type LoggerInterface struct { -} +type LoggerInterface struct{} type Logs struct { PING [100]string @@ -177,6 +190,7 @@ type CONFIG_FORM struct { PrevSession *CONTROLLER_SESSION_REQUEST `json:"PrevSlot"` DisableIPv6OnConnect bool `json:"DisableIPv6OnConnect"` CloseConnectionsOnConnect bool `json:"CloseConnectionsOnConnect"` + CustomDNS bool `json:"CustomDNS"` } type Config struct { @@ -191,13 +205,14 @@ type Config struct { Version string RouterFilePath string - PrevSession *CONTROLLER_SESSION_REQUEST `json:"-"` DomainWhitelist string EnabledBlockLists []string + LogBlockedDomains bool DisableIPv6OnConnect bool CloseConnectionsOnConnect bool + CustomDNS bool - CLI bool `json:"-"` + PrevSession *CONTROLLER_SESSION_REQUEST `json:"-"` } type AdapterSettings struct { @@ -573,3 +588,31 @@ type MIB_TCPTABLE_OWNER_PID struct { dwNumEntries uint32 table [30000]MIB_TCPROW_OWNER_PID } + +// Device token struct need for the login respons from user scruct +type DEVICE_TOKEN struct { + DT string `bson:"DT"` + N string `bson:"N"` + Created time.Time `bson:"C"` +} + +// use struct you get from the login request +type User struct { + ID primitive.ObjectID `json:"_id,omitempty" bson:"_id,omitempty"` + APIKey string `bson:"AK" json:"APIKey"` + Email string `bson:"E"` + TwoFactorEnabled bool `json:"TwoFactorEnabled" bson:"TFE"` + Disabled bool `bson:"D" json:"Disabled"` + Tokens []*DEVICE_TOKEN `json:"Tokens" bson:"T"` + DeviceToken *DEVICE_TOKEN `json:",omitempty" bson:"-"` + + CashCode int `bson:"CSC" json:"CashCode"` + Affiliate string `bson:"AF"` + SubLevel int `bson:"SUL"` + SubExpiration time.Time `bson:"SE"` + TrialStarted time.Time `bson:"TrialStarted" json:"TrialStarted"` + + CancelSub bool `json:"CancelSub" bson:"CS"` + + Version string `json:"Version" bson:"-"` +} diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index 09442c8..14cd762 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -6,7 +6,6 @@ import toast, { Toaster } from 'react-hot-toast'; import dayjs from "dayjs"; import "./assets/style/app.scss"; - import { CloseApp, IsProduction } from '../wailsjs/go/main/App'; import { Disconnect, LoadRoutersUnAuthenticated, GetRoutersAndAccessPoints, GetState } from '../wailsjs/go/main/Service'; @@ -27,14 +26,6 @@ import StatsSideBar from "./App/StatsSideBar"; const root = createRoot(document.getElementById('app')); -// window.addEventListener('focus', -// STORE.Cache.Set("focus", true) -// ); - -// window.addEventListener('blur', -// STORE.Cache.Set("focus", false) -// ); - const ToggleError = (e) => { let lastFetch = STORE.Cache.Get("error-timeout") let now = dayjs().unix() @@ -104,8 +95,8 @@ const LaunchApp = () => { try { - console.dir(state.ActiveRouter) - console.log("getting access points") + // console.dir(state.ActiveRouter) + // console.log("getting access points") if (STORE.ActiveRouterSet(state)) { let user = STORE.GetUser() if (user) { @@ -152,9 +143,8 @@ const LaunchApp = () => { try { - console.log("GET STATE!!!!") GetState().then((x) => { - console.dir(x) + // console.debug(x) if (x.Err) { ToggleError(x.Err) setState(newState) @@ -330,6 +320,7 @@ class ErrorBoundary extends React.Component { console.info = function() { } console.warn = function() { } console.error = function() { } + console.debug = function() { } window.console = console } diff --git a/frontend/src/App/Dashboard.jsx b/frontend/src/App/Dashboard.jsx index e402b7e..72a2f11 100644 --- a/frontend/src/App/Dashboard.jsx +++ b/frontend/src/App/Dashboard.jsx @@ -327,19 +327,18 @@ const Dashboard = (props) => { } -
{ap.Tag}
-
Quality Score: {ap.Router.Score}
- + {ap.Online && +
Quality Score: {ap.Router.Score}
+ } + {!ap.Online && +
OFFLINE
+ }
- - - - ) } @@ -418,7 +417,6 @@ const Dashboard = (props) => { - {(AccessPoints.length < 1 && PrivateAccessPoints.length < 1 && filter == "") && { - const [inputs, setInputs] = useState({}); - const [user, setUser] = useState(); - const [autoLogout, setAutoLogout] = useState(STORE.Cache.GetBool("auto-logout")) - const navigate = useNavigate(); - const [blockList, setBlockList] = useState([]) + const [inputs, setInputs] = useState({}); + const [user, setUser] = useState(); + const [autoLogout, setAutoLogout] = useState(STORE.Cache.GetBool("auto-logout")) + const navigate = useNavigate(); + const [blockList, setBlockList] = useState([]) + const [blockedLogging, setBlockedLogging] = useState(false) + + // const [capture, setCapture] = useState(false) + // const [whitelist, setWhitelist] = useState(true) + + const ToggleAllBlocking = (enabled) => { + console.log("TOGGLE ALL") + + if (enabled) { + EnableAllBlocklists() - // const [capture, setCapture] = useState(false) - // const [whitelist, setWhitelist] = useState(true) + blockList.forEach((item, index) => { + blockList[index].Enabled = true + }) - const ToggleAllBlocking = (enabled) => { - console.log("TOGGLE ALL") + } else { + DisableAllBlocklists() - if (enabled) { - EnableAllBlocklists() + blockList.forEach((item, index) => { + blockList[index].Enabled = false + }) - blockList.forEach((item, index) => { - blockList[index].Enabled = true - }) + } - } else { - DisableAllBlocklists() + setBlockList([...blockList]) + } - blockList.forEach((item, index) => { - blockList[index].Enabled = false - }) + const ToggleBlockedDomainLogging = async () => { + BlockedDomainLogging(!blockedLogging) + setBlockedLogging(!blockedLogging) + } - } + const ApplyBlocklistConfigurations = async () => { + props.toggleLoading({ logTag: "", tag: "DOMAIN-BLOCK", show: true, msg: "Creating a new combined blocklist..", includeLogs: false }) + try { + await RebuildDomainBlocklist() + } catch (error) { + console.dir(error) + } + props.toggleLoading(undefined) + } - setBlockList([...blockList]) - } + const ToggleBlockList = async (tag) => { - - const ApplyBlocklistConfigurations = async () => { - props.toggleLoading({ logTag: "", tag: "DOMAIN-BLOCK", show: true, msg: "Creating a new combined blocklist..", includeLogs: false }) - try { - await RebuildDomainBlocklist() - } catch (error) { - console.dir(error) - } - props.toggleLoading(undefined) - } - - const ToggleBlockList = async (tag) => { - - blockList.forEach((item, index) => { - if (item.Tag === tag) { - if (item.Enabled) { - DisableBlocklist(tag) - blockList[index].Enabled = false - } else { - EnableBlocklist(tag) - blockList[index].Enabled = true - } - } - }) - - setBlockList([...blockList]) - } - - - // const EnableWhitelist = () => { - // setWhitelist(true) - // EnableDNSWhitelist() - // } - - // const DisableWhitelist = () => { - // setWhitelist(false) - // DisableDNSWhitelist() - // } - - // const StartCapture = () => { - // setCapture(true) - // StartDNSCapture() - // } - - // const StopCapture = () => { - // setCapture(false) - // StopDNSCapture() - // } - - - const ToggleAutoLogout = () => { - let AL = STORE.Cache.GetBool("auto-logout") - if (AL) { - AL = false - } else { - AL = true - } - setAutoLogout(AL) - STORE.Cache.Set("auto-logout", AL) - } - - const Reset = async () => { - - await props.disconnectFromVPN() - - props.toggleLoading({ logTag: "", tag: "NETRESET", show: true, msg: "Recovering network settings", includeLogs: false }) - - ResetEverything().then((x) => { - props.showSuccessToast("Network Settings have been reset", undefined) - }).catch((e) => { - console.dir(e) - props.toggleError("Unknown error, please try again in a moment") - }) - - props.toggleLoading(undefined) - } - - const UpdateRouterFile = async (clearFile) => { - - props.toggleLoading({ logTag: "loader", tag: "ROUTERFILE", show: true, msg: "Updating routers from file", includeLogs: true }) - - await OpenFileDialogForRouterFile(clearFile).then((x) => { - - }).catch((e) => { - console.dir(e) - props.toggleError("Unknown error, please try again in a moment") - }) - - props.toggleLoading(undefined) - } - - const UpdateUser = async () => { - - // try { - props.toggleLoading({ logTag: "", tag: "USER-UPDATE", show: true, msg: "Updating User Settings", includeLogs: false }) - - - let FORM = { - Email: user.Email, - DeviceToken: user.DeviceToken.DT, - APIKey: inputs["APIKey"].APIKey - } - - if (!FORM.APIKey || FORM.APIKey === "") { - props.toggleError("API Key missing, please generate a new API Key.") - props.toggleLoading(undefined) - return - } - - let FR = { - Path: "v2/user/update", - Method: "POST", - JSONData: FORM, - Timeout: 20000 - } - - ForwardToController(FR).then((x) => { - console.dir(x) - if (x.Err) { - props.toggleError(x.Err) - } else { - if (x.Code === 200) { - let nu = { ...user } - nu.APIKey = FORM.APIKey - STORE.Cache.SetObject("user", nu) - props.showSuccessToast("User updated", undefined) - } else { - props.toggleError(x.Data) - } - } - }).catch((e) => { - console.dir(e) - props.toggleError("Unknown error, please try again in a moment") - }) - - props.toggleLoading(undefined) - - } - - const UpdateConfig = async () => { - - props.toggleLoading({ logTag: "loader", tag: "CONFIG", show: true, msg: "Updating configurations", includeLogs: false }) - - let config = STORE.Cache.GetObject("config") - let newConfig = { ...config } - - newConfig.DNS1 = inputs["DNS"].DNS1 - newConfig.DNS2 = inputs["DNS"].DNS2 - newConfig.RouterFilePath = inputs["RP"].RP - newConfig.DebugLogging = inputs["DB"].DB - newConfig.AutoReconnect = inputs["AR"].AR - newConfig.KillSwitch = inputs["KS"].KS - newConfig.DisableIPv6OnConnect = inputs["IP6"].IP6 - newConfig.CloseConnectionsOnConnect = inputs["CC"].CC - - SetConfig(newConfig).then((x) => { - if (x.Err) { - props.toggleError(x.Err) - } else { - STORE.Cache.SetObject("config", newConfig) - props.showSuccessToast("Config saved", undefined) - STORE.Cache.Set("aps-timeout", dayjs().subtract(120, "s").unix()) - } - }).catch((e) => { - console.dir(e) - props.toggleError("Unknown error, please try again in a moment") - }) - - props.toggleLoading(undefined) - } - - const ToggleSubscription = async (toggle) => { - - props.toggleLoading({ logTag: "", tag: "DISABLE", show: true, msg: "Updating your subscription", includeLogs: false }) - - let user = STORE.Cache.GetObject("user") - - let FR = { - Method: "POST", - Timeout: 20000, - Path: "v2/user/toggle/substatus", - JSONData: { - DeviceToken: user.DeviceToken.DT, - Email: user.Email, - Disable: toggle - } - } - - ForwardToController(FR).then((x) => { - console.dir(x) - if (x.Err) { - props.toggleError(x.Err) - } else { - if (x.Code === 200) { - props.showSuccessToast("Subscription status updated", undefined) - user.CancelSub = toggle - STORE.Cache.SetObject("user", user) - setUser(user) - } else { - props.toggleError(x.Data) - } - } - }).catch((e) => { - console.dir(e) - props.toggleError("Unknown error, please try again in a moment") - }) - - - props.toggleLoading(undefined) - } - - const DisableAccount = async () => { - - - props.toggleLoading({ logTag: "", tag: "DISABLE", show: true, msg: "Disabling your account", includeLogs: false }) - - let user = STORE.Cache.GetObject("user") - - let FR = { - Method: "POST", - Timeout: 20000, - Path: "v2/user/toggle/status", - JSONData: { - DeviceToken: user.DeviceToken.DT, - ID: user._id, - Disabled: true - } - } - - ForwardToController(FR).then((x) => { - console.dir(x) - if (x.Err) { - props.toggleError(x.Err) - } else { - if (x.Code === 200) { - props.showSuccessToast("Account Disabled", undefined) - STORE.Cache.Clear() - navigate("/login") - } else { - props.toggleError(x.Data) - } - } - }).catch((e) => { - console.dir(e) - props.toggleError("Unknown error, please try again in a moment") - }) - - props.toggleLoading(undefined) - } - - const EnableAccount = async () => { - props.toggleLoading({ logTag: "", tag: "ENABLE", show: true, msg: "Enabling your account", includeLogs: false }) - - let user = STORE.Cache.GetObject("user") - - let FR = { - Method: "POST", - Timeout: 20000, - Path: "v2/user/toggle/status", - JSONData: { - DeviceToken: user.DeviceToken.DT, - ID: user._id, - Disabled: false - } - } - - ForwardToController(FR).then((x) => { - console.dir(x) - if (x.Err) { - props.toggleError(x.Err) - } else { - if (x.Code === 200) { - props.showSuccessToast("Account Enabled", undefined) - user.Disabled = false - STORE.Cache.SetObject("user", user) - setUser(user) - } else { - props.toggleError(x.Data) - } - } - }).catch((e) => { - console.dir(e) - props.toggleError("Unknown error, please try again in a moment") - }) - - props.toggleLoading(undefined) - } - - const ToggleCC = () => { - let i = { ...inputs } - if (i["CC"].CC === true) { - i["CC"].CC = false - } else { - i["CC"].CC = true - } - setInputs(i) - } - - const ToggleLogging = () => { - let i = { ...inputs } - if (i["DB"].DB === true) { - i["DB"].DB = false - } else { - i["DB"].DB = true - } - setInputs(i) - } - - const ToggleKS = () => { - let i = { ...inputs } - if (i["KS"].KS === true) { - i["KS"].KS = false - } else { - i["KS"].KS = true - } - setInputs(i) - } - - const ToggleIP6 = () => { - let i = { ...inputs } - if (i["IP6"].IP6 === true) { - i["IP6"].IP6 = false - } else { - i["IP6"].IP6 = true - } - setInputs(i) - } - - const ToggleAR = () => { - let i = { ...inputs } - if (i["AR"].AR === true) { - i["AR"].AR = false - } else { - i["AR"].AR = true - } - setInputs(i) - } - - const UpdateInput = (index, key, value) => { - - let i = { ...inputs } - let hasErrors = false - - i[index][key] = value - - let skipErrorValidation = false - if (index !== "APIKey") { - skipErrorValidation = true - } - - - if (!skipErrorValidation) { - if (value === "") { - i[index].Errors[key] = key + " missing" - } else { - i[index].Errors[key] = "" - - if (index === "Email") { - if (!value.includes("@") || !value.includes(".")) { - i[index].Errors[key] = key + " format is wrong" - } - } - } - } - - setInputs(i) - - if (hasErrors) { - props.setSuccess("") - props.toggleError("Error in settings input") - } - - } - - return { - inputs, - setInputs, - user, - setUser, - UpdateInput, - UpdateConfig, - Reset, - DisableAccount, - EnableAccount, - UpdateRouterFile, - ToggleLogging, - ToggleAR, - ToggleKS, - UpdateUser, - ToggleSubscription, - autoLogout, - ToggleAutoLogout, - ToggleBlockList, - ApplyBlocklistConfigurations, - setBlockList, - blockList, - ToggleIP6, - ToggleAllBlocking, - ToggleCC, - } + blockList.forEach((item, index) => { + if (item.Tag === tag) { + if (item.Enabled) { + DisableBlocklist(tag) + blockList[index].Enabled = false + } else { + EnableBlocklist(tag) + blockList[index].Enabled = true + } + } + }) + + setBlockList([...blockList]) + } + + + // const EnableWhitelist = () => { + // setWhitelist(true) + // EnableDNSWhitelist() + // } + + // const DisableWhitelist = () => { + // setWhitelist(false) + // DisableDNSWhitelist() + // } + + // const StartCapture = () => { + // setCapture(true) + // StartDNSCapture() + // } + + // const StopCapture = () => { + // setCapture(false) + // StopDNSCapture() + // } + + + + const ToggleAutoLogout = () => { + let AL = STORE.Cache.GetBool("auto-logout") + if (AL) { + AL = false + } else { + AL = true + } + setAutoLogout(AL) + STORE.Cache.Set("auto-logout", AL) + } + + const Reset = async () => { + + await props.disconnectFromVPN() + + props.toggleLoading({ logTag: "", tag: "NETRESET", show: true, msg: "Recovering network settings", includeLogs: false }) + + ResetEverything().then((x) => { + props.showSuccessToast("Network Settings have been reset", undefined) + }).catch((e) => { + console.dir(e) + props.toggleError("Unknown error, please try again in a moment") + }) + + props.toggleLoading(undefined) + } + + const UpdateRouterFile = async (clearFile) => { + + props.toggleLoading({ logTag: "loader", tag: "ROUTERFILE", show: true, msg: "Updating routers from file", includeLogs: true }) + + await OpenFileDialogForRouterFile(clearFile).then((x) => { + + }).catch((e) => { + console.dir(e) + props.toggleError("Unknown error, please try again in a moment") + }) + + props.toggleLoading(undefined) + } + + const UpdateUser = async () => { + + // try { + props.toggleLoading({ logTag: "", tag: "USER-UPDATE", show: true, msg: "Updating User Settings", includeLogs: false }) + + + let FORM = { + Email: user.Email, + DeviceToken: user.DeviceToken.DT, + APIKey: inputs["APIKey"].APIKey + } + + if (!FORM.APIKey || FORM.APIKey === "") { + props.toggleError("API Key missing, please generate a new API Key.") + props.toggleLoading(undefined) + return + } + + let FR = { + Path: "v2/user/update", + Method: "POST", + JSONData: FORM, + Timeout: 20000 + } + + ForwardToController(FR).then((x) => { + console.dir(x) + if (x.Err) { + props.toggleError(x.Err) + } else { + if (x.Code === 200) { + let nu = { ...user } + nu.APIKey = FORM.APIKey + STORE.Cache.SetObject("user", nu) + props.showSuccessToast("User updated", undefined) + } else { + props.toggleError(x.Data) + } + } + }).catch((e) => { + console.dir(e) + props.toggleError("Unknown error, please try again in a moment") + }) + + props.toggleLoading(undefined) + + } + + const UpdateConfig = async () => { + + props.toggleLoading({ logTag: "loader", tag: "CONFIG", show: true, msg: "Updating configurations", includeLogs: false }) + + let config = STORE.Cache.GetObject("config") + let newConfig = { ...config } + + newConfig.DNS1 = inputs["DNS"].DNS1 + newConfig.DNS2 = inputs["DNS"].DNS2 + newConfig.RouterFilePath = inputs["RP"].RP + newConfig.DebugLogging = inputs["DB"].DB + newConfig.AutoReconnect = inputs["AR"].AR + newConfig.KillSwitch = inputs["KS"].KS + newConfig.DisableIPv6OnConnect = inputs["IP6"].IP6 + newConfig.CloseConnectionsOnConnect = inputs["CC"].CC + newConfig.CustomDNS = inputs["DT"].DT + + SetConfig(newConfig).then((x) => { + console.dir(x) + if (x.Err) { + props.toggleError(x.Err) + } else { + STORE.Cache.SetObject("config", newConfig) + props.showSuccessToast("Config saved", undefined) + STORE.Cache.Set("aps-timeout", dayjs().subtract(120, "s").unix()) + } + }).catch((e) => { + console.dir(e) + props.toggleError("Unknown error, please try again in a moment") + }) + + props.toggleLoading(undefined) + } + + const ToggleSubscription = async (toggle) => { + + props.toggleLoading({ logTag: "", tag: "DISABLE", show: true, msg: "Updating your subscription", includeLogs: false }) + + let user = STORE.Cache.GetObject("user") + + let FR = { + Method: "POST", + Timeout: 20000, + Path: "v2/user/toggle/substatus", + JSONData: { + DeviceToken: user.DeviceToken.DT, + Email: user.Email, + Disable: toggle + } + } + + ForwardToController(FR).then((x) => { + console.dir(x) + if (x.Err) { + props.toggleError(x.Err) + } else { + if (x.Code === 200) { + props.showSuccessToast("Subscription status updated", undefined) + user.CancelSub = toggle + STORE.Cache.SetObject("user", user) + setUser(user) + } else { + props.toggleError(x.Data) + } + } + }).catch((e) => { + console.dir(e) + props.toggleError("Unknown error, please try again in a moment") + }) + + + props.toggleLoading(undefined) + } + + const DisableAccount = async () => { + + + props.toggleLoading({ logTag: "", tag: "DISABLE", show: true, msg: "Disabling your account", includeLogs: false }) + + let user = STORE.Cache.GetObject("user") + + let FR = { + Method: "POST", + Timeout: 20000, + Path: "v2/user/toggle/status", + JSONData: { + DeviceToken: user.DeviceToken.DT, + ID: user._id, + Disabled: true + } + } + + ForwardToController(FR).then((x) => { + console.dir(x) + if (x.Err) { + props.toggleError(x.Err) + } else { + if (x.Code === 200) { + props.showSuccessToast("Account Disabled", undefined) + STORE.Cache.Clear() + navigate("/login") + } else { + props.toggleError(x.Data) + } + } + }).catch((e) => { + console.dir(e) + props.toggleError("Unknown error, please try again in a moment") + }) + + props.toggleLoading(undefined) + } + + const EnableAccount = async () => { + props.toggleLoading({ logTag: "", tag: "ENABLE", show: true, msg: "Enabling your account", includeLogs: false }) + + let user = STORE.Cache.GetObject("user") + + let FR = { + Method: "POST", + Timeout: 20000, + Path: "v2/user/toggle/status", + JSONData: { + DeviceToken: user.DeviceToken.DT, + ID: user._id, + Disabled: false + } + } + + ForwardToController(FR).then((x) => { + console.dir(x) + if (x.Err) { + props.toggleError(x.Err) + } else { + if (x.Code === 200) { + props.showSuccessToast("Account Enabled", undefined) + user.Disabled = false + STORE.Cache.SetObject("user", user) + setUser(user) + } else { + props.toggleError(x.Data) + } + } + }).catch((e) => { + console.dir(e) + props.toggleError("Unknown error, please try again in a moment") + }) + + props.toggleLoading(undefined) + } + + const ToggleCC = () => { + let i = { ...inputs } + if (i["CC"].CC === true) { + i["CC"].CC = false + } else { + i["CC"].CC = true + } + setInputs(i) + } + + const ToggleCustomDNS = () => { + let i = { ...inputs } + if (i["DT"].DT === true) { + i["DT"].DT = false + } else { + i["DT"].DT = true + } + setInputs(i) + } + + const ToggleLogging = () => { + let i = { ...inputs } + if (i["DB"].DB === true) { + i["DB"].DB = false + } else { + i["DB"].DB = true + } + setInputs(i) + } + + const ToggleKS = () => { + let i = { ...inputs } + if (i["KS"].KS === true) { + i["KS"].KS = false + } else { + i["KS"].KS = true + } + setInputs(i) + } + + const ToggleIP6 = () => { + let i = { ...inputs } + if (i["IP6"].IP6 === true) { + i["IP6"].IP6 = false + } else { + i["IP6"].IP6 = true + } + setInputs(i) + } + + const ToggleAR = () => { + let i = { ...inputs } + if (i["AR"].AR === true) { + i["AR"].AR = false + } else { + i["AR"].AR = true + } + setInputs(i) + } + + const UpdateInput = (index, key, value) => { + + let i = { ...inputs } + let hasErrors = false + + i[index][key] = value + + let skipErrorValidation = false + if (index !== "APIKey") { + skipErrorValidation = true + } + + + if (!skipErrorValidation) { + if (value === "") { + i[index].Errors[key] = key + " missing" + } else { + i[index].Errors[key] = "" + + if (index === "Email") { + if (!value.includes("@") || !value.includes(".")) { + i[index].Errors[key] = key + " format is wrong" + } + } + } + } + + setInputs(i) + + if (hasErrors) { + props.setSuccess("") + props.toggleError("Error in settings input") + } + + } + + return { + inputs, + setInputs, + user, + setUser, + UpdateInput, + UpdateConfig, + Reset, + DisableAccount, + EnableAccount, + UpdateRouterFile, + ToggleLogging, + ToggleAR, + ToggleKS, + UpdateUser, + ToggleSubscription, + autoLogout, + ToggleAutoLogout, + ToggleBlockList, + ApplyBlocklistConfigurations, + setBlockList, + blockList, + ToggleIP6, + ToggleAllBlocking, + ToggleCC, + blockedLogging, + setBlockedLogging, + ToggleBlockedDomainLogging, + ToggleCustomDNS, + } } const Settings = (props) => { - const navigate = useNavigate(); - - const { inputs, setInputs, user, setUser, UpdateInput, UpdateConfig, Reset, DisableAccount, EnableAccount, UpdateRouterFile, ToggleLogging, ToggleAR, ToggleKS, UpdateUser, ToggleSubscription, autoLogout, ToggleAutoLogout, ToggleBlockList, ApplyBlocklistConfigurations, setBlockList, blockList, ToggleIP6, ToggleAllBlocking, ToggleCC } = useForm(props); - - const inputFile = useRef(null) - - const ResetApp = async () => { - await props.disconnectFromVPN() - STORE.Cache.Clear() - CloseApp() - } - - const CreateApiKey = () => { - let i = { ...inputs } - i["APIKey"].APIKey = uuidv4() - setInputs(i) - } - - useEffect(() => { - - if (props.state?.BlockLists?.length > 0) { - setBlockList(props.state.BlockLists) - } - - let i = {} - i["Email"] = {} - i["Email"].Errors = {} - - i["Password"] = {} - i["Password"].Errors = {} - - i["APIKey"] = {} - i["APIKey"].Errors = {} - - let u = STORE.Cache.GetObject("user"); - let newUser = undefined - if (u) { - newUser = { ...u } - } - - if (newUser) { - i["Email"].Email = newUser.Email - i["Password"].Password = newUser.Password - i["APIKey"].APIKey = newUser.APIKey - let splitDate = u.SubExpiration.split("T") - let finalDate = dayjs(splitDate[0]).format("DD MMMM YYYY") - newUser.SubExpirationString = finalDate - setUser(newUser) - } - - i["RP"] = {} - i["RP"].Errors = {} - - i["DB"] = {} - i["DB"].Errors = {} - - i["KS"] = {} - i["KS"].Errors = {} - - i["CC"] = {} - i["CC"].Errors = {} - - i["IP6"] = {} - i["IP6"].Errors = {} - - i["AR"] = {} - i["AR"].Errors = {} - - i["DNS"] = {} - i["DNS"].Errors = {} - i["DNS"].DNS1 = "" - i["DNS"].DNS2 = "" - - let config = STORE.Cache.GetObject("config") - if (config) { - i["DNS"].DNS1 = config.DNS1 - i["DNS"].DNS2 = config.DNS2 - i["RP"].RP = config.RouterFilePath - i["DB"].DB = config.DebugLogging - i["KS"].KS = config.KillSwitch - i["AR"].AR = config.AutoReconnect - i["IP6"].IP6 = config.DisableIPv6OnConnect - i["CC"].CC = config.CloseConnectionsOnConnect - } else { - i["DNS"].DNS0 = "1.1.1.1" - i["DNS"].DNS2 = "8.8.8.8" - i["DB"].DB = true - } - - setInputs(i) - - - }, []); - - const NavigateToDebug = () => { - navigate("/debug") - } - const NavigateToSubscriptions = () => { - OpenURL("https://www.nicelandvpn.is/#/pricing") - } - - const NavigateTo2fa = () => { - navigate("/twofactor") - } - - const NavigateToLogs = () => { - navigate("/logs") - } - const NavigateToTokens = () => { - navigate("/tokens") - } - - return ( -
- {user && - <> -
- -
- -
Account
-
- -
- -
-
{inputs["Email"].Email}
-
- -
-
Account Status
-
{`${user.Disabled ? "Disabled" : "Active"}`}
-
- -
-
Last Account Update
-
- {dayjs(user.Updated).format("DD-MM-YYYY HH:mm:ss")} -
-
- -
-
Subcription
-
- {user.SubLevel === 1 && - <>Nice - } - {user.SubLevel === 2 && - <>Nicer - } - {user.SubLevel === 3 && - <>Nicest - } - {(user.SubLevel === 0 || user.SubLevel === 666) && -
NavigateToSubscriptions()}>Click To Subscribe
- } -
-
- - {(user.SubLevel > 0 && user.SubLevel !== 666) && - <> -
-
Status
-
{`${user.CancelSub ? "Disabled" : "Active"}`}
-
-
-
Expires
-
{user.SubExpirationString}
-
- {user.CancelSub && -
-
ToggleSubscription(false)} >Enable Subscription
-
- } - {!user.CancelSub && -
-
ToggleSubscription(true)} >Disable Subscription
-
- } - - } - {!user?.Disabled && -
-
DisableAccount()} >Disable Account
-
- } - - -
-
NavigateToTokens()} >Device Logins
-
-
-
NavigateTo2fa()} >Two-Factor Authentication
-
- - {user?.Disabled && -
-
EnableAccount()} >Enable Account
-
- } - - -
- - - - } - + const navigate = useNavigate(); + const { inputs, setInputs, user, setUser, UpdateInput, UpdateConfig, Reset, DisableAccount, EnableAccount, UpdateRouterFile, ToggleLogging, ToggleAR, ToggleKS, UpdateUser, ToggleSubscription, autoLogout, ToggleAutoLogout, ToggleBlockList, ApplyBlocklistConfigurations, setBlockList, blockList, ToggleIP6, ToggleAllBlocking, ToggleCC, blockedLogging, setBlockedLogging, ToggleBlockedDomainLogging, ToggleCustomDNS } = useForm(props); -
+ const ResetApp = async () => { + await props.disconnectFromVPN() + STORE.Cache.Clear() + CloseApp() + } -
- -
Utility
-
UpdateConfig()}>Save
-
+ const CreateApiKey = () => { + let i = { ...inputs } + i["APIKey"].APIKey = uuidv4() + setInputs(i) + } + + useEffect(() => { + + if (props.state?.BlockLists?.length > 0) { + setBlockList(props.state.BlockLists) + } + + let i = {} + i["Email"] = {} + i["Email"].Errors = {} -
+ i["Password"] = {} + i["Password"].Errors = {} + + i["APIKey"] = {} + i["APIKey"].Errors = {} + + let u = STORE.Cache.GetObject("user"); + let newUser = undefined + if (u) { + newUser = { ...u } + } + + if (newUser) { + i["Email"].Email = newUser.Email + i["Password"].Password = newUser.Password + i["APIKey"].APIKey = newUser.APIKey + let splitDate = u.SubExpiration.split("T") + let finalDate = dayjs(splitDate[0]).format("DD MMMM YYYY") + newUser.SubExpirationString = finalDate + setUser(newUser) + } + + i["RP"] = {} + i["RP"].Errors = {} + + i["DB"] = {} + i["DB"].Errors = {} + + i["KS"] = {} + i["KS"].Errors = {} + + i["CC"] = {} + i["CC"].Errors = {} + + i["IP6"] = {} + i["IP6"].Errors = {} + + i["AR"] = {} + i["AR"].Errors = {} + + i["DT"] = {} + i["DT"].Errors = {} + + i["DNS"] = {} + i["DNS"].Errors = {} + i["DNS"].DNS1 = "" + i["DNS"].DNS2 = "" + + let config = STORE.Cache.GetObject("config") + if (config) { + i["DNS"].DNS1 = config.DNS1 + i["DNS"].DNS2 = config.DNS2 + i["RP"].RP = config.RouterFilePath + i["DB"].DB = config.DebugLogging + i["KS"].KS = config.KillSwitch + i["AR"].AR = config.AutoReconnect + i["DT"].DT = config.CustomDNS + i["IP6"].IP6 = config.DisableIPv6OnConnect + i["CC"].CC = config.CloseConnectionsOnConnect + setBlockedLogging(config.LogBlockedDomains) + } else { + i["DNS"].DNS0 = "1.1.1.1" + i["DNS"].DNS2 = "8.8.8.8" + i["DB"].DB = true + } + + setInputs(i) + + + + }, []); + + const NavigateToSubscriptions = () => { + OpenURL("https://www.nicelandvpn.is/#/pricing") + } + + const NavigateTo2fa = () => { + navigate("/twofactor") + } + + const NavigateToLogs = () => { + navigate("/logs") + } + const NavigateToTokens = () => { + navigate("/tokens") + } + + return ( +
+ {user && + <> +
+ +
+ +
Account
+
+ +
+ +
+
{inputs["Email"].Email}
+
+ +
+
Account Status
+
{`${user.Disabled ? "Disabled" : "Active"}`}
+
+ +
+
Last Account Update
+
+ {dayjs(user.Updated).format("DD-MM-YYYY HH:mm:ss")} +
+
+ +
+
Subcription
+
+ {user.SubLevel === 1 && + <>Nice + } + {user.SubLevel === 2 && + <>Nicer + } + {user.SubLevel === 3 && + <>Nicest + } + {(user.SubLevel === 0 || user.SubLevel === 666) && +
NavigateToSubscriptions()}>Click To Subscribe
+ } +
+
+ + {(user.SubLevel > 0 && user.SubLevel !== 666) && + <> +
+
Status
+
{`${user.CancelSub ? "Disabled" : "Active"}`}
+
+
+
Expires
+
{user.SubExpirationString}
+
+ {user.CancelSub && +
+
ToggleSubscription(false)} >Enable Subscription
+
+ } + {!user.CancelSub && +
+
ToggleSubscription(true)} >Disable Subscription
+
+ } + + } + {!user?.Disabled && +
+
DisableAccount()} >Disable Account
+
+ } + + +
+
NavigateToTokens()} >Device Logins
+
+
+
NavigateTo2fa()} >Two-Factor Authentication
+
+ + {user?.Disabled && +
+
EnableAccount()} >Enable Account
+
+ } + + +
+ + + + } + + + +
+ +
+ +
Utility
+
UpdateConfig()}>Save
+
+ +
+ +
+
+ +
+ Advanced Mode +
+
+
+ +
+
+ +
+ Auto Reconnect +
+
+
+ + {props.advancedMode && + <> + +
+
+ +
+ Killswitch +
+
+
+ + +
+
+ +
+ Disable IPv6 +
+
+
+ +
+
+ +
+ Logging +
+
+
+ + + + } + + + + + + +
+
Reset()} >Reset Network Settings
+
+ +
+
ResetApp()} >Reset App Settings
+
+ +
+ + {props.advancedMode && +
+ +
+ +
Routing
+
UpdateConfig()}>Save
+
+ +
+ +
+
+ +
+ Use Custom DNS +
+
+
+ +
+
Primary DNS
+ UpdateInput("DNS", "DNS1", e.target.value)} + value={inputs["DNS"] ? inputs["DNS"].DNS1 : ""} /> +
+ +
+
Backup DNS
+ UpdateInput("DNS", "DNS2", e.target.value)} + value={inputs["DNS"] ? inputs["DNS"].DNS2 : ""} /> +
-
-
- -
- Advanced Mode -
-
-
+
-
-
- -
- Auto Reconnect -
-
-
+
UpdateRouterFile(false)} className="title neutral-color">Select A Router File
- {props.advancedMode && - <> - -
-
- -
- Killswitch -
-
-
+
+
+
UpdateRouterFile(true)} >Clear Router File
+
-
-
- -
- Disable IPv6 -
-
-
+
-
-
- -
- Logging -
-
-
- + } - } +
+
+ +
Domain Blocking
+
ApplyBlocklistConfigurations()}>Save
+
+
+
Enabling blocklists will increase memory usage
+
+
+
+ +
+ Log blocked domains +
+
+
+
+
+
ToggleAllBlocking(true)} >Enable All
+
ToggleAllBlocking(false)} >Disable All
+
-
-
Reset()} >Reset Network Settings
-
+
-
-
ResetApp()} >Reset App Settings
-
+ {blockList?.map((item) => { + return ( -
+
+
+ +
+ {item.Tag} ( {item.Domains}{' Domains'} ) +
+
+
+ ) - {props.advancedMode && -
-
- -
Routing
-
UpdateConfig()}>Save
-
- -
- -
-
Primary DNS
- UpdateInput("DNS", "DNS1", e.target.value)} - value={inputs["DNS"] ? inputs["DNS"].DNS1 : ""} /> -
- -
-
Backup DNS
- UpdateInput("DNS", "DNS2", e.target.value)} - value={inputs["DNS"] ? inputs["DNS"].DNS2 : ""} /> -
- -
+ })} -
UpdateRouterFile(false)} className="title neutral-color">Select A Router File
-
- -
-
UpdateRouterFile(true)} >Clear Router File
-
- -
+
- } - -
-
- -
Domain Blocking
-
ApplyBlocklistConfigurations()}>Save
-
- - -
-
Enabling blocklists will increase memory usage
-
- -
-
ToggleAllBlocking(true)} >Enable All
-
ToggleAllBlocking(false)} >Disable All
-
-
-
- - {blockList?.map((item) => { - return ( - -
-
- -
- {item.Tag} ( {item.Domains}{' Domains'} ) -
-
-
- ) - - - })} - - -
- - - {/*
+ {/*
@@ -868,115 +917,116 @@ const Settings = (props) => {
*/} -
- -
- -
Other
- {user && -
UpdateUser()}>Save
- } -
- -
- - {user && -
-
Account ID
-
{user._id}
-
- } - - {user && -
-
Cash Payment Code
-
{user.CashCode}
-
- } - - {(user && props.advancedMode) && - <> - {inputs["APIKey"] && -
-
API Key
-
{inputs["APIKey"].APIKey}
-
- } -
-
CreateApiKey()} >Refresh API Key
-
- - } - - {props.state?.LogPath && -
-
Log File
-
- {props.state.LogFileName} -
-
- } - - {props.state?.ConfigPath && -
-
Config Backup
-
- {props.state.ConfigPath} -
-
- } - {props.state?.C?.RouterFilePath !== "" && -
-
Router File
-
- {props.state?.C?.RouterFilePath} -
-
- } - -
- - {props.advancedMode && - -
- -
- -
Experimental Features
- {user && -
UpdateConfig()}>Save
- } -
- -
-
All features in this section are experimental, use with caution
-
- -
-
- -
- Close Sockets On Connect -
-
-
-
This feature will attempt to close TCP sockets when the VPN connects, preventing network leakage outside the VPN connection. Currently this feature only works on Windows
-
-
- - -
- } - - - - -
- ) +
+ +
+ +
Other
+ {user && +
UpdateUser()}>Save
+ } +
+ +
+ + {user && +
+
Account ID
+
{user._id}
+
+ } + + {user && +
+
Cash Payment Code
+
{user.CashCode}
+
+ } + + {(user && props.advancedMode) && + <> + {inputs["APIKey"] && +
+
API Key
+
{inputs["APIKey"].APIKey}
+
+ } +
+
CreateApiKey()} >Refresh API Key
+
+ + } + + {props.state?.LogPath && +
+
Log File
+
+ {props.state.LogFileName} +
+
+ } + + {props.state?.ConfigPath && +
+
Config Backup
+
+ {props.state.ConfigPath} +
+
+ } + {props.state?.C?.RouterFilePath !== "" && +
+
Router File
+
+ {props.state?.C?.RouterFilePath} +
+
+ } + +
+ + {props.advancedMode && + +
+ +
+ +
Experimental Features
+ {user && +
UpdateConfig()}>Save
+ } +
+ +
+
All features in this section are experimental, use with caution
+
+ +
+
+ +
+ Close Sockets On Connect +
+
+
+
This feature will attempt to close TCP sockets when the VPN connects, preventing network leakage outside the VPN connection. Currently this feature only works on Windows
+
+
+ + +
+ } + + + + +
+ ) } -export default Settings; \ No newline at end of file + +export default Settings diff --git a/frontend/src/App/SideBar.jsx b/frontend/src/App/SideBar.jsx index c4c7746..faea5ca 100644 --- a/frontend/src/App/SideBar.jsx +++ b/frontend/src/App/SideBar.jsx @@ -2,251 +2,253 @@ import { useNavigate, useLocation } from "react-router-dom"; import React, { useEffect, useState } from "react"; import { ActivityLogIcon, BarChartIcon, ChatBubbleIcon, DesktopIcon, EnterIcon, ExitIcon, ExternalLinkIcon, FileTextIcon, GearIcon, GlobeIcon, HamburgerMenuIcon, LinkBreak2Icon, MobileIcon, Share1Icon, } from '@radix-ui/react-icons' -import Loader from "react-spinners/PacmanLoader"; +import Loader from "react-spinners/ClockLoader"; import toast from 'react-hot-toast'; +import x from "../assets/images/ripple.svg" import { OpenURL } from "../../wailsjs/go/main/App"; import STORE from "../store"; const SideBar = (props) => { - const navigate = useNavigate(); - const location = useLocation(); - const [menuTab, setMenuTab] = useState(1) - - let { pathname } = location - let sp = pathname.split("/") - - const clickHandler = (path) => { - console.log("navigating to:", path) - navigate(path) - } - - const HandleLogout = async () => { - props.toggleLoading({ logTag: "disconnect", tag: "LOGOUT", show: true, msg: "Disconnecting and logging out...", includeLogs: true }) - await props.disconnectFromVPN() - STORE.CleanupOnLogout() - navigate("/login") - } - - const ConfirmLogout = () => { - - toast.error((t) => - ( -
-
- Are you sure you want to -
-
- Logout -
- - -
- - ), { id: "logout", duration: 999999 } - ) - } - - const ConfirmDisconnect = () => { - - toast.error((t) => - ( -
-
- Are you sure you want to -
-
- Disconnect -
- - -
- - ), { id: "logout", duration: 999999 } - ) - } - - let user = STORE.GetUser() - let hasSub = false - let needsUpdate = false - - if (user) { - if (user.Version !== props.state.Version) { - needsUpdate = true - } - - if (user.SubLevel === 666) { - hasSub = false - } else if (user.SubLevel > 0) { - hasSub = true - } - } - - const RenderPopovers = () => { - return ( -
- {needsUpdate && -
- A new version is available
- -
OpenURL("https://nicelandvpn.is")}>Please visit the website to download the latest version
-
- } - - {(!hasSub && user) && -
- No active subscription
-
OpenURL("https://www.nicelandvpn.is/#/pricing")} >Visit the website to subscribe
-
- } -
- ) - } - - if (!props.state) { - return ( -
-
setMenuTab(1)}> {`<`} Menu
-
Loading state ...
-
- ) - } - - - return ( -
- {RenderPopovers()} - {props.loading && -
- -
- } - {!props.loading && -
-
- } - -
- {!user && -
clickHandler("/login")} > - -
- Login -
-
- - } - {user && -
clickHandler("/")} > - -
- VPNs -
-
- } - - {props.advancedMode && - - <> -
clickHandler("/routers")} > - -
- Routers -
-
- - {!props.stats && -
props.setStats(true)} > - -
- Stats -
-
- } - - {props.stats && -
props.setStats(false)} > - -
- Stats -
-
- } - - - - } - - - -
clickHandler("/settings")} > - -
- Settings -
-
- -
clickHandler("/logs")} > - -
- Logs -
-
- -
clickHandler("/support")} > - -
- Help -
-
- - {props.state?.ActiveAccessPoint && -
ConfirmDisconnect()} > - -
- Disconnect -
-
- } - - - {user && -
ConfirmLogout()} > - -
- Logout -
-
- } - - -
-
- ) + const navigate = useNavigate(); + const location = useLocation(); + const [menuTab, setMenuTab] = useState(1) + + let { pathname } = location + let sp = pathname.split("/") + + const clickHandler = (path) => { + console.log("navigating to:", path) + navigate(path) + } + + const HandleLogout = async () => { + props.toggleLoading({ logTag: "disconnect", tag: "LOGOUT", show: true, msg: "Disconnecting and logging out...", includeLogs: true }) + await props.disconnectFromVPN() + STORE.CleanupOnLogout() + navigate("/login") + } + + const ConfirmLogout = () => { + + toast.error((t) => + ( +
+
+ Are you sure you want to +
+
+ Logout +
+ + +
+ + ), { id: "logout", duration: 999999 } + ) + } + + const ConfirmDisconnect = () => { + + toast.error((t) => + ( +
+
+ Are you sure you want to +
+
+ Disconnect +
+ + +
+ + ), { id: "logout", duration: 999999 } + ) + } + + let user = STORE.GetUser() + let hasSub = false + let needsUpdate = false + + if (user) { + if (user.Version !== props.state.Version) { + needsUpdate = true + } + + if (user.SubLevel === 666) { + hasSub = false + } else if (user.SubLevel > 0) { + hasSub = true + } + } + + const RenderPopovers = () => { + return ( +
+ {needsUpdate && +
+ A new version is available
+ +
OpenURL("https://nicelandvpn.is/#/download")}>Click here to download the latest version
+
+ } + + {(!hasSub && user) && +
+ No active subscription
+
OpenURL("https://www.nicelandvpn.is/#/pricing")} >Click here to subscribe
+
+ } +
+ ) + } + + if (!props.state) { + return ( +
+
setMenuTab(1)}> {`<`} Menu
+
Loading state ...
+
+ ) + } + + + return ( +
+ {RenderPopovers()} + {props.loading && + + //
+ // + //
+ } + {!props.loading && +
+
+ } + +
+ {!user && +
clickHandler("/login")} > + +
+ Login +
+
+ + } + {user && +
clickHandler("/")} > + +
+ VPNs +
+
+ } + + {props.advancedMode && + + <> +
clickHandler("/routers")} > + +
+ Routers +
+
+ + {!props.stats && +
props.setStats(true)} > + +
+ Stats +
+
+ } + + {props.stats && +
props.setStats(false)} > + +
+ Stats +
+
+ } + + + + } + + + +
clickHandler("/settings")} > + +
+ Settings +
+
+ +
clickHandler("/logs")} > + +
+ Logs +
+
+ +
clickHandler("/support")} > + +
+ Help +
+
+ + {props.state?.ActiveAccessPoint && +
ConfirmDisconnect()} > + +
+ Disconnect +
+
+ } + + + {user && +
ConfirmLogout()} > + +
+ Logout +
+
+ } + + +
+
+ ) } -export default SideBar; \ No newline at end of file +export default SideBar; diff --git a/frontend/src/assets/images/inf.svg b/frontend/src/assets/images/inf.svg new file mode 100644 index 0000000..acd9199 --- /dev/null +++ b/frontend/src/assets/images/inf.svg @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/frontend/src/assets/images/ripple.svg b/frontend/src/assets/images/ripple.svg new file mode 100644 index 0000000..7dc4764 --- /dev/null +++ b/frontend/src/assets/images/ripple.svg @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/frontend/src/assets/style/layout.scss b/frontend/src/assets/style/layout.scss index 0720cc1..b8ee33d 100644 --- a/frontend/src/assets/style/layout.scss +++ b/frontend/src/assets/style/layout.scss @@ -12,6 +12,14 @@ html, html a { background-color: $body-dark-bg; } + .big-loader { +--wails-draggable: drag; +position: relative; +width: 120px; +height: 120px; +left: 20px; +top: 40px; + } body { height: auto; @@ -335,6 +343,11 @@ body::-webkit-scrollbar { width: 300px; .popover { + + .popover-click{ + cursor: pointer; + text-decoration: underline; + } // margin-top: 20px; padding: 10px; color: $popover-color; @@ -1109,6 +1122,11 @@ button { font-size: 0.8em; color: gray; } + + .offline { + color: rgb(151, 41, 41); + font-weight: bold; + } } .connected-flag { @@ -1710,6 +1728,7 @@ button { } .panel { + max-width:500px; margin-left: 20px; width: 250px; min-width: 250px; @@ -2947,11 +2966,10 @@ button { background: none; background-color: none; padding-left: 10px; - padding-top: 10px; + padding-top: 6px; z-index: 2000; outline: none; border: none; color: white; } } - diff --git a/go.mod b/go.mod index 5dc5e76..f4b4ba2 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/tunnels-is/nicelandvpn-desktop -go 1.19 +go 1.21 require ( github.com/charmbracelet/bubbles v0.16.1 @@ -8,6 +8,7 @@ require ( github.com/charmbracelet/lipgloss v0.7.1 github.com/go-ping/ping v1.1.0 github.com/miekg/dns v1.1.55 + github.com/muesli/termenv v0.15.1 github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8 github.com/wailsapp/wails/v2 v2.6.0 github.com/xlzd/gotp v0.1.0 @@ -16,6 +17,8 @@ require ( golang.org/x/sys v0.13.0 ) +replace github.com/wailsapp/wails/v2 => ../wails-fork/v2 + require ( github.com/atotto/clipboard v0.1.4 // indirect github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect @@ -29,6 +32,7 @@ require ( github.com/leaanthony/go-ansi-parser v1.6.0 // indirect github.com/leaanthony/gosod v1.0.3 // indirect github.com/leaanthony/slicer v1.6.0 // indirect + github.com/leaanthony/u v1.1.0 // indirect github.com/lucasb-eyer/go-colorful v1.2.0 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.19 // indirect @@ -37,7 +41,6 @@ require ( github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b // indirect github.com/muesli/cancelreader v0.2.2 // indirect github.com/muesli/reflow v0.3.0 // indirect - github.com/muesli/termenv v0.15.1 // indirect github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/rivo/uniseg v0.4.4 // indirect @@ -45,7 +48,7 @@ require ( github.com/tkrajina/go-reflector v0.5.6 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/fasttemplate v1.2.2 // indirect - github.com/wailsapp/go-webview2 v1.0.1 // indirect + github.com/wailsapp/go-webview2 v1.0.7 // indirect github.com/wailsapp/mimetype v1.4.1 // indirect golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 // indirect golang.org/x/mod v0.12.0 // indirect diff --git a/go.sum b/go.sum index 7ca3872..4be4514 100644 --- a/go.sum +++ b/go.sum @@ -40,6 +40,8 @@ github.com/leaanthony/gosod v1.0.3/go.mod h1:BJ2J+oHsQIyIQpnLPjnqFGTMnOZXDbvWtRC github.com/leaanthony/slicer v1.5.0/go.mod h1:FwrApmf8gOrpzEWM2J/9Lh79tyq8KTX5AzRtwV7m4AY= github.com/leaanthony/slicer v1.6.0 h1:1RFP5uiPJvT93TAHi+ipd3NACobkW53yUiBqZheE/Js= github.com/leaanthony/slicer v1.6.0/go.mod h1:o/Iz29g7LN0GqH3aMjWAe90381nyZlDNquK+mtH2Fj8= +github.com/leaanthony/u v1.1.0 h1:2n0d2BwPVXSUq5yhe8lJPHdxevE2qK5G99PMStMZMaI= +github.com/leaanthony/u v1.1.0/go.mod h1:9+o6hejoRljvZ3BzdYlVL0JYCwtnAsVuN9pVTQcaRfI= github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= github.com/matryer/is v1.4.0 h1:sosSmIWwkYITGrxZ25ULNDeKiMNzFSr4V/eqBQP0PeE= @@ -84,6 +86,7 @@ github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8/go.mod h1:P5HUIBuIWK github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/tkrajina/go-reflector v0.5.6 h1:hKQ0gyocG7vgMD2M3dRlYN6WBBOmdoOzJ6njQSepKdE= github.com/tkrajina/go-reflector v0.5.6/go.mod h1:ECbqLgccecY5kPmPmXg1MrHW585yMcDkVl6IvJe64T4= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= @@ -91,12 +94,10 @@ github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyC github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo= github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= -github.com/wailsapp/go-webview2 v1.0.1 h1:dEJIeEApW/MhO2tTMISZBFZPuW7kwrFA1NtgFB1z1II= -github.com/wailsapp/go-webview2 v1.0.1/go.mod h1:Uk2BePfCRzttBBjFrBmqKGJd41P6QIHeV9kTgIeOZNo= +github.com/wailsapp/go-webview2 v1.0.7 h1:s95+7irJsAsTy1RsjJ6N0cYX7tZ4gP7Uzawds0L2urs= +github.com/wailsapp/go-webview2 v1.0.7/go.mod h1:Uk2BePfCRzttBBjFrBmqKGJd41P6QIHeV9kTgIeOZNo= github.com/wailsapp/mimetype v1.4.1 h1:pQN9ycO7uo4vsUUuPeHEYoUkLVkaRntMnHJxVwYhwHs= github.com/wailsapp/mimetype v1.4.1/go.mod h1:9aV5k31bBOv5z6u+QP8TltzvNGJPmNJD4XlAL3U+j3o= -github.com/wailsapp/wails/v2 v2.6.0 h1:EyH0zR/EO6dDiqNy8qU5spaXDfkluiq77xrkabPYD4c= -github.com/wailsapp/wails/v2 v2.6.0/go.mod h1:WBG9KKWuw0FKfoepBrr/vRlyTmHaMibWesK3yz6nNiM= github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4= github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM= @@ -170,3 +171,4 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/main.go b/main.go index 1e1baab..e3fc2c7 100644 --- a/main.go +++ b/main.go @@ -17,18 +17,21 @@ import ( //go:embed all:frontend/dist var assets embed.FS -const VERSION = "1.1.5" -const PRODUCTION = false -const ENABLE_INSTERFACE = false +const ( + VERSION = "1.1.5" + PRODUCTION = true + ENABLE_INTERFACE = true +) var MONITOR = make(chan int, 200) -var APP = NewApp() -var SERVICE = NewService() +var ( + APP = NewApp() + SERVICE = NewService() +) func main() { - - var WebViewGPUPolicy = 0 + WebViewGPUPolicy := 0 for i := range os.Args { argToLower := strings.ToLower(os.Args[i]) if argToLower == "-disablegpu" { @@ -37,7 +40,7 @@ func main() { } core.PRODUCTION = PRODUCTION - core.ENABLE_INSTERFACE = ENABLE_INSTERFACE + core.ENABLE_INSTERFACE = ENABLE_INTERFACE core.GLOBAL_STATE.Version = VERSION go core.StartService(MONITOR) @@ -105,10 +108,12 @@ func main() { }, Debug: options.Debug{ - OpenInspectorOnStartup: !PRODUCTION, + OpenInspectorOnStartup: true, }, + // Debug: options.Debug{ + // OpenInspectorOnStartup: !PRODUCTION, + // }, }) - if err != nil { if core.LogFile != nil { _, _ = core.LogFile.WriteString("Unable to start the GUI(wails.io): " + err.Error()) diff --git a/service.go b/service.go index ff5b777..5a54f0a 100644 --- a/service.go +++ b/service.go @@ -196,7 +196,6 @@ func (s *Service) DisableDNSWhitelist() { func (s *Service) StartDNSCapture() { core.StartCapturing() - return } func (s *Service) StopDNSCapture() string { @@ -225,6 +224,11 @@ func (s *Service) StopDNSCapture() string { return "" } +func (s *Service) BlockedDomainLogging(enabled bool) { + core.GLOBAL_STATE.C.LogBlockedDomains = enabled + _ = core.SaveConfig() +} + func (s *Service) RebuildDomainBlocklist() { core.BuildDomainBlocklist()