github.com/TeaOSLab/EdgeNode@v1.3.8/internal/nodes/client_conn.go (about) 1 // Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved. 2 3 package nodes 4 5 import ( 6 "fmt" 7 "github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs" 8 "github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/firewallconfigs" 9 "github.com/TeaOSLab/EdgeNode/internal/conns" 10 teaconst "github.com/TeaOSLab/EdgeNode/internal/const" 11 "github.com/TeaOSLab/EdgeNode/internal/iplibrary" 12 "github.com/TeaOSLab/EdgeNode/internal/stats" 13 "github.com/TeaOSLab/EdgeNode/internal/utils" 14 connutils "github.com/TeaOSLab/EdgeNode/internal/utils/conns" 15 "github.com/TeaOSLab/EdgeNode/internal/utils/counters" 16 "github.com/TeaOSLab/EdgeNode/internal/utils/fasttime" 17 "github.com/TeaOSLab/EdgeNode/internal/waf" 18 "github.com/iwind/TeaGo/Tea" 19 "github.com/iwind/TeaGo/types" 20 "net" 21 "os" 22 "strings" 23 "sync/atomic" 24 "time" 25 ) 26 27 // ClientConn 客户端连接 28 type ClientConn struct { 29 BaseClientConn 30 31 createdAt int64 32 33 isTLS bool 34 isHTTP bool 35 hasRead bool 36 37 isLO bool // 是否为环路 38 isNoStat bool // 是否不统计带宽 39 isInAllowList bool 40 41 hasResetSYNFlood bool 42 43 lastReadAt int64 44 lastWriteAt int64 45 lastErr error 46 47 readDeadlineTime int64 48 isShortReading bool // reading header or tls handshake 49 50 isDebugging bool 51 autoReadTimeout bool 52 autoWriteTimeout bool 53 } 54 55 func NewClientConn(rawConn net.Conn, isHTTP bool, isTLS bool, isInAllowList bool) net.Conn { 56 // 是否为环路 57 var remoteAddr = rawConn.RemoteAddr().String() 58 59 var conn = &ClientConn{ 60 BaseClientConn: BaseClientConn{rawConn: rawConn}, 61 isTLS: isTLS, 62 isHTTP: isHTTP, 63 isLO: strings.HasPrefix(remoteAddr, "127.0.0.1:") || strings.HasPrefix(remoteAddr, "[::1]:"), 64 isNoStat: connutils.IsNoStatConn(remoteAddr), 65 isInAllowList: isInAllowList, 66 createdAt: fasttime.Now().Unix(), 67 } 68 69 if existsLnNodeIP(conn.RawIP()) { 70 conn.SetIsPersistent(true) 71 } 72 73 // 超时等设置 74 var globalServerConfig = sharedNodeConfig.GlobalServerConfig 75 if globalServerConfig != nil { 76 var performanceConfig = globalServerConfig.Performance 77 conn.isDebugging = performanceConfig.Debug 78 conn.autoReadTimeout = performanceConfig.AutoReadTimeout 79 conn.autoWriteTimeout = performanceConfig.AutoWriteTimeout 80 } 81 82 if isHTTP { 83 // TODO 可以在配置中设置此值 84 _ = conn.SetLinger(nodeconfigs.DefaultTCPLinger) 85 } 86 87 // 加入到Map 88 conns.SharedMap.Add(conn) 89 90 return conn 91 } 92 93 func (this *ClientConn) Read(b []byte) (n int, err error) { 94 if this.isDebugging { 95 this.lastReadAt = fasttime.Now().Unix() 96 97 defer func() { 98 if err != nil { 99 this.lastErr = fmt.Errorf("read error: %w", err) 100 } else { 101 this.lastErr = nil 102 } 103 }() 104 } 105 106 // 环路直接读取 107 if this.isLO { 108 n, err = this.rawConn.Read(b) 109 if n > 0 { 110 atomic.AddUint64(&teaconst.InTrafficBytes, uint64(n)) 111 } 112 return 113 } 114 115 // 设置读超时时间 116 if this.isHTTP && !this.isPersistent && !this.isShortReading && this.autoReadTimeout { 117 this.setHTTPReadTimeout() 118 } 119 120 // 开始读取 121 n, err = this.rawConn.Read(b) 122 if n > 0 { 123 atomic.AddUint64(&teaconst.InTrafficBytes, uint64(n)) 124 this.hasRead = true 125 } 126 127 // 检测是否为超时错误 128 var isTimeout = err != nil && os.IsTimeout(err) 129 var isHandshakeError = isTimeout && !this.hasRead 130 131 if err != nil { 132 _ = this.SetLinger(nodeconfigs.DefaultTCPLinger) 133 } 134 135 // 忽略白名单和局域网 136 if !this.isPersistent && this.isHTTP && !this.isInAllowList && !utils.IsLocalIP(this.RawIP()) { 137 // SYN Flood检测 138 if this.serverId == 0 || !this.hasResetSYNFlood { 139 var synFloodConfig = sharedNodeConfig.SYNFloodConfig() 140 if synFloodConfig != nil && synFloodConfig.IsOn { 141 if isHandshakeError { 142 this.increaseSYNFlood(synFloodConfig) 143 } else if err == nil && !this.hasResetSYNFlood { 144 this.hasResetSYNFlood = true 145 this.resetSYNFlood() 146 } 147 } 148 } 149 } 150 151 return 152 } 153 154 func (this *ClientConn) Write(b []byte) (n int, err error) { 155 if len(b) == 0 { 156 return 0, nil 157 } 158 159 if this.isDebugging { 160 this.lastWriteAt = fasttime.Now().Unix() 161 162 defer func() { 163 if err != nil { 164 this.lastErr = fmt.Errorf("write error: %w", err) 165 } else { 166 this.lastErr = nil 167 } 168 }() 169 } 170 171 // 设置写超时时间 172 if !this.isPersistent && this.autoWriteTimeout { 173 var timeoutSeconds = len(b) / 1024 174 if timeoutSeconds < 3 { 175 timeoutSeconds = 3 176 } 177 _ = this.rawConn.SetWriteDeadline(time.Now().Add(time.Duration(timeoutSeconds) * time.Second)) // TODO 时间可以设置 178 } 179 180 // 延长读超时时间 181 if this.isHTTP && !this.isPersistent && this.autoReadTimeout { 182 this.setHTTPReadTimeout() 183 } 184 185 // 开始写入 186 var before = time.Now() 187 n, err = this.rawConn.Write(b) 188 if n > 0 { 189 atomic.AddInt64(&this.totalSentBytes, int64(n)) 190 191 // 统计当前服务带宽 192 if this.serverId > 0 { 193 // TODO 需要加入在serverId绑定之前的带宽 194 if !this.isNoStat || Tea.IsTesting() { // 环路不统计带宽,避免缓存预热等行为产生带宽 195 atomic.AddUint64(&teaconst.OutTrafficBytes, uint64(n)) 196 197 var cost = time.Since(before).Seconds() 198 if cost > 1 { 199 stats.SharedBandwidthStatManager.AddBandwidth(this.userId, this.userPlanId, this.serverId, int64(float64(n)/cost), int64(n)) 200 } else { 201 stats.SharedBandwidthStatManager.AddBandwidth(this.userId, this.userPlanId, this.serverId, int64(n), int64(n)) 202 } 203 } 204 } 205 } 206 207 // 如果是写入超时,则立即关闭连接 208 if err != nil && os.IsTimeout(err) { 209 // TODO 考虑对多次慢连接的IP做出惩罚 210 conn, ok := this.rawConn.(LingerConn) 211 if ok { 212 _ = conn.SetLinger(0) 213 } 214 215 _ = this.Close() 216 } 217 218 return 219 } 220 221 func (this *ClientConn) Close() error { 222 this.isClosed = true 223 224 err := this.rawConn.Close() 225 226 // 单个服务并发数限制 227 // 不能加条件限制,因为服务配置随时有变化 228 sharedClientConnLimiter.Remove(this.rawConn.RemoteAddr().String()) 229 230 // 从conn map中移除 231 conns.SharedMap.Remove(this) 232 233 return err 234 } 235 236 func (this *ClientConn) LocalAddr() net.Addr { 237 return this.rawConn.LocalAddr() 238 } 239 240 func (this *ClientConn) RemoteAddr() net.Addr { 241 return this.rawConn.RemoteAddr() 242 } 243 244 func (this *ClientConn) SetDeadline(t time.Time) error { 245 return this.rawConn.SetDeadline(t) 246 } 247 248 func (this *ClientConn) SetReadDeadline(t time.Time) error { 249 // 如果开启了HTTP自动读超时选项,则自动控制超时时间 250 if this.isHTTP && !this.isPersistent && this.autoReadTimeout { 251 this.isShortReading = false 252 253 var unixTime = t.Unix() 254 if unixTime < 10 { 255 return nil 256 } 257 if unixTime == this.readDeadlineTime { 258 return nil 259 } 260 this.readDeadlineTime = unixTime 261 var seconds = -time.Since(t) 262 if seconds <= 0 || seconds > HTTPIdleTimeout { 263 return nil 264 } 265 if seconds < HTTPIdleTimeout-1*time.Second { 266 this.isShortReading = true 267 } 268 } 269 return this.rawConn.SetReadDeadline(t) 270 } 271 272 func (this *ClientConn) SetWriteDeadline(t time.Time) error { 273 return this.rawConn.SetWriteDeadline(t) 274 } 275 276 func (this *ClientConn) CreatedAt() int64 { 277 return this.createdAt 278 } 279 280 func (this *ClientConn) LastReadAt() int64 { 281 return this.lastReadAt 282 } 283 284 func (this *ClientConn) LastWriteAt() int64 { 285 return this.lastWriteAt 286 } 287 288 func (this *ClientConn) LastErr() error { 289 return this.lastErr 290 } 291 292 func (this *ClientConn) resetSYNFlood() { 293 counters.SharedCounter.ResetKey("SYN_FLOOD:" + this.RawIP()) 294 } 295 296 func (this *ClientConn) increaseSYNFlood(synFloodConfig *firewallconfigs.SYNFloodConfig) { 297 var ip = this.RawIP() 298 if len(ip) > 0 && !iplibrary.IsInWhiteList(ip) && (!synFloodConfig.IgnoreLocal || !utils.IsLocalIP(ip)) { 299 var result = counters.SharedCounter.IncreaseKey("SYN_FLOOD:"+ip, 60) 300 var minAttempts = synFloodConfig.MinAttempts 301 if minAttempts < 5 { 302 minAttempts = 5 303 } 304 if !this.isTLS { 305 // 非TLS,设置为两倍,防止误封 306 minAttempts = 2 * minAttempts 307 } 308 if result >= types.Uint32(minAttempts) { 309 var timeout = synFloodConfig.TimeoutSeconds 310 if timeout <= 0 { 311 timeout = 600 312 } 313 314 // 关闭当前连接 315 _ = this.SetLinger(0) 316 _ = this.Close() 317 318 waf.SharedIPBlackList.RecordIP(waf.IPTypeAll, firewallconfigs.FirewallScopeGlobal, 0, ip, fasttime.Now().Unix()+int64(timeout), 0, true, 0, 0, "疑似SYN Flood攻击,当前1分钟"+types.String(result)+"次空连接") 319 } 320 } 321 } 322 323 // 设置读超时时间 324 func (this *ClientConn) setHTTPReadTimeout() { 325 _ = this.SetReadDeadline(time.Now().Add(HTTPIdleTimeout)) 326 }