├── .github └── workflows │ ├── remove-old-artifacts.yml │ └── sdunetd.yml ├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── UPDATELOG.md ├── checks.go ├── console.go ├── go.mod ├── go.sum ├── main.go ├── manager-instance.go ├── sdunet ├── challenge.go ├── http_linux.go ├── http_other.go └── manager.go ├── setting ├── defaults.go └── settings.go ├── utils └── utils.go └── versions.go /.github/workflows/remove-old-artifacts.yml: -------------------------------------------------------------------------------- 1 | name: Remove old artifacts 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | 7 | jobs: 8 | remove-old-artifacts: 9 | runs-on: ubuntu-latest 10 | timeout-minutes: 10 11 | 12 | steps: 13 | - name: Remove old artifacts 14 | uses: c-hive/gha-remove-artifacts@v1 15 | with: 16 | age: '1 second' 17 | # Optional inputs 18 | # skip-tags: true 19 | # the value of `skip-recent` means reserving the latest number's artifacts, instead of the latest number's commits'. 20 | skip-recent: 4 -------------------------------------------------------------------------------- /.github/workflows/sdunetd.yml: -------------------------------------------------------------------------------- 1 | name: Build sdunetd 2 | 3 | on: 4 | push: 5 | branches: [ "master" ] 6 | pull_request: 7 | branches: [ "master" ] 8 | 9 | jobs: 10 | 11 | build: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v3 15 | 16 | - name: Set up Go 17 | uses: actions/setup-go@v3 18 | with: 19 | go-version: 1.21 20 | 21 | - name: Build 22 | run: make build-all 23 | 24 | - name: Upload artifact 25 | uses: actions/upload-artifact@v2 26 | with: 27 | name: output 28 | path: build/ 29 | if-no-files-found: error 30 | 31 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # If you prefer the allow list template instead of the deny list, see community template: 2 | # https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore 3 | # 4 | # Binaries for programs and plugins 5 | *.exe 6 | *.exe~ 7 | *.dll 8 | *.so 9 | *.dylib 10 | 11 | # Test binary, built with `go test -c` 12 | *.test 13 | 14 | # Output of the go coverage tool, specifically when used with LiteIDE 15 | *.out 16 | 17 | # Dependency directories (remove the comment below to include it) 18 | # vendor/ 19 | 20 | # Go workspace file 21 | go.work 22 | 23 | # compiled files 24 | build/ -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright © 2018 Sad Pencil 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | SHELL := /bin/bash 2 | 3 | BUILD_DIR = build 4 | GO_BUILD_ARGS = -trimpath -ldflags=all="-s -w" 5 | export CGO_ENABLED = 0 6 | 7 | build: 8 | go build $(GO_BUILD_ARGS) -o $(BUILD_DIR)/sdunetd 9 | 10 | run: 11 | go run . 12 | 13 | build-all: 14 | GOOS=linux GOARCH=arm go build $(GO_BUILD_ARGS) -o $(BUILD_DIR)/sdunetd-linux-arm 15 | GOOS=linux GOARCH=arm64 go build $(GO_BUILD_ARGS) -o $(BUILD_DIR)/sdunetd-linux-arm64 16 | GOOS=linux GOARCH=386 go build $(GO_BUILD_ARGS) -o $(BUILD_DIR)/sdunetd-linux-386 17 | GOOS=linux GOARCH=amd64 go build $(GO_BUILD_ARGS) -o $(BUILD_DIR)/sdunetd-linux-amd64 18 | GOMIPS=softfloat GOOS=linux GOARCH=mips go build $(GO_BUILD_ARGS) -o $(BUILD_DIR)/sdunetd-linux-mips-softfloat 19 | GOOS=linux GOARCH=mips64 go build $(GO_BUILD_ARGS) -o $(BUILD_DIR)/sdunetd-linux-mips64 20 | GOMIPS=softfloat GOOS=linux GOARCH=mipsle go build $(GO_BUILD_ARGS) -o $(BUILD_DIR)/sdunetd-linux-mipsle-softfloat 21 | GOOS=linux GOARCH=mips64le go build $(GO_BUILD_ARGS) -o $(BUILD_DIR)/sdunetd-linux-mips64le 22 | GOOS=linux GOARCH=arm go build $(GO_BUILD_ARGS) -o $(BUILD_DIR)/sdunetd-linux-arm 23 | GOOS=linux GOARCH=arm64 go build $(GO_BUILD_ARGS) -o $(BUILD_DIR)/sdunetd-linux-arm64 24 | GOOS=windows GOARCH=amd64 go build $(GO_BUILD_ARGS) -o $(BUILD_DIR)/sdunetd-windows-amd64.exe 25 | GOOS=windows GOARCH=386 go build $(GO_BUILD_ARGS) -o $(BUILD_DIR)/sdunetd-windows-386.exe 26 | GOOS=windows GOARCH=arm64 go build $(GO_BUILD_ARGS) -o $(BUILD_DIR)/sdunetd-windows-arm64.exe 27 | GOOS=darwin GOARCH=amd64 go build $(GO_BUILD_ARGS) -o $(BUILD_DIR)/sdunetd-darwin-amd64 28 | GOOS=freebsd GOARCH=amd64 go build $(GO_BUILD_ARGS) -o $(BUILD_DIR)/sdunetd-freebsd-amd64 29 | 30 | upx: build-all 31 | upx --best --ultra-brute $(BUILD_DIR)/* 32 | 33 | clean: 34 | rm -r $(BUILD_DIR) 35 | 36 | all: build-all upx 37 | 38 | .PHONY: all build clean upx build-all run -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # sdunetd 2 | 3 | Embedded SRUN3000 Portal Client for SDU-Qingdao 4 | _________ 5 | 6 | # 适用院校 7 | 8 | 如有未列出的适用院校、科研单位或其他单位,请通过 issue 补充 9 | 10 | - 山东大学(济南无线网、青岛有线网、青岛无线网) 11 | - 中科院计算所(有线网、无线网) 12 | 13 | Ver 1.1 is once suitable for Shandong University, Qingdao Campus, up to 2018. No longer supported. 14 | 15 | Ver 2.0+ is suitable for Shandong University, Qingdao Campus, since March 2019. 16 | 17 | ## Copyright 18 | 19 | Copyright © 2018-2022 Sad Pencil 20 | 21 | MIT License 22 | 23 | ## Get the executable 24 | 25 | Static builds are available [here](https://github.com/SadPencil/sdunetd/releases). 26 | 27 | You can also compile by your self. 28 | 29 | Rename the executable to `sdunetd`. 30 | 31 | ## Generate a configuration file 32 | 33 | Run the program without any parameters and it will guide you to create a configuration file. 34 | 35 | ```bash 36 | ./sdunetd 37 | ``` 38 | 39 | ## Installation on Linux (based on systemd) 40 | 41 | 1. Copy the executable to `/usr/local/bin`, and rename it to `sdunetd` 42 | 2. `chmod +x /usr/local/bin/sdunetd` 43 | 3. Create a configuration file at `/etc/sdunetd/config.json` 44 | 4. `vi /etc/systemd/system/sdunetd.service` 45 | 46 | ```ini 47 | [Unit] 48 | Description=sdunetd 49 | After=network.target 50 | Wants=network.target 51 | 52 | [Service] 53 | Type=simple 54 | PrivateTmp=true 55 | ExecStart=/usr/local/bin/sdunetd -c /etc/sdunetd/config.json -m 56 | Restart=always 57 | 58 | [Install] 59 | WantedBy=multi-user.target 60 | ``` 61 | 62 | 6. `systemctl daemon-reload` 63 | 7. `systemctl enable sdunetd` 64 | 8. `systemctl start sdunetd` 65 | 66 | ## Installation on OpenWRT 67 | 68 | 1. Copy the executable to `/usr/local/bin`, and rename it to `sdunetd` 69 | 70 | - Note: You MUST choose proper builds according to `/proc/cpuinfo`. 71 | 72 | - Note: It might take a few minutes to copy a large file to the router. 73 | 74 | 2. `chmod +x /usr/local/bin/sdunetd` 75 | 3. Create a configuration file at `/etc/sdunetd/config.json` 76 | 4. `touch /etc/init.d/sdunetd` 77 | 5. `chmod +x /etc/init.d/sdunetd` 78 | 6. `vi /etc/init.d/sdunetd` 79 | 80 | ```shell 81 | #!/bin/sh /etc/rc.common 82 | 83 | START=60 84 | 85 | start() { 86 | (/usr/local/bin/sdunetd -c /etc/sdunetd/config.json -m -o - 2>&1 | logger -t sdunetd) & 87 | } 88 | 89 | stop() { 90 | killall sdunetd 91 | } 92 | ``` 93 | 94 | 7. `/etc/init.d/sdunetd enable` 95 | 8. `/etc/init.d/sdunetd start` 96 | 97 | ## Installation on Windows 98 | 99 | Although it is okay to create a shortcut at `startup` folder, it is better to create a service. `srvany` is a 32-bit 100 | program provided by Microsoft to let any program become a service, and you can get a 64-bit implementation at 101 | repo [birkett/srvany-ng](https://github.com/birkett/srvany-ng.git). 102 | 103 | Example: 104 | 105 | 0. Suppose `sdunetd.exe` `config.json` and `srvany.exe` are all placed at `C:\Program Files\sdunetd` 106 | 107 | 1. Create a service named `sdunetd` 108 | 109 | ```winbatch 110 | sc create "sdunetd" start= auto binPath= "C:\Program Files\sdunetd\srvany.exe" 111 | ``` 112 | 113 | 2. Import the following to the registry 114 | 115 | ```ini 116 | Windows Registry Editor Version 5.00 117 | [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\sdunetd\Parameters] 118 | "Application"="C:\\Program Files\\sdunetd\\srvany.exe" 119 | "AppDirectory"="C:\\Program Files\\sdunetd" 120 | "AppParameters"="-c \"C:\\Program Files\\sdunetd\\config.json\"" 121 | ``` 122 | 123 | ## Dynamic DNS 124 | 125 | We recommend [TimothyYe/GoDNS](https://github.com/TimothyYe/godns). In the configuration file, set `ip_interface` to 126 | your network interface to help GoDNS get the real IPv4 address. 127 | Click [here](https://github.com/TimothyYe/godns#get-an-ip-address-from-the-interface) to get detailed help. 128 | 129 | However, you can't use GoDNS behind a NAT router because the Internet traffic at SDU-Qingdao is being masqueraded, so 130 | that online services can't determine your real IPv4 address. 131 | 132 | `sdunetd` is able to detect your real IPv4 address at SDU-Qingdao no matter you are under a router or not. So, if you do 133 | need this feature, open an issue at [sdunetd](https://github.com/SadPencil/sdunetd/issues) so that we can fork a special 134 | version of GoDNS which is suitable for SDU-Qingdao. 135 | 136 | ## How to compile sdunetd 137 | 138 | Go 1.13 or higher version is **required**. 139 | 140 | This project uses **go module**. If you live in Mainland China, you might need to configure a proxy 141 | like [goproxy.cn](https://github.com/goproxy/goproxy.cn) to execute the following code. 142 | 143 | ```bash 144 | git clone https://github.com/SadPencil/sdunetd 145 | cd sdunetd 146 | make 147 | ``` 148 | 149 | To build for all supported platform: 150 | 151 | ```bash 152 | make all 153 | ``` 154 | -------------------------------------------------------------------------------- /UPDATELOG.md: -------------------------------------------------------------------------------- 1 | # Update log of sdunetd 2 | 3 | ## [v2.4.0](https://github.com/SadPencil/sdunetd/releases/tag/v2.4.0) 4 | - The network section is re-added in the configuration file. 5 | The strict mode is now re-supported, behaves like curl, but it is now a Linux-specific feature. See [this page](https://stackoverflow.com/a/73295452/7774607) for technical details. 6 | - Breaking change: the configuration file is now updated. Please re-generate the config file. 7 | - Breaking change: the version parameter is changed to `-V` instead of `-v`. 8 | - Breaking change: `-v` now stands for verbose output. 9 | - Breaking change: the log is now by default written to stderr, instead of stdout. 10 | - Breaking change: the log output is now controlled by `-o` parameter, instead of the configuration file. 11 | - Retries are now supported, specified in the configuration file. 12 | 13 | ## [v2.3.1](https://github.com/SadPencil/sdunetd/releases/tag/v2.3.1) 14 | - The network section is removed in the configuration file 15 | - Support detecting the Internet via either the auth server or the online service. In the configuration file, set `online_detection_method` to `auth` for the auth server, or `ms` for the detection url by Microsoft 16 | 17 | ## [v2.2.2](https://github.com/SadPencil/sdunetd/releases/tag/v2.2.2) 18 | 19 | The 2.2.2 version. Suitable for Shandong University, Qingdao Campus, since March 2019. 20 | 21 | Update log: 22 | 23 | - add `-l` flag to logout 24 | - add some tips on the guide 25 | 26 | ## [v2.2](https://github.com/SadPencil/sdunetd/releases/tag/v2.2) 27 | 28 | The 2.2 version. Suitable for Shandong University, Qingdao Campus, since March 2019. 29 | 30 | Update log: 31 | 32 | - add jsonp callback for disgusting 33 | 34 | ## [v2.1](https://github.com/SadPencil/sdunetd/releases/tag/v2.1) 35 | 36 | The 2.1 version. Suitable for Shandong University, Qingdao Campus, since March 2019. 37 | 38 | Update log: 39 | 40 | - `-a` flag will only output the IP address 41 | - add `-t` flag 42 | - will check whether the authenticate server reject the login, and output the given error message if so. 43 | - the return value when `-a`, `-t`, `-f` is enabled is meaningful. Returning a zero value means there is no error occurs, and vise versa. 44 | 45 | ## [v2.0](https://github.com/SadPencil/sdunetd/releases/tag/v2.0) 46 | 47 | The 2.0 version. Suitable for Shandong University, Qingdao Campus, since March 2019. 48 | 49 | ## [v1.1](https://github.com/SadPencil/sdunetd/releases/tag/v1.1) 50 | 51 | used to be suitable for SDU-Qingdao, until March 2019 52 | -------------------------------------------------------------------------------- /checks.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2018-2022 Sad Pencil 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 5 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 6 | */ 7 | 8 | package main 9 | 10 | import ( 11 | "errors" 12 | "github.com/SadPencil/sdunetd/setting" 13 | "strings" 14 | ) 15 | 16 | func checkAuthServer(settings *setting.Settings) (err error) { 17 | settings.Account.AuthServer = strings.TrimSpace(settings.Account.AuthServer) 18 | 19 | if settings.Account.AuthServer == "" { 20 | settings.Account.AuthServer = setting.DEFAULT_AUTH_SERVER 21 | } 22 | 23 | if len(settings.Account.AuthServer) >= 5 { 24 | if settings.Account.AuthServer[0:5] == "http:" { 25 | return errors.New(`I'm asking you about the server's FQDN, not the URI. Please remove "http://".`) 26 | } 27 | } 28 | if len(settings.Account.AuthServer) >= 6 { 29 | if settings.Account.AuthServer[0:6] == "https:" { 30 | return errors.New(`I'm asking you about the server's FQDN, not the URI. Please remove "https:".`) 31 | } 32 | } 33 | 34 | return nil 35 | } 36 | func checkUsername(settings *setting.Settings) (err error) { 37 | settings.Account.Username = strings.TrimSpace(settings.Account.Username) 38 | 39 | if settings.Account.Username == "" { 40 | return errors.New("Dude. I can't login without a username.") 41 | } 42 | 43 | return nil 44 | } 45 | func checkPassword(settings *setting.Settings) (err error) { 46 | settings.Account.Password = strings.TrimSpace(settings.Account.Password) 47 | 48 | if settings.Account.Password == "" { 49 | return errors.New("Give me your password, bitch. 'Cause I need it to hack into your... Ah, I mean, login the network.") 50 | } 51 | 52 | return nil 53 | } 54 | func checkScheme(settings *setting.Settings) (err error) { 55 | settings.Account.Scheme = strings.ToLower(strings.TrimSpace(settings.Account.Scheme)) 56 | 57 | if settings.Account.Scheme == "" { 58 | settings.Account.Scheme = setting.DEFAULT_AUTH_SCHEME 59 | } else if strings.Contains(settings.Account.Scheme, "fuck") { 60 | return errors.New("Fuck you! You are a fucking asshole.") 61 | } else if !(settings.Account.Scheme == "http" || settings.Account.Scheme == "https") { 62 | return errors.New("HTTP or HTTPS. Douchebag.") 63 | } 64 | return nil 65 | } 66 | func checkInterval(settings *setting.Settings) error { 67 | if settings.Control.LoopIntervalSec == 0 { 68 | return errors.New("interval should be more than 0 seconds") 69 | } 70 | return nil 71 | } 72 | -------------------------------------------------------------------------------- /console.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2018-2022 Sad Pencil 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 5 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 6 | */ 7 | 8 | package main 9 | 10 | import ( 11 | "bufio" 12 | "context" 13 | "encoding/json" 14 | "fmt" 15 | "github.com/SadPencil/sdunetd/sdunet" 16 | "github.com/SadPencil/sdunetd/setting" 17 | "github.com/SadPencil/sdunetd/utils" 18 | "golang.org/x/term" 19 | "net" 20 | "os" 21 | "runtime" 22 | "strconv" 23 | "strings" 24 | "syscall" 25 | ) 26 | 27 | func cartman() { 28 | settings := setting.NewSettings() 29 | reader := bufio.NewReader(os.Stdin) 30 | 31 | fmt.Println("To generate a config file, a few questions need to be answered. Leave it blank if you want the default answer in the bracket.") 32 | 33 | var err error 34 | 35 | for { 36 | fmt.Println() 37 | fmt.Println("Question 0. Are you ready? [Yes]") 38 | ans, err := reader.ReadString('\n') 39 | if err != nil { 40 | panic(err) 41 | } 42 | ans = strings.TrimSpace(ans) 43 | if ans == "" { 44 | fmt.Println("Cool. Let's get started.") 45 | break 46 | } else { 47 | fmt.Println("Ah, god dammit. I told you that you can just LEAVE IT BLANK if you want the default answer. Now try again.") 48 | } 49 | } 50 | 51 | for { 52 | fmt.Println() 53 | fmt.Println("Question 1. What's your username? []") 54 | 55 | settings.Account.Username, err = reader.ReadString('\n') 56 | if err != nil { 57 | panic(err) 58 | } 59 | err = checkUsername(settings) 60 | if err != nil { 61 | fmt.Println(err) 62 | } else { 63 | break 64 | } 65 | } 66 | for { 67 | fmt.Println() 68 | fmt.Println("Question 2. What's your password? []") 69 | bytePassword, err := term.ReadPassword(int(syscall.Stdin)) 70 | fmt.Println() // it's necessary to add a new line after user's input 71 | if err != nil { 72 | panic(err) 73 | } else { 74 | settings.Account.Password = string(bytePassword) 75 | } 76 | err = checkPassword(settings) 77 | if err != nil { 78 | fmt.Println(err) 79 | } else { 80 | fmt.Println("Great. Your password contains", fmt.Sprint(len(settings.Account.Password)), "characters.") 81 | break 82 | } 83 | } 84 | 85 | for { 86 | fmt.Println() 87 | fmt.Println("Question 3. What's the authentication server's ip address? [" + setting.DEFAULT_AUTH_SERVER + "]") 88 | fmt.Println("Hint: You can also write down the server's FQDN if necessary. You may specify either an IPv4 or IPv6 server.") 89 | fmt.Println("Hint: The authentication servers of SDU-Qingdao are [2001:250:5800:11::1] and 101.76.193.1.") 90 | settings.Account.AuthServer, err = reader.ReadString('\n') 91 | if err != nil { 92 | panic(err) 93 | } 94 | err = checkAuthServer(settings) 95 | if err != nil { 96 | fmt.Println(err) 97 | } else { 98 | if strings.Count(settings.Account.AuthServer, ":") >= 2 { 99 | fmt.Println("Hint: Add a pair of [] with the IPv6 address. Omit this hint if you have already done so.") 100 | fmt.Println("Example 1 \t [2001:250:5800:11::1]") 101 | fmt.Println("Example 2 \t [2001:250:5800:11::1]:8080") 102 | } 103 | 104 | break 105 | } 106 | } 107 | 108 | for { 109 | fmt.Println() 110 | fmt.Println("Question 4. Does the authentication server use HTTP, or HTTPS? [" + setting.DEFAULT_AUTH_SCHEME + "]") 111 | fmt.Println("Hint: The authentication servers of SDU-Qingdao use HTTP.") 112 | settings.Account.Scheme, err = reader.ReadString('\n') 113 | if err != nil { 114 | panic(err) 115 | } 116 | err = checkScheme(settings) 117 | if err != nil { 118 | fmt.Println(err) 119 | } else { 120 | break 121 | } 122 | } 123 | 124 | //for { 125 | // fmt.Println() 126 | // fmt.Println("Question 5. Do you want to log out the network when the program get terminated? [y/N]") 127 | // yesOrNoStr, err := reader.ReadString('\n') 128 | // if err != nil { 129 | // panic(err) 130 | // } 131 | // yesOrNoStr = strings.ToLower(strings.TrimSpace(yesOrNoStr)) 132 | // if yesOrNoStr == "" || yesOrNoStr == "n" { 133 | // settings.Control.LogoutWhenExit = false 134 | // break 135 | // } else if yesOrNoStr == "y" { 136 | // settings.Control.LogoutWhenExit = true 137 | // break 138 | // } else { 139 | // fmt.Println("All you need to do is to answer me yes or no. Don't be a pussy.") 140 | // } 141 | //} 142 | if runtime.GOOS == "linux" { 143 | for { 144 | fmt.Println() 145 | fmt.Println("Question 5. (Linux only)") 146 | var ips []string 147 | var interfaceStrings []string 148 | { 149 | var err error 150 | var ip string 151 | for { 152 | var manager *sdunet.Manager 153 | manager, err = getManager(context.Background(), settings) 154 | if err != nil { 155 | break 156 | } 157 | info, err := manager.GetUserInfo(context.Background()) 158 | if err != nil { 159 | break 160 | } 161 | ip = info.ClientIP 162 | break 163 | } 164 | 165 | if err != nil { 166 | ip = "" 167 | fmt.Println("Warning: Failed to connected to the authentication server.") 168 | fmt.Println(err) 169 | fmt.Println() 170 | } 171 | 172 | ips = append(ips, ip) 173 | interfaceStrings = append(interfaceStrings, "") 174 | fmt.Println("["+fmt.Sprint(len(ips)-1)+"]", "\t", ip, "\t", "[Auto detect]") 175 | } 176 | 177 | interfaces, err := net.Interfaces() 178 | if err != nil { 179 | panic(err) 180 | } 181 | 182 | for _, networkInterface := range interfaces { 183 | ip, err := utils.GetIPv4FromInterface(networkInterface.Name) 184 | if err == nil { 185 | ips = append(ips, ip) 186 | interfaceStrings = append(interfaceStrings, networkInterface.Name) 187 | fmt.Println("["+fmt.Sprint(len(ips)-1)+"]", "\t", ip, "\t", networkInterface.Name) 188 | } 189 | } 190 | 191 | // TODO: get an IPv4 and an IPv6 address from each interface 192 | 193 | if len(ips) == 0 { 194 | fmt.Println("There is not even a network interface with a valid IPv4 address.") 195 | fmt.Println("Screw you guys, I'm going home.") 196 | return 197 | } 198 | 199 | fmt.Println("Network interfaces are listed above. Which one is connected to the Portal network? [0]") 200 | fmt.Println("Hint: It is recommended to choose auto detect. Only choose a specific network interface if you have multiple network interfaces.") 201 | choice, err := reader.ReadString('\n') 202 | if err != nil { 203 | panic(err) 204 | } 205 | choice = strings.TrimSpace(choice) 206 | 207 | var choiceID int 208 | if choice == "" { 209 | choiceID = 0 210 | } else { 211 | choiceID, err = strconv.Atoi(choice) 212 | if err != nil { 213 | fmt.Println(err) 214 | continue 215 | } 216 | } 217 | 218 | if choiceID == 0 { 219 | settings.Network.Interface = "" 220 | settings.Network.StrictMode = false 221 | } else if choiceID > 0 && choiceID < len(interfaceStrings) { 222 | settings.Network.Interface = interfaceStrings[choiceID] 223 | settings.Network.StrictMode = true 224 | } else { 225 | fmt.Println("Make a valid selection, please. If you are not sure, just hit the enter key to select [0]. You are a fucking asshole.") 226 | continue 227 | } 228 | 229 | if err != nil { 230 | fmt.Println(err) 231 | } else { 232 | break 233 | } 234 | } 235 | } 236 | 237 | { 238 | fmt.Println() 239 | fmt.Println("That's all the information needed. Please save it to a configuration file. Where to save the file? [" + setting.DEFAULT_CONFIG_FILENAME + "]") 240 | fmt.Println("Hint: If the program doesn't have permission to write, it will crash.") 241 | filename, err := reader.ReadString('\n') 242 | if err != nil { 243 | panic(err) 244 | } 245 | filename = strings.TrimSpace(filename) 246 | if filename == "" { 247 | filename = setting.DEFAULT_CONFIG_FILENAME 248 | } 249 | f, err := os.Create(filename) 250 | defer f.Close() 251 | if err != nil { 252 | fmt.Println(err) 253 | } else { 254 | jsonBytes, err := json.Marshal(settings) 255 | if err != nil { 256 | fmt.Println(err) 257 | } else { 258 | _, err := f.WriteString(string(jsonBytes)) 259 | if err != nil { 260 | fmt.Println(err) 261 | } else { 262 | fmt.Println(`File saved. You may re-run the program with the "-c" flag. Example: sdunetd -c config.json`) 263 | } 264 | } 265 | } 266 | } 267 | } 268 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/SadPencil/sdunetd 2 | 3 | go 1.13 4 | 5 | require ( 6 | github.com/flowchartsman/retry v1.2.0 7 | github.com/hashicorp/go-cleanhttp v0.5.2 8 | github.com/hashicorp/go-retryablehttp v0.7.5 9 | github.com/robertkrimen/otto v0.2.1 10 | golang.org/x/sys v0.14.0 11 | golang.org/x/term v0.14.0 12 | golang.org/x/text v0.14.0 // indirect 13 | ) 14 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= 2 | github.com/chzyer/logex v1.2.1/go.mod h1:JLbx6lG2kDbNRFnfkgvh4eRJRPX1QCoOIWomwysCBrQ= 3 | github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= 4 | github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8= 5 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 6 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 7 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 8 | github.com/flowchartsman/retry v1.2.0 h1:qDhlw6RNufXz6RGr+IiYimFpMMkt77SUSHY5tgFaUCU= 9 | github.com/flowchartsman/retry v1.2.0/go.mod h1:+sfx8OgCCiAr3t5jh2Gk+T0fRTI+k52edaYxURQxY64= 10 | github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= 11 | github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= 12 | github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= 13 | github.com/hashicorp/go-hclog v0.9.2 h1:CG6TE5H9/JXsFWJCfoIVpKFIkFe6ysEuHirp4DxCsHI= 14 | github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= 15 | github.com/hashicorp/go-retryablehttp v0.7.1 h1:sUiuQAnLlbvmExtFQs72iFW/HXeUn8Z1aJLQ4LJJbTQ= 16 | github.com/hashicorp/go-retryablehttp v0.7.1/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY= 17 | github.com/hashicorp/go-retryablehttp v0.7.5 h1:bJj+Pj19UZMIweq/iie+1u5YCdGrnxCT9yvm0e+Nd5M= 18 | github.com/hashicorp/go-retryablehttp v0.7.5/go.mod h1:Jy/gPYAdjqffZ/yFGCFV2doI5wjtH1ewM9u8iYVjtX8= 19 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 20 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 21 | github.com/robertkrimen/otto v0.0.0-20221011175642-09fc211e5ab1 h1:SQiIjmrbwsmwsf68GxOPZa3y2q98Vfo41CT6h7pOMAE= 22 | github.com/robertkrimen/otto v0.0.0-20221011175642-09fc211e5ab1/go.mod h1:DKHCllR988yoiVXPZrLqCjwAKhryyDPNmb9cBVtG/aQ= 23 | github.com/robertkrimen/otto v0.2.1 h1:FVP0PJ0AHIjC+N4pKCG9yCDz6LHNPCwi/GKID5pGGF0= 24 | github.com/robertkrimen/otto v0.2.1/go.mod h1:UPwtJ1Xu7JrLcZjNWN8orJaM5n5YEtqL//farB5FlRY= 25 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 26 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 27 | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= 28 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 29 | github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= 30 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 31 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 32 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 33 | github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= 34 | github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 35 | github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= 36 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 37 | golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 38 | golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= 39 | golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= 40 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 41 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 42 | golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= 43 | golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= 44 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 45 | golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 46 | golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 47 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 48 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 49 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 50 | golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 51 | golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 52 | golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U= 53 | golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 54 | golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 55 | golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q= 56 | golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 57 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 58 | golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= 59 | golang.org/x/term v0.0.0-20221017184919-83659145692c h1:dveknrit5futqEmXAvd2I1BbZIDhxRijsyWHM86NlcA= 60 | golang.org/x/term v0.0.0-20221017184919-83659145692c/go.mod h1:VTIZ7TEbF0BS9Sv9lPTvGbtW8i4z6GGbJBCM37uMCzY= 61 | golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= 62 | golang.org/x/term v0.14.0 h1:LGK9IlZ8T9jvdy6cTdfKUCltatMFOehAQo9SRC46UQ8= 63 | golang.org/x/term v0.14.0/go.mod h1:TySc+nGkYR6qt8km8wUhuFRTVSMIX3XPR58y2lC8vww= 64 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 65 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 66 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 67 | golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= 68 | golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= 69 | golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= 70 | golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= 71 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 72 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 73 | golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= 74 | golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= 75 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 76 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 77 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 78 | gopkg.in/readline.v1 v1.0.0-20160726135117-62c6fe619375/go.mod h1:lNEQeAhU009zbRxng+XOj5ITVgY24WcbNnQopyfKoYQ= 79 | gopkg.in/sourcemap.v1 v1.0.5 h1:inv58fC9f9J3TK2Y2R1NPntXEn3/wjWHkonhIUODNTI= 80 | gopkg.in/sourcemap.v1 v1.0.5/go.mod h1:2RlvNNSMglmRrcvhfuzp4hQHwOtjxlbjX7UPY/GXb78= 81 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= 82 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 83 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 84 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 85 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2018-2022 Sad Pencil 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 5 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 6 | */ 7 | 8 | package main 9 | 10 | import ( 11 | "bytes" 12 | "context" 13 | "flag" 14 | "fmt" 15 | "github.com/SadPencil/sdunetd/sdunet" 16 | "github.com/SadPencil/sdunetd/setting" 17 | "github.com/SadPencil/sdunetd/utils" 18 | "github.com/flowchartsman/retry" 19 | "io/ioutil" 20 | "log" 21 | "net/http" 22 | "os" 23 | "os/signal" 24 | "syscall" 25 | "time" 26 | ) 27 | 28 | var logger *log.Logger = log.New(os.Stderr, "", log.LstdFlags) 29 | var verboseLogger *log.Logger = log.New(ioutil.Discard, "", 0) 30 | 31 | func version() { 32 | fmt.Println(NAME, VERSION) 33 | fmt.Println(DESCRIPTION) 34 | } 35 | 36 | func detectNetwork(ctx context.Context, settings *setting.Settings, manager *sdunet.Manager) (bool, error) { 37 | if settings.Control.OnlineDetectionMethod == setting.ONLINE_DETECTION_METHOD_AUTH { 38 | return detectNetworkWithAuthServer(ctx, manager) 39 | } else if settings.Control.OnlineDetectionMethod == setting.ONLINE_DETECTION_METHOD_MS { 40 | return detectNetworkWithMicrosoft(ctx, manager) 41 | } else { 42 | return detectNetworkWithAuthServer(ctx, manager) 43 | } 44 | } 45 | 46 | func detectNetworkWithMicrosoft(ctx context.Context, manager *sdunet.Manager) (bool, error) { 47 | client, err := manager.GetHttpClient() 48 | if err != nil { 49 | return false, err 50 | } 51 | 52 | req, err := http.NewRequestWithContext(ctx, "GET", "http://www.msftconnecttest.com/connecttest.txt", nil) 53 | if err != nil { 54 | return false, err 55 | } 56 | 57 | resp, err := client.Do(req) 58 | if err != nil { 59 | return false, err 60 | } 61 | defer resp.Body.Close() 62 | 63 | body, err := ioutil.ReadAll(resp.Body) 64 | if err != nil { 65 | return false, err 66 | } 67 | 68 | return bytes.Compare(body, []byte("Microsoft Connect Test")) == 0, nil 69 | } 70 | 71 | func detectNetworkWithAuthServer(ctx context.Context, manager *sdunet.Manager) (bool, error) { 72 | info, err := manager.GetUserInfo(ctx) 73 | if err != nil { 74 | return false, err 75 | } else { 76 | logger.Println("IP address:", info.ClientIP) 77 | return info.LoggedIn, nil 78 | } 79 | } 80 | 81 | func retryWithSettings(ctx context.Context, settings *setting.Settings, action func() error) error { 82 | return _retry(ctx, int(settings.Control.MaxRetryCount), int(settings.Control.RetryIntervalSec), action) 83 | } 84 | 85 | func _retry(ctx context.Context, retryTimes int, retryIntervalSec int, action func() error) error { 86 | retrier := retry.NewRetrier(retryTimes, time.Duration(retryIntervalSec)*time.Second, time.Duration(retryIntervalSec)*time.Second) 87 | return retrier.RunContext(ctx, func(ctx context.Context) error { 88 | return action() 89 | }) 90 | } 91 | 92 | func logout(ctx context.Context, settings *setting.Settings) error { 93 | logger.Println("Logout via web portal...") 94 | return retryWithSettings(ctx, settings, func() error { 95 | manager, err := getManager(ctx, settings) 96 | if err != nil { 97 | return err 98 | } 99 | err = manager.Logout(ctx) 100 | if err != nil { 101 | return err 102 | } 103 | logger.Println("Logged out.") 104 | return nil 105 | }) 106 | } 107 | 108 | func login(ctx context.Context, settings *setting.Settings) error { 109 | logger.Println("Log in via web portal...") 110 | return retryWithSettings(ctx, settings, func() error { 111 | manager, err := getManager(ctx, settings) 112 | if err != nil { 113 | return err 114 | } 115 | err = manager.Login(ctx, settings.Account.Password) 116 | if err != nil { 117 | return err 118 | } 119 | logger.Println("Logged in.") 120 | return nil 121 | }) 122 | } 123 | 124 | func loginIfNotOnline(ctx context.Context, settings *setting.Settings) error { 125 | isOnline := false 126 | 127 | err := retryWithSettings(ctx, settings, func() error { 128 | manager, err := getManager(ctx, settings) 129 | if err != nil { 130 | return err 131 | } 132 | isOnline, err = detectNetwork(ctx, settings, manager) 133 | return err 134 | }) 135 | 136 | if err == nil && isOnline { 137 | logger.Println("Network is up. Nothing to do.") 138 | return nil 139 | } else { 140 | // not online 141 | if err != nil { 142 | logger.Println(err) 143 | } 144 | 145 | logger.Println("Network is down.") 146 | err = login(ctx, settings) 147 | if err != nil { 148 | logger.Println(err) 149 | } 150 | return err 151 | } 152 | } 153 | 154 | func onExit(action func()) { 155 | // set up handler for SIGINT and SIGTERM 156 | c := make(chan os.Signal) 157 | signal.Notify(c, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT) 158 | go func() { 159 | for s := range c { 160 | switch s { 161 | case syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT: 162 | action() 163 | return 164 | default: 165 | } 166 | } 167 | }() 168 | } 169 | 170 | func main() { 171 | var FlagShowHelp bool 172 | flag.BoolVar(&FlagShowHelp, "h", false, "standalone: show this help.") 173 | 174 | var FlagShowVersion bool 175 | flag.BoolVar(&FlagShowVersion, "V", false, "standalone: show the version.") 176 | 177 | var FlagConfigFile string 178 | flag.StringVar(&FlagConfigFile, "c", "", "the path to the config.json file. Leave it blank to generate a new configuration file interactively.") 179 | 180 | var FlagLogOutput string 181 | flag.StringVar(&FlagLogOutput, "o", "", "the path to the output file of log message. Empty means stderr, and - means stdout.") 182 | 183 | var FlagNoAttribute bool 184 | flag.BoolVar(&FlagNoAttribute, "m", false, "option: output log without the timestamp prefix. Turn it on when running as a systemd service.") 185 | 186 | var FlagIPDetect bool 187 | flag.BoolVar(&FlagIPDetect, "a", false, "standalone: detect the IP address from the authenticate server. Useful when behind a NAT router.") 188 | 189 | var FlagOneshoot bool 190 | flag.BoolVar(&FlagOneshoot, "f", false, "standalone: login to the network for once, regardless of whether the network is offline.") 191 | 192 | var FlagTryOneshoot bool 193 | flag.BoolVar(&FlagTryOneshoot, "t", false, "standalone: login to the network for once, only if the network is offline.") 194 | 195 | var FlagLogout bool 196 | flag.BoolVar(&FlagLogout, "l", false, "standalone: logout from the network for once.") 197 | 198 | var FlagVerbose bool 199 | flag.BoolVar(&FlagVerbose, "v", false, "option: output verbose log") 200 | 201 | flag.Parse() 202 | 203 | if FlagShowVersion { 204 | version() 205 | return 206 | } 207 | if FlagShowHelp { 208 | version() 209 | flag.Usage() 210 | return 211 | } 212 | 213 | fileExist, err := utils.PathExists(FlagConfigFile) 214 | if err != nil { 215 | panic(err) 216 | } 217 | 218 | if !fileExist { 219 | version() 220 | cartman() 221 | return 222 | } 223 | 224 | settings, err := setting.LoadSettings(FlagConfigFile) 225 | if err != nil { 226 | panic(err) 227 | } 228 | //required arguments 229 | err = checkInterval(settings) 230 | if err != nil { 231 | panic(err) 232 | } 233 | err = checkAuthServer(settings) 234 | if err != nil { 235 | panic(err) 236 | } 237 | err = checkPassword(settings) 238 | if err != nil { 239 | panic(err) 240 | } 241 | err = checkScheme(settings) 242 | if err != nil { 243 | panic(err) 244 | } 245 | err = checkUsername(settings) 246 | if err != nil { 247 | panic(err) 248 | } 249 | 250 | //open the log file for writing 251 | if FlagLogOutput == "" { 252 | log.New(os.Stderr, "", log.LstdFlags) 253 | } else if FlagLogOutput == "-" { 254 | log.New(os.Stdout, "", log.LstdFlags) 255 | } else { 256 | logFile, err := os.OpenFile(FlagLogOutput, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644) 257 | defer logFile.Close() 258 | if err != nil { 259 | logger.Panicln(err) 260 | return 261 | } 262 | logger = log.New(logFile, "", log.LstdFlags) 263 | } 264 | if FlagNoAttribute { 265 | logger.SetFlags(0) 266 | } 267 | 268 | if FlagVerbose { 269 | verboseLogger = logger 270 | } 271 | 272 | if FlagIPDetect { 273 | err := retryWithSettings(context.Background(), settings, func() error { 274 | manager, err := getManager(context.Background(), settings) 275 | if err != nil { 276 | return err 277 | } 278 | info, err := manager.GetUserInfo(context.Background()) 279 | if err != nil { 280 | return err 281 | } 282 | fmt.Println(info.ClientIP) 283 | return nil 284 | }) 285 | if err != nil { 286 | logger.Panicln(err) 287 | } 288 | return 289 | } 290 | 291 | if FlagOneshoot { 292 | version() 293 | err := login(context.Background(), settings) 294 | if err != nil { 295 | logger.Panicln(err) 296 | } 297 | 298 | return 299 | } 300 | 301 | if FlagTryOneshoot { 302 | version() 303 | err := loginIfNotOnline(context.Background(), settings) 304 | if err != nil { 305 | logger.Panicln(err) 306 | } 307 | return 308 | } 309 | 310 | if FlagLogout { 311 | version() 312 | err := logout(context.Background(), settings) 313 | if err != nil { 314 | logger.Panicln(err) 315 | } 316 | return 317 | } 318 | 319 | version() 320 | 321 | // main loop 322 | ctx, cancelFunc := context.WithCancel(context.Background()) 323 | onExit(func() { 324 | logger.Println("Exiting...") 325 | cancelFunc() 326 | }) 327 | 328 | _ = loginIfNotOnline(ctx, settings) 329 | 330 | for { 331 | canceled := false 332 | 333 | select { 334 | case <-time.After(time.Duration(settings.Control.LoopIntervalSec) * time.Second): 335 | _ = loginIfNotOnline(ctx, settings) 336 | case <-ctx.Done(): 337 | canceled = true 338 | } 339 | 340 | if canceled { 341 | break 342 | } 343 | } 344 | 345 | // Cleanup 346 | if settings.Control.LogoutWhenExit { 347 | ctx, cancelFunc := context.WithCancel(context.Background()) 348 | onExit(func() { 349 | logger.Println("Force exiting. Abort logging out action...") 350 | cancelFunc() 351 | }) 352 | _ = logout(ctx, settings) 353 | } 354 | } 355 | -------------------------------------------------------------------------------- /manager-instance.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "github.com/SadPencil/sdunetd/sdunet" 6 | "github.com/SadPencil/sdunetd/setting" 7 | "time" 8 | ) 9 | 10 | var _manager *sdunet.Manager 11 | 12 | func getManager(ctx context.Context, settings *setting.Settings) (*sdunet.Manager, error) { 13 | if _manager == nil { 14 | networkInterface := "" 15 | if settings.Network.StrictMode { 16 | networkInterface = settings.Network.Interface 17 | } 18 | 19 | manager, err := sdunet.GetManager(ctx, 20 | settings.Account.Scheme, 21 | settings.Account.AuthServer, 22 | settings.Account.Username, 23 | networkInterface, 24 | ) 25 | if err != nil { 26 | return nil, err 27 | } 28 | manager.Timeout = time.Duration(settings.Network.Timeout) * time.Second 29 | manager.MaxRetryCount = int(settings.Network.MaxRetryCount) 30 | manager.RetryWait = time.Duration(settings.Network.RetryIntervalSec) * time.Second 31 | manager.Logger = verboseLogger 32 | 33 | _manager = &manager 34 | } 35 | return _manager, nil 36 | } 37 | -------------------------------------------------------------------------------- /sdunet/challenge.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2018-2022 Sad Pencil 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 5 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 6 | */ 7 | 8 | package sdunet 9 | 10 | import "github.com/robertkrimen/otto" 11 | 12 | func sdunetChallenge(username, password, localIP, token string, dataInfoStr, dataPasswordMd5Str, dataChecksumStr *string) (err error) { 13 | vm := otto.New() 14 | 15 | err = vm.Set("input_username", username) 16 | if err != nil { 17 | return err 18 | } 19 | err = vm.Set("input_password", password) 20 | if err != nil { 21 | return err 22 | } 23 | 24 | err = vm.Set("input_ip", localIP) 25 | if err != nil { 26 | return err 27 | } 28 | 29 | err = vm.Set("input_ac_id", 1) 30 | if err != nil { 31 | return err 32 | } 33 | err = vm.Set("input_token", token) 34 | if err != nil { 35 | return err 36 | } 37 | 38 | _, err = vm.Run(` 39 | 40 | 41 | /*! jshashes - New BSD License - https://github.com/h2non/jshashes */ 42 | (function(){var n;function e(n){var e,t,r="",o=-1,f;if(n&&n.length){f=n.length;while((o+=1)>>6&31,128|e&63)}else if(e<=65535){r+=String.fromCharCode(224|e>>>12&15,128|e>>>6&63,128|e&63)}else if(e<=2097151){r+=String.fromCharCode(240|e>>>18&7,128|e>>>12&63,128|e>>>6&63,128|e&63)}}}return r}function t(n){var e,t,r,o,f,i=[],h;e=t=r=o=f=0;if(n&&n.length){h=n.length;n+="";while(e191&&r<224){o=n.charCodeAt(e+1);i[t]=String.fromCharCode((r&31)<<6|o&63);e+=2}else{o=n.charCodeAt(e+1);f=n.charCodeAt(e+2);i[t]=String.fromCharCode((r&15)<<12|(o&63)<<6|f&63);e+=3}}}return i.join("")}function r(n,e){var t=(n&65535)+(e&65535),r=(n>>16)+(e>>16)+(t>>16);return r<<16|t&65535}function o(n,e){return n<>>32-e}function f(n,e){var t=e?"0123456789ABCDEF":"0123456789abcdef",r="",o,f=0,i=n.length;for(;f>>4&15)+t.charAt(o&15)}return r}function i(n){var e,t=n.length,r="";for(e=0;e>>8&255)}return r}function h(n){var e,t=n.length,r="";for(e=0;e>>8&255,n.charCodeAt(e)&255)}return r}function u(n){var e,t=n.length*32,r="";for(e=0;e>5]>>>24-e%32&255)}return r}function a(n){var e,t=n.length*32,r="";for(e=0;e>5]>>>e%32&255)}return r}function c(n){var e,t=n.length*8,r=Array(n.length>>2),o=r.length;for(e=0;e>5]|=(n.charCodeAt(e/8)&255)<>2),o=r.length;for(e=0;e>5]|=(n.charCodeAt(e/8)&255)<<24-e%32}return r}function D(n,e){var t=e.length,r=Array(),o,f,i,h,u,a,c,l;a=Array(Math.ceil(n.length/2));h=a.length;for(o=0;o0){u=Array();i=0;for(o=0;o0||f>0){u[u.length]=f}}r[r.length]=i;a=u}c="";for(o=r.length-1;o>=0;o--){c+=e.charAt(r[o])}l=Math.ceil(n.length*8/(Math.log(e.length)/Math.log(2)));for(o=c.length;on.length*8){r+=e}else{r+=t.charAt(h>>>6*(3-i)&63)}}}return r}n={VERSION:"1.0.5",Base64:function(){var n="LVoJPiCN2R8G90yg+hmFHuacZ1OWMnrsSTXkYpUq/3dlbfKwv6xztjI7DeBE45QA",r="=",o=false,f=false;this.encode=function(t){var o,i,h,u="",a=t.length;r=r||"=";t=f?e(t):t;for(o=0;oa*8){u+=r}else{u+=n.charAt(h>>>6*(3-i)&63)}}}return u};this.decode=function(e){var o,i,h,u,a,c,l,D,B,C,A="",s=[];if(!e){return e}o=C=0;e=e.replace(new RegExp("\\"+r,"gi"),"");do{a=n.indexOf(e.charAt(o+=1));c=n.indexOf(e.charAt(o+=1));l=n.indexOf(e.charAt(o+=1));D=n.indexOf(e.charAt(o+=1));B=a<<18|c<<12|l<<6|D;i=B>>16&255;h=B>>8&255;u=B&255;C+=1;if(l===64){s[C]=String.fromCharCode(i)}else if(D===64){s[C]=String.fromCharCode(i,h)}else{s[C]=String.fromCharCode(i,h,u)}}while(o>>8^r}return(t^-1)>>>0},MD5:function(n){var t=n&&typeof n.uppercase==="boolean"?n.uppercase:false,i=n&&typeof n.pad==="string"?n.pda:"=",h=n&&typeof n.utf8==="boolean"?n.utf8:true;this.hex=function(n){return f(u(n,h),t)};this.b64=function(n){return B(u(n),i)};this.any=function(n,e){return D(u(n,h),e)};this.raw=function(n){return u(n,h)};this.hex_hmac=function(n,e){return f(l(n,e),t)};this.b64_hmac=function(n,e){return B(l(n,e),i)};this.any_hmac=function(n,e,t){return D(l(n,e),t)};this.vm_test=function(){return hex("abc").toLowerCase()==="900150983cd24fb0d6963f7d28e17f72"};this.setUpperCase=function(n){if(typeof n==="boolean"){t=n}return this};this.setPad=function(n){i=n||i;return this};this.setUTF8=function(n){if(typeof n==="boolean"){h=n}return this};function u(n){n=h?e(n):n;return a(C(c(n),n.length*8))}function l(n,t){var r,o,f,i,u;n=h?e(n):n;t=h?e(t):t;r=c(n);if(r.length>16){r=C(r,n.length*8)}o=Array(16),f=Array(16);for(u=0;u<16;u+=1){o[u]=r[u]^909522486;f[u]=r[u]^1549556828}i=C(o.concat(c(t)),512+t.length*8);return a(C(f.concat(i),512+128))}function C(n,e){var t,o,f,i,h,u=1732584193,a=-271733879,c=-1732584194,l=271733878;n[e>>5]|=128<>>9<<4)+14]=e;for(t=0;t16){r=C(r,n.length*8)}o=Array(16),f=Array(16);for(i=0;i<16;i+=1){o[i]=r[i]^909522486;f[i]=r[i]^1549556828}a=C(o.concat(l(t)),512+t.length*8);return u(C(f.concat(a),512+160))}function C(n,e){var t,f,i,h,u,a,c,l,D=Array(80),B=1732584193,C=-271733879,w=-1732584194,F=271733878,E=-1009589776;n[e>>5]|=128<<24-e%32;n[(e+64>>9<<4)+15]=e;for(t=0;t16){f=m(f,n.length*8)}for(;o<16;o+=1){h[o]=f[o]^909522486;a[o]=f[o]^1549556828}r=m(h.concat(l(t)),512+t.length*8);return u(m(a.concat(r),512+256))}function C(n,e){return n>>>e|n<<32-e}function A(n,e){return n>>>e}function s(n,e,t){return n&e^~n&t}function w(n,e,t){return n&e^n&t^e&t}function F(n){return C(n,2)^C(n,13)^C(n,22)}function E(n){return C(n,6)^C(n,11)^C(n,25)}function d(n){return C(n,7)^C(n,18)^A(n,3)}function g(n){return C(n,17)^C(n,19)^A(n,10)}function p(n){return C(n,28)^C(n,34)^C(n,39)}function y(n){return C(n,14)^C(n,18)^C(n,41)}function b(n){return C(n,1)^C(n,8)^A(n,7)}function v(n){return C(n,19)^C(n,61)^A(n,6)}h=[1116352408,1899447441,-1245643825,-373957723,961987163,1508970993,-1841331548,-1424204075,-670586216,310598401,607225278,1426881987,1925078388,-2132889090,-1680079193,-1046744716,-459576895,-272742522,264347078,604807628,770255983,1249150122,1555081692,1996064986,-1740746414,-1473132947,-1341970488,-1084653625,-958395405,-710438585,113926993,338241895,666307205,773529912,1294757372,1396182291,1695183700,1986661051,-2117940946,-1838011259,-1564481375,-1474664885,-1035236496,-949202525,-778901479,-694614492,-200395387,275423344,430227734,506948616,659060556,883997877,958139571,1322822218,1537002063,1747873779,1955562222,2024104815,-2067236844,-1933114872,-1866530822,-1538233109,-1090935817,-965641998];function m(n,e){var t=[1779033703,-1150833019,1013904242,-1521486534,1359893119,-1694144372,528734635,1541459225];var o=new Array(64);var f,i,u,a,c,l,D,B;var C,A,p,y;n[e>>5]|=128<<24-e%32;n[(e+64>>9<<4)+15]=e;for(C=0;C32){i=c(i,n.length*8)}for(;f<32;f+=1){h[f]=i[f]^909522486;a[f]=i[f]^1549556828}r=c(h.concat(l(t)),1024+t.length*8);return u(c(a.concat(r),1024+512))}function c(n,e){var t,r,o,f=new Array(80),h=new Array(16),u=[new C(1779033703,-205731576),new C(-1150833019,-2067093701),new C(1013904242,-23791573),new C(-1521486534,1595750129),new C(1359893119,-1377402159),new C(-1694144372,725511199),new C(528734635,-79577749),new C(1541459225,327033209)],a=new C(0,0),c=new C(0,0),l=new C(0,0),D=new C(0,0),B=new C(0,0),p=new C(0,0),y=new C(0,0),b=new C(0,0),v=new C(0,0),m=new C(0,0),x=new C(0,0),_=new C(0,0),S=new C(0,0),U=new C(0,0),j=new C(0,0),M=new C(0,0),T=new C(0,0);if(i===undefined){i=[new C(1116352408,-685199838),new C(1899447441,602891725),new C(-1245643825,-330482897),new C(-373957723,-2121671748),new C(961987163,-213338824),new C(1508970993,-1241133031),new C(-1841331548,-1357295717),new C(-1424204075,-630357736),new C(-670586216,-1560083902),new C(310598401,1164996542),new C(607225278,1323610764),new C(1426881987,-704662302),new C(1925078388,-226784913),new C(-2132889090,991336113),new C(-1680079193,633803317),new C(-1046744716,-815192428),new C(-459576895,-1628353838),new C(-272742522,944711139),new C(264347078,-1953704523),new C(604807628,2007800933),new C(770255983,1495990901),new C(1249150122,1856431235),new C(1555081692,-1119749164),new C(1996064986,-2096016459),new C(-1740746414,-295247957),new C(-1473132947,766784016),new C(-1341970488,-1728372417),new C(-1084653625,-1091629340),new C(-958395405,1034457026),new C(-710438585,-1828018395),new C(113926993,-536640913),new C(338241895,168717936),new C(666307205,1188179964),new C(773529912,1546045734),new C(1294757372,1522805485),new C(1396182291,-1651133473),new C(1695183700,-1951439906),new C(1986661051,1014477480),new C(-2117940946,1206759142),new C(-1838011259,344077627),new C(-1564481375,1290863460),new C(-1474664885,-1136513023),new C(-1035236496,-789014639),new C(-949202525,106217008),new C(-778901479,-688958952),new C(-694614492,1432725776),new C(-200395387,1467031594),new C(275423344,851169720),new C(430227734,-1194143544),new C(506948616,1363258195),new C(659060556,-544281703),new C(883997877,-509917016),new C(958139571,-976659869),new C(1322822218,-482243893),new C(1537002063,2003034995),new C(1747873779,-692930397),new C(1955562222,1575990012),new C(2024104815,1125592928),new C(-2067236844,-1578062990),new C(-1933114872,442776044),new C(-1866530822,593698344),new C(-1538233109,-561857047),new C(-1090935817,-1295615723),new C(-965641998,-479046869),new C(-903397682,-366583396),new C(-779700025,566280711),new C(-354779690,-840897762),new C(-176337025,-294727304),new C(116418474,1914138554),new C(174292421,-1563912026),new C(289380356,-1090974290),new C(460393269,320620315),new C(685471733,587496836),new C(852142971,1086792851),new C(1017036298,365543100),new C(1126000580,-1676669620),new C(1288033470,-885112138),new C(1501505948,-60457430),new C(1607167915,987167468),new C(1816402316,1246189591)]}for(r=0;r<80;r+=1){f[r]=new C(0,0)}n[e>>5]|=128<<24-(e&31);n[(e+128>>10<<5)+31]=e;o=n.length;for(r=0;r>>t|e.h<<32-t;n.h=e.h>>>t|e.l<<32-t}function w(n,e,t){n.l=e.h>>>t|e.l<<32-t;n.h=e.l>>>t|e.h<<32-t}function F(n,e,t){n.l=e.l>>>t|e.h<<32-t;n.h=e.h>>>t}function E(n,e,t){var r=(e.l&65535)+(t.l&65535);var o=(e.l>>>16)+(t.l>>>16)+(r>>>16);var f=(e.h&65535)+(t.h&65535)+(o>>>16);var i=(e.h>>>16)+(t.h>>>16)+(f>>>16);n.l=r&65535|o<<16;n.h=f&65535|i<<16}function d(n,e,t,r,o){var f=(e.l&65535)+(t.l&65535)+(r.l&65535)+(o.l&65535);var i=(e.l>>>16)+(t.l>>>16)+(r.l>>>16)+(o.l>>>16)+(f>>>16);var h=(e.h&65535)+(t.h&65535)+(r.h&65535)+(o.h&65535)+(i>>>16);var u=(e.h>>>16)+(t.h>>>16)+(r.h>>>16)+(o.h>>>16)+(h>>>16);n.l=f&65535|i<<16;n.h=h&65535|u<<16}function g(n,e,t,r,o,f){var i=(e.l&65535)+(t.l&65535)+(r.l&65535)+(o.l&65535)+(f.l&65535),h=(e.l>>>16)+(t.l>>>16)+(r.l>>>16)+(o.l>>>16)+(f.l>>>16)+(i>>>16),u=(e.h&65535)+(t.h&65535)+(r.h&65535)+(o.h&65535)+(f.h&65535)+(h>>>16),a=(e.h>>>16)+(t.h>>>16)+(r.h>>>16)+(o.h>>>16)+(f.h>>>16)+(u>>>16);n.l=i&65535|h<<16;n.h=u&65535|a<<16}},RMD160:function(n){var t=n&&typeof n.uppercase==="boolean"?n.uppercase:false,i=n&&typeof n.pad==="string"?n.pda:"=",h=n&&typeof n.utf8==="boolean"?n.utf8:true,u=[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,7,4,13,1,10,6,15,3,12,0,9,5,2,14,11,8,3,10,14,4,9,15,8,1,2,7,0,6,13,11,5,12,1,9,11,10,0,8,12,4,13,3,7,15,14,5,6,2,4,0,5,9,7,12,2,10,14,1,3,8,11,6,15,13],a=[5,14,7,0,9,2,11,4,13,6,15,8,1,10,3,12,6,11,3,7,0,13,5,10,14,15,8,12,4,9,1,2,15,5,1,3,7,14,6,9,11,8,12,2,10,0,4,13,8,6,4,1,3,11,15,0,5,12,2,13,9,7,10,14,12,15,10,4,1,5,8,7,6,2,13,14,0,3,9,11],l=[11,14,15,12,5,8,7,9,11,13,14,15,6,7,9,8,7,6,8,13,11,9,7,15,7,12,15,9,11,7,13,12,11,13,6,7,14,9,13,15,14,8,13,6,5,12,7,5,11,12,14,15,14,15,9,8,9,14,5,6,8,6,5,12,9,15,5,11,6,8,13,12,5,12,13,14,11,8,5,6],C=[8,9,9,11,13,15,15,5,7,7,8,11,14,14,12,6,9,13,15,7,12,8,9,11,7,7,12,7,6,15,13,11,9,7,15,11,8,6,6,14,12,13,5,14,13,13,7,5,15,5,8,11,14,14,6,14,6,9,12,9,12,5,15,8,8,5,12,9,12,5,14,6,8,13,6,5,15,13,11,11];this.hex=function(n){return f(A(n,h))};this.b64=function(n){return B(A(n,h),i)};this.any=function(n,e){return D(A(n,h),e)};this.raw=function(n){return A(n,h)};this.hex_hmac=function(n,e){return f(s(n,e))};this.b64_hmac=function(n,e){return B(s(n,e),i)};this.any_hmac=function(n,e,t){return D(s(n,e),t)};this.vm_test=function(){return hex("abc").toLowerCase()==="900150983cd24fb0d6963f7d28e17f72"};this.setUpperCase=function(n){if(typeof n==="boolean"){t=n}return this};this.setPad=function(n){if(typeof n!=="undefined"){i=n}return this};this.setUTF8=function(n){if(typeof n==="boolean"){h=n}return this};function A(n){n=h?e(n):n;return w(F(c(n),n.length*8))}function s(n,t){n=h?e(n):n;t=h?e(t):t;var r,o,f=c(n),i=Array(16),u=Array(16);if(f.length>16){f=F(f,n.length*8)}for(r=0;r<16;r+=1){i[r]=f[r]^909522486;u[r]=f[r]^1549556828}o=F(i.concat(c(t)),512+t.length*8);return w(F(u.concat(o),512+160))}function w(n){var e,t="",r=n.length*32;for(e=0;e>5]>>>e%32&255)}return t}function F(n,e){var t,f,i,h,c=1732584193,D=4023233417,B=2562383102,A=271733878,s=3285377520,w,F,p,y,b,v,m,x,_,S;n[e>>5]|=128<>>9<<4)+14]=e;h=n.length;for(i=0;i>> 2 & 3; 66 | for (p = 0; p < n; p++) { 67 | y = v[p + 1]; 68 | m = z >>> 5 ^ y << 2; 69 | m += (y >>> 3 ^ z << 4) ^ (d ^ y); 70 | m += k[(p & 3) ^ e] ^ z; 71 | z = v[p] = v[p] + m & (0xEFB8D130 | 0x10472ECF); 72 | } 73 | y = v[0]; 74 | m = z >>> 5 ^ y << 2; 75 | m += (y >>> 3 ^ z << 4) ^ (d ^ y); 76 | m += k[(p & 3) ^ e] ^ z; 77 | z = v[n] = v[n] + m & (0xBB390742 | 0x44C6F8BD); 78 | } 79 | 80 | function s(a, b) { 81 | var c = a.length, 82 | v = []; 83 | for (var i = 0; i < c; i += 4) { 84 | v[i >> 2] = a.charCodeAt(i) | a.charCodeAt(i + 1) << 8 | a.charCodeAt(i + 2) << 16 | a.charCodeAt(i + 3) << 24; 85 | } 86 | if (b) { 87 | v[v.length] = c; 88 | } 89 | return v; 90 | } 91 | 92 | function l(a, b) { 93 | var d = a.length, 94 | c = (d - 1) << 2; 95 | if (b) { 96 | var m = a[d - 1]; 97 | if ((m < c - 3) || (m > c)) 98 | return null; 99 | c = m; 100 | } 101 | for (var i = 0; i < d; i++) { 102 | a[i] = String.fromCharCode(a[i] & 0xff, a[i] >>> 8 & 0xff, a[i] >>> 16 & 0xff, a[i] >>> 24 & 0xff); 103 | } 104 | if (b) { 105 | return a.join('').substring(0, c); 106 | } else { 107 | return a.join(''); 108 | } 109 | } 110 | 111 | return l(v, false); 112 | } 113 | 114 | var base64 = new Hashes.Base64(); 115 | return "{SRBX1}" + base64.encode(xEncode("{\"username\":" + JSON.stringify(username)+",\"password\":"+JSON.stringify(password)+",\"ip\":" 116 | +JSON.stringify(ip)+",\"acid\":\"1\",\"enc_ver\":\"srun_bx1\"}", token)); 117 | } 118 | 119 | 120 | function getDataChecksum(username, ip, token, datainfo, dataMd5) { 121 | var n = 200, 122 | type = 1; 123 | return new Hashes.SHA1().hex(token + username + token + dataMd5 + token + "1" + token + ip + token + n + token + type + token + datainfo); 124 | } 125 | 126 | function getHmd5(password,token){ 127 | var hmd5 = new Hashes.MD5().hex_hmac(token, undefined); 128 | return hmd5; 129 | } 130 | 131 | function getDataPassword(password, token) { 132 | var hmd5 = new Hashes.MD5().hex_hmac(token, undefined); 133 | return "{MD5}" + hmd5; 134 | } 135 | 136 | 137 | dataMd5 = getHmd5(input_password, input_token); 138 | dataPasswordMd5 = getDataPassword(input_password, input_token); 139 | dataInfo = getDataInfo(input_username, input_password, input_ip, input_token); 140 | dataChecksum = getDataChecksum(input_username, input_ip, input_token, dataInfo,dataMd5); 141 | 142 | `) 143 | if err != nil { 144 | return err 145 | } 146 | 147 | dataInfo, err := vm.Get("dataInfo") 148 | if err != nil { 149 | return err 150 | } 151 | 152 | *dataInfoStr, err = dataInfo.ToString() 153 | if err != nil { 154 | return err 155 | } 156 | //fmt.Println(*dataInfoStr) 157 | 158 | dataPasswordMd5, err := vm.Get("dataPasswordMd5") 159 | if err != nil { 160 | return err 161 | } 162 | 163 | *dataPasswordMd5Str, err = dataPasswordMd5.ToString() 164 | if err != nil { 165 | return err 166 | } 167 | //fmt.Println(*dataPasswordMd5Str) 168 | 169 | dataChecksum, err := vm.Get("dataChecksum") 170 | if err != nil { 171 | return err 172 | } 173 | 174 | *dataChecksumStr, err = dataChecksum.ToString() 175 | if err != nil { 176 | return err 177 | } 178 | //fmt.Println(*dataChecksumStr) 179 | 180 | return nil 181 | } 182 | -------------------------------------------------------------------------------- /sdunet/http_linux.go: -------------------------------------------------------------------------------- 1 | //go:build linux 2 | // +build linux 3 | 4 | /* 5 | Copyright © 2018-2022 Sad Pencil 6 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 9 | */ 10 | 11 | package sdunet 12 | 13 | import ( 14 | "github.com/hashicorp/go-cleanhttp" 15 | retryableHttp "github.com/hashicorp/go-retryablehttp" 16 | "golang.org/x/sys/unix" 17 | "log" 18 | "net" 19 | "net/http" 20 | "syscall" 21 | "time" 22 | ) 23 | 24 | func getHttpClient(forceNetworkInterface string, timeout time.Duration, retryCount int, retryWait time.Duration, logger *log.Logger) (*http.Client, error) { 25 | client := retryableHttp.NewClient() 26 | client.HTTPClient.Timeout = timeout 27 | client.RetryMax = retryCount 28 | client.RetryWaitMin = retryWait 29 | client.RetryWaitMax = retryWait 30 | client.Logger = logger 31 | 32 | if forceNetworkInterface != "" { 33 | // https://iximiuz.com/en/posts/go-net-http-setsockopt-example/ 34 | // https://linux.die.net/man/7/socket 35 | transport := cleanhttp.DefaultPooledTransport() 36 | dialer := &net.Dialer{ 37 | Timeout: 30 * time.Second, 38 | KeepAlive: 30 * time.Second, 39 | DualStack: true, 40 | Control: func(network, address string, conn syscall.RawConn) error { 41 | var operr error 42 | if err := conn.Control(func(fd uintptr) { 43 | operr = unix.BindToDevice(int(fd), forceNetworkInterface) 44 | }); err != nil { 45 | return err 46 | } 47 | return operr 48 | }, 49 | } 50 | transport.DialContext = dialer.DialContext 51 | client.HTTPClient.Transport = transport 52 | } 53 | return client.StandardClient(), nil 54 | } 55 | -------------------------------------------------------------------------------- /sdunet/http_other.go: -------------------------------------------------------------------------------- 1 | //go:build !linux 2 | // +build !linux 3 | 4 | /* 5 | Copyright © 2018-2022 Sad Pencil 6 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 9 | */ 10 | 11 | package sdunet 12 | 13 | import ( 14 | "errors" 15 | retryableHttp "github.com/hashicorp/go-retryablehttp" 16 | "log" 17 | "net/http" 18 | "time" 19 | ) 20 | 21 | func getHttpClient(forceNetworkInterface string, timeout time.Duration, retryCount int, retryWait time.Duration, logger *log.Logger) (*http.Client, error) { 22 | client := retryableHttp.NewClient() 23 | client.HTTPClient.Timeout = timeout 24 | client.RetryMax = retryCount 25 | client.RetryWaitMin = retryWait 26 | client.RetryWaitMax = retryWait 27 | client.Logger = logger 28 | 29 | if forceNetworkInterface != "" { 30 | return nil, errors.New("the strict mode is only available in Linux") 31 | } 32 | return client.StandardClient(), nil 33 | } 34 | -------------------------------------------------------------------------------- /sdunet/manager.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2018-2022 Sad Pencil 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 5 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 6 | */ 7 | 8 | package sdunet 9 | 10 | import ( 11 | "bytes" 12 | "context" 13 | "encoding/json" 14 | "errors" 15 | "io/ioutil" 16 | "log" 17 | "net/http" 18 | "net/url" 19 | "time" 20 | ) 21 | 22 | type MangerBase struct { 23 | client *http.Client 24 | 25 | Scheme string 26 | Server string 27 | ForceNetworkInterface string 28 | Timeout time.Duration 29 | MaxRetryCount int 30 | RetryWait time.Duration 31 | Logger *log.Logger 32 | } 33 | 34 | type Manager struct { 35 | MangerBase 36 | Username string 37 | ClientIP string 38 | } 39 | 40 | type UserInfo struct { 41 | ClientIP string 42 | LoggedIn bool 43 | } 44 | 45 | func GetManager(ctx context.Context, scheme string, server string, username string, forceNetworkInterface string) (Manager, error) { 46 | base := MangerBase{ 47 | Scheme: scheme, 48 | Server: server, 49 | ForceNetworkInterface: forceNetworkInterface, 50 | Timeout: 3 * time.Second, 51 | MaxRetryCount: 3, 52 | RetryWait: 1 * time.Second, 53 | Logger: log.Default(), 54 | } 55 | info, err := base.GetUserInfo(ctx) 56 | if err != nil { 57 | return Manager{}, err 58 | } 59 | return Manager{ 60 | MangerBase: base, 61 | Username: username, 62 | ClientIP: info.ClientIP, 63 | }, nil 64 | } 65 | 66 | func (m MangerBase) GetHttpClient() (*http.Client, error) { 67 | if m.client == nil { 68 | client, err := getHttpClient(m.ForceNetworkInterface, m.Timeout, m.MaxRetryCount, m.RetryWait, m.Logger) 69 | if err != nil { 70 | return nil, err 71 | } 72 | m.client = client 73 | } 74 | return m.client, nil 75 | } 76 | 77 | func (m Manager) getRawChallenge(ctx context.Context) (map[string]interface{}, error) { 78 | return m.httpJsonQuery(ctx, 79 | "/cgi-bin/get_challenge", 80 | map[string][]string{ 81 | "username": {m.Username}, 82 | "ip": {m.ClientIP}, 83 | }, 84 | "jQuery", 85 | ) 86 | } 87 | 88 | func (m MangerBase) getRawUserInfo(ctx context.Context) (map[string]interface{}, error) { 89 | return m.httpJsonQuery(ctx, 90 | "/cgi-bin/rad_user_info", 91 | map[string][]string{}, 92 | "jQuery", 93 | ) 94 | } 95 | func (m MangerBase) GetUserInfo(ctx context.Context) (UserInfo, error) { 96 | output, err := m.getRawUserInfo(ctx) 97 | if err != nil { 98 | return UserInfo{}, err 99 | } 100 | return UserInfo{ 101 | ClientIP: output["online_ip"].(string), 102 | LoggedIn: output["error"].(string) == "ok", 103 | }, nil 104 | } 105 | 106 | func (m MangerBase) httpJsonQuery(ctx context.Context, relativeUrl string, getParams url.Values, jsonCallback string) (map[string]interface{}, error) { 107 | if relativeUrl[0] != '/' { 108 | return nil, errors.New("invalid relative url") 109 | } 110 | 111 | client, err := m.GetHttpClient() 112 | if err != nil { 113 | return nil, err 114 | } 115 | 116 | req, err := http.NewRequestWithContext(ctx, "GET", m.Scheme+"://"+m.Server+relativeUrl, nil) 117 | if err != nil { 118 | return nil, err 119 | } 120 | req.Header.Add("Accept", "application/json") 121 | if jsonCallback != "" { 122 | getParams.Add("callback", jsonCallback) 123 | } 124 | req.URL.RawQuery = getParams.Encode() 125 | 126 | resp, err := client.Do(req) 127 | if err != nil { 128 | return nil, err 129 | } 130 | defer resp.Body.Close() 131 | 132 | if resp.StatusCode != 200 { 133 | return nil, errors.New(resp.Status) 134 | } 135 | 136 | respBody, err := ioutil.ReadAll(resp.Body) 137 | if err != nil { 138 | return nil, err 139 | } 140 | 141 | if bytes.Equal(respBody[0:len(jsonCallback)+1], []byte(jsonCallback+"(")) { 142 | respBody = respBody[len(jsonCallback)+1:] 143 | respBody = respBody[:len(respBody)-1] 144 | } 145 | 146 | var output map[string]interface{} 147 | err = json.Unmarshal(respBody, &output) 148 | if err != nil { 149 | return nil, err 150 | } 151 | 152 | return output, nil 153 | } 154 | 155 | func (m Manager) getChallengeID(ctx context.Context) (string, error) { 156 | output, err := m.getRawChallenge(ctx) 157 | if err != nil { 158 | return "", err 159 | } 160 | return output["challenge"].(string), nil 161 | } 162 | 163 | func (m Manager) Login(ctx context.Context, password string) error { 164 | challenge, err := m.getChallengeID(ctx) 165 | if err != nil { 166 | return err 167 | } 168 | 169 | var dataInfoStr, dataPasswordMd5Str, dataChecksumStr string 170 | err = sdunetChallenge(m.Username, password, m.ClientIP, challenge, &dataInfoStr, &dataPasswordMd5Str, &dataChecksumStr) 171 | if err != nil { 172 | return err 173 | } 174 | 175 | output, err := m.httpJsonQuery(ctx, 176 | "/cgi-bin/srun_portal", 177 | map[string][]string{ 178 | "action": {"login"}, 179 | "username": {m.Username}, 180 | "password": {dataPasswordMd5Str}, 181 | "ac_id": {"1"}, 182 | "ip": {m.ClientIP}, 183 | "info": {dataInfoStr}, 184 | "chksum": {dataChecksumStr}, 185 | "n": {"200"}, 186 | "type": {"1"}, 187 | }, 188 | "jQuery", 189 | ) 190 | if err != nil { 191 | return err 192 | } 193 | 194 | errorStr := output["error"].(string) 195 | if errorStr == "ok" { 196 | return nil 197 | } else { 198 | return errors.New(errorStr) 199 | } 200 | } 201 | 202 | func (m Manager) Logout(ctx context.Context) error { 203 | output, err := m.httpJsonQuery(ctx, 204 | "/cgi-bin/srun_portal", 205 | map[string][]string{ 206 | "ac_id": {"1"}, 207 | "action": {"logout"}, 208 | "username": {m.Username}, 209 | }, 210 | "jQuery", 211 | ) 212 | if err != nil { 213 | return err 214 | } 215 | errorStr := output["error"].(string) 216 | if errorStr == "ok" { 217 | return nil 218 | } else { 219 | return errors.New(errorStr) 220 | } 221 | } 222 | -------------------------------------------------------------------------------- /setting/defaults.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2018-2022 Sad Pencil 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 5 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 6 | */ 7 | 8 | package setting 9 | 10 | const DEFAULT_AUTH_SERVER string = "101.76.193.1" 11 | const DEFAULT_AUTH_SCHEME string = "http" 12 | const DEFAULT_CONFIG_FILENAME string = "config.json" 13 | 14 | const ONLINE_DETECTION_METHOD_AUTH = "auth" 15 | const ONLINE_DETECTION_METHOD_MS = "ms" 16 | -------------------------------------------------------------------------------- /setting/settings.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2018-2022 Sad Pencil 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 5 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 6 | */ 7 | 8 | package setting 9 | 10 | import ( 11 | "encoding/json" 12 | "io/ioutil" 13 | ) 14 | 15 | type Account struct { 16 | Username string `json:"username"` 17 | Password string `json:"password"` 18 | AuthServer string `json:"server"` 19 | Scheme string `json:"scheme"` 20 | } 21 | 22 | type Network struct { 23 | Interface string `json:"interface"` 24 | StrictMode bool `json:"strict"` 25 | Timeout int32 `json:"timeout"` 26 | MaxRetryCount int32 `json:"max_retry_count"` 27 | RetryIntervalSec int32 `json:"retry_interval_sec"` 28 | } 29 | 30 | type Control struct { 31 | MaxRetryCount int32 `json:"max_retry_count"` 32 | RetryIntervalSec int32 `json:"retry_interval_sec"` 33 | LoopIntervalSec int32 `json:"loop_interval_sec"` 34 | LogoutWhenExit bool `json:"logout_when_exit"` 35 | OnlineDetectionMethod string `json:"online_detection_method"` 36 | } 37 | 38 | type Settings struct { 39 | Account Account `json:"account"` 40 | Network Network `json:"network"` 41 | Control Control `json:"control"` 42 | } 43 | 44 | func NewSettings() *Settings { 45 | return &Settings{ 46 | Account: Account{Scheme: DEFAULT_AUTH_SCHEME, AuthServer: DEFAULT_AUTH_SERVER}, 47 | Control: Control{ 48 | LoopIntervalSec: 60, 49 | RetryIntervalSec: 1, 50 | MaxRetryCount: 3, 51 | OnlineDetectionMethod: ONLINE_DETECTION_METHOD_AUTH, 52 | LogoutWhenExit: false, 53 | }, 54 | Network: Network{ 55 | Interface: "", 56 | StrictMode: false, 57 | Timeout: 3, 58 | MaxRetryCount: 3, 59 | RetryIntervalSec: 1, 60 | }, 61 | } 62 | } 63 | 64 | // LoadSettings -- Load settings from config file 65 | func LoadSettings(configPath string) (settings *Settings, err error) { 66 | // LoadSettings from config file 67 | file, err := ioutil.ReadFile(configPath) 68 | if err != nil { 69 | return nil, err 70 | } 71 | 72 | settings = NewSettings() 73 | err = json.Unmarshal(file, &settings) 74 | if err != nil { 75 | return nil, err 76 | } 77 | 78 | return settings, nil 79 | } 80 | -------------------------------------------------------------------------------- /utils/utils.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2018-2022 Sad Pencil 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 5 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 6 | */ 7 | 8 | package utils 9 | 10 | import ( 11 | "errors" 12 | "net" 13 | "os" 14 | ) 15 | 16 | // PathExists returns whether the given file or directory exists 17 | func PathExists(path string) (bool, error) { 18 | _, err := os.Stat(path) 19 | if err == nil { 20 | return true, nil 21 | } 22 | if os.IsNotExist(err) { 23 | return false, nil 24 | } 25 | return true, err 26 | } 27 | 28 | //GetIPv4FromInterface gets an IPv4 address from the specific interface 29 | func GetIPv4FromInterface(networkInterface string) (string, error) { 30 | ifaces, err := net.InterfaceByName(networkInterface) 31 | if err != nil { 32 | return "", err 33 | } 34 | 35 | addrs, err := ifaces.Addrs() 36 | if err != nil { 37 | return "", err 38 | } 39 | 40 | for _, addr := range addrs { 41 | var ip net.IP 42 | switch v := addr.(type) { 43 | case *net.IPNet: 44 | ip = v.IP 45 | case *net.IPAddr: 46 | ip = v.IP 47 | } 48 | if ip == nil { 49 | continue 50 | } 51 | 52 | if !(ip.IsGlobalUnicast() && !(ip.IsUnspecified() || ip.IsMulticast() || ip.IsLoopback() || ip.IsLinkLocalUnicast() || ip.IsLinkLocalMulticast() || ip.IsInterfaceLocalMulticast())) { 53 | continue 54 | } 55 | 56 | //the code is not ready for updating an AAAA record 57 | /* 58 | if (isIPv4(ip.String())){ 59 | if (configuration.IPType=="IPv6"){ 60 | continue; 61 | } 62 | }else{ 63 | if (configuration.IPType!="IPv6"){ 64 | continue; 65 | } 66 | } */ 67 | if ip.To4() == nil { 68 | continue 69 | } 70 | 71 | return ip.String(), nil 72 | 73 | } 74 | return "", errors.New("can't get a vaild address from " + networkInterface) 75 | } 76 | -------------------------------------------------------------------------------- /versions.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2018-2022 Sad Pencil 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 5 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 6 | */ 7 | 8 | package main 9 | 10 | const NAME string = "sdunetd" 11 | const DESCRIPTION string = "Embedded SRUN3000 Portal Client for SDU-Qingdao" 12 | const VERSION string = "v2.4.0" 13 | --------------------------------------------------------------------------------