A privacy-first, self-hosted, fully open source personal knowledge management software, written in typescript and golang. (PERSONAL FORK)
at lambda-fork/main 238 lines 5.6 kB view raw
1// SiYuan - Refactor your thinking 2// Copyright (c) 2020-present, b3log.org 3// 4// This program is free software: you can redistribute it and/or modify 5// it under the terms of the GNU Affero General Public License as published by 6// the Free Software Foundation, either version 3 of the License, or 7// (at your option) any later version. 8// 9// This program is distributed in the hope that it will be useful, 10// but WITHOUT ANY WARRANTY; without even the implied warranty of 11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12// GNU Affero General Public License for more details. 13// 14// You should have received a copy of the GNU Affero General Public License 15// along with this program. If not, see <https://www.gnu.org/licenses/>. 16 17package util 18 19import ( 20 "net" 21 "net/http" 22 "net/url" 23 "os" 24 "strconv" 25 "strings" 26 "time" 27 28 "github.com/88250/gulu" 29 "github.com/88250/lute/ast" 30 "github.com/gin-gonic/gin" 31 "github.com/imroc/req/v3" 32 "github.com/siyuan-note/httpclient" 33 "github.com/siyuan-note/logging" 34) 35 36// GetPrivateIPv4s 获取本地所有的私有 IPv4 地址(排除虚拟网卡) 37func GetPrivateIPv4s() (ret []string) { 38 ret = []string{} 39 40 interfaces, err := net.Interfaces() 41 if err != nil { 42 return 43 } 44 45 // 常见的虚拟网卡名称关键字黑名单 46 virtualKeywords := []string{"docker", "veth", "br-", "vmnet", "vbox", "utun", "tun", "tap", "bridge", "cloud", "hyper-"} 47 48 for _, itf := range interfaces { 49 // 1. 基础状态过滤:必须是启动状态且不能是回环网卡 50 if itf.Flags&net.FlagUp == 0 || itf.Flags&net.FlagLoopback != 0 { 51 continue 52 } 53 54 // 2. 硬件地址过滤:物理网卡通常必须有 MAC 地址 55 if len(itf.HardwareAddr) == 0 { 56 continue 57 } 58 59 // 3. 名称过滤:排除已知虚拟网卡前缀 60 name := strings.ToLower(itf.Name) 61 isVirtual := false 62 for _, kw := range virtualKeywords { 63 if strings.Contains(name, kw) { 64 isVirtual = true 65 break 66 } 67 } 68 if isVirtual { 69 continue 70 } 71 72 // 4. 提取并校验 IP 73 addrs, err := itf.Addrs() 74 if err != nil { 75 continue 76 } 77 78 for _, addr := range addrs { 79 ipNet, ok := addr.(*net.IPNet) 80 if !ok { 81 continue 82 } 83 84 ip := ipNet.IP 85 // 仅保留 IPv4 且必须是私有局域网地址 (10.x, 172.16.x, 192.168.x) 86 if ip.To4() != nil && ip.IsPrivate() { 87 ret = append(ret, ip.String()) 88 } 89 } 90 } 91 return 92} 93 94func IsLocalHostname(hostname string) bool { 95 if "localhost" == hostname || strings.HasSuffix(hostname, ".localhost") { 96 return true 97 } 98 if ip := net.ParseIP(hostname); nil != ip { 99 return ip.IsLoopback() 100 } 101 return false 102} 103 104func IsLocalHost(host string) bool { 105 if hostname, _, err := net.SplitHostPort(strings.TrimSpace(host)); err != nil { 106 return false 107 } else { 108 return IsLocalHostname(hostname) 109 } 110} 111 112func IsLocalOrigin(origin string) bool { 113 if u, err := url.Parse(origin); err == nil { 114 return IsLocalHostname(u.Hostname()) 115 } 116 return false 117} 118 119func IsOnline(checkURL string, skipTlsVerify bool, timeout int) bool { 120 if "" == checkURL { 121 return false 122 } 123 124 u, err := url.Parse(checkURL) 125 if err != nil { 126 logging.LogWarnf("invalid check URL [%s]", checkURL) 127 return false 128 } 129 if u.Scheme == "file" { 130 filePath := strings.TrimPrefix(checkURL, "file://") 131 _, err := os.Stat(filePath) 132 return err == nil 133 } 134 135 if isOnline(checkURL, skipTlsVerify, timeout) { 136 return true 137 } 138 139 logging.LogWarnf("network is offline [checkURL=%s]", checkURL) 140 return false 141} 142 143func IsPortOpen(port string) bool { 144 timeout := time.Second 145 conn, err := net.DialTimeout("tcp", net.JoinHostPort("127.0.0.1", port), timeout) 146 if err != nil { 147 return false 148 } 149 if nil != conn { 150 conn.Close() 151 return true 152 } 153 return false 154} 155 156func isOnline(checkURL string, skipTlsVerify bool, timeout int) (ret bool) { 157 c := req.C(). 158 SetTimeout(time.Duration(timeout) * time.Millisecond). 159 SetProxy(httpclient.ProxyFromEnvironment). 160 SetUserAgent(UserAgent) 161 if skipTlsVerify { 162 c.EnableInsecureSkipVerify() 163 } 164 165 for i := 0; i < 2; i++ { 166 resp, err := c.R().Get(checkURL) 167 if resp.GetHeader("Location") != "" { 168 return true 169 } 170 171 switch err.(type) { 172 case *url.Error: 173 if err.(*url.Error).URL != checkURL { 174 // DNS 重定向 175 logging.LogWarnf("network is online [DNS redirect, checkURL=%s, retURL=%s]", checkURL, err.(*url.Error).URL) 176 return true 177 } 178 } 179 180 ret = err == nil 181 if ret { 182 break 183 } 184 185 logging.LogWarnf("check url [%s] is online failed: %s", checkURL, err) 186 time.Sleep(1 * time.Second) 187 } 188 return 189} 190 191func GetRemoteAddr(req *http.Request) string { 192 ret := req.Header.Get("X-forwarded-for") 193 ret = strings.TrimSpace(ret) 194 if "" == ret { 195 ret = req.Header.Get("X-Real-IP") 196 } 197 ret = strings.TrimSpace(ret) 198 if "" == ret { 199 return req.RemoteAddr 200 } 201 return strings.Split(ret, ",")[0] 202} 203 204func JsonArg(c *gin.Context, result *gulu.Result) (arg map[string]interface{}, ok bool) { 205 arg = map[string]interface{}{} 206 if err := c.BindJSON(&arg); err != nil { 207 result.Code = -1 208 result.Msg = "parses request failed" 209 return 210 } 211 212 ok = true 213 return 214} 215 216func InvalidIDPattern(idArg string, result *gulu.Result) bool { 217 if ast.IsNodeIDPattern(idArg) { 218 return false 219 } 220 221 result.Code = -1 222 result.Msg = "invalid ID argument" 223 return true 224} 225 226func initHttpClient() { 227 http.DefaultClient = httpclient.GetCloudFileClient2Min() 228 http.DefaultTransport = httpclient.NewTransport(false) 229} 230 231func ParsePort(portString string) (uint16, error) { 232 port, err := strconv.ParseUint(portString, 10, 16) 233 if err != nil { 234 logging.LogErrorf("parse port [%s] failed: %s", portString, err) 235 return 0, err 236 } 237 return uint16(port), nil 238}