2021-03-22 09:40:50 -04:00
package monitor
import (
"fmt"
"io/ioutil"
"net/http"
2021-07-12 22:59:13 -04:00
"strings"
2021-03-22 09:40:50 -04:00
"time"
2021-05-21 23:28:43 -04:00
2021-06-21 09:30:42 -04:00
"github.com/naiba/nezha/pkg/utils"
2021-03-22 09:40:50 -04:00
)
type geoIP struct {
2022-03-31 10:59:07 -04:00
CountryCode string ` json:"country_code,omitempty" `
CountryCode2 string ` json:"countryCode,omitempty" `
IP string ` json:"ip,omitempty" `
Query string ` json:"query,omitempty" `
2022-04-04 23:54:38 -04:00
Location struct {
CountryCode string ` json:"country_code,omitempty" `
} ` json:"location,omitempty" `
2022-03-31 10:59:07 -04:00
}
func ( ip * geoIP ) Unmarshal ( body [ ] byte ) error {
if err := utils . Json . Unmarshal ( body , ip ) ; err != nil {
return err
}
if ip . IP == "" && ip . Query != "" {
ip . IP = ip . Query
}
if ip . CountryCode == "" && ip . CountryCode2 != "" {
ip . CountryCode = ip . CountryCode2
}
2022-04-04 23:54:38 -04:00
if ip . CountryCode == "" && ip . Location . CountryCode != "" {
ip . CountryCode = ip . Location . CountryCode
}
2022-03-31 10:59:07 -04:00
return nil
2021-03-22 09:40:50 -04:00
}
2021-05-21 23:28:43 -04:00
var (
2021-07-08 12:01:58 -04:00
geoIPApiList = [ ] string {
2022-04-04 23:54:38 -04:00
"https://api.myip.la/en?json" ,
2021-07-05 01:44:21 -04:00
"https://api.ip.sb/geoip" ,
2021-06-21 09:30:42 -04:00
"https://ipapi.co/json" ,
2021-07-08 12:01:58 -04:00
"http://ip-api.com/json/" ,
2022-04-25 21:26:52 -04:00
// "https://extreme-ip-lookup.com/json/", // 不精确
// "https://ip.seeip.org/geoip", // 不精确
// "https://freegeoip.app/json/", // 需要 Key
2021-05-21 23:28:43 -04:00
}
cachedIP , cachedCountry string
2021-06-21 09:30:42 -04:00
httpClientV4 = utils . NewSingleStackHTTPClient ( time . Second * 20 , time . Second * 5 , time . Second * 10 , false )
httpClientV6 = utils . NewSingleStackHTTPClient ( time . Second * 20 , time . Second * 5 , time . Second * 10 , true )
2021-05-21 23:28:43 -04:00
)
2021-03-22 09:40:50 -04:00
2022-04-12 07:02:22 -04:00
// UpdateIP 每30分钟更新一次IP地址与国家码的缓存
2021-03-22 09:40:50 -04:00
func UpdateIP ( ) {
for {
2021-07-08 12:01:58 -04:00
ipv4 := fetchGeoIP ( geoIPApiList , false )
ipv6 := fetchGeoIP ( geoIPApiList , true )
2021-09-29 07:58:02 -04:00
if ipv4 . IP == "" && ipv6 . IP == "" {
time . Sleep ( time . Minute )
continue
}
if ipv4 . IP == "" || ipv6 . IP == "" {
cachedIP = fmt . Sprintf ( "%s%s" , ipv4 . IP , ipv6 . IP )
} else {
cachedIP = fmt . Sprintf ( "%s/%s" , ipv4 . IP , ipv6 . IP )
}
2021-03-22 09:40:50 -04:00
if ipv4 . CountryCode != "" {
cachedCountry = ipv4 . CountryCode
} else if ipv6 . CountryCode != "" {
cachedCountry = ipv6 . CountryCode
}
2021-09-29 07:58:02 -04:00
time . Sleep ( time . Minute * 30 )
2021-03-22 09:40:50 -04:00
}
}
2021-05-21 23:28:43 -04:00
func fetchGeoIP ( servers [ ] string , isV6 bool ) geoIP {
2021-03-22 09:40:50 -04:00
var ip geoIP
2021-05-21 23:28:43 -04:00
var resp * http . Response
var err error
2022-03-31 20:30:44 -04:00
// 双栈支持参差不齐,不能随机请求,有些 IPv6 取不到 IP
for i := 0 ; i < len ( servers ) ; i ++ {
if isV6 {
resp , err = httpClientV6 . Get ( servers [ i ] )
} else {
resp , err = httpClientV4 . Get ( servers [ i ] )
}
if err == nil {
body , err := ioutil . ReadAll ( resp . Body )
if err != nil {
continue
}
resp . Body . Close ( )
if err := ip . Unmarshal ( body ) ; err != nil {
continue
}
// 没取到 v6 IP
if isV6 && ! strings . Contains ( ip . IP , ":" ) {
continue
}
// 没取到 v4 IP
if ! isV6 && ! strings . Contains ( ip . IP , "." ) {
continue
}
return ip
}
2021-03-22 09:40:50 -04:00
}
return ip
}
2022-03-31 10:45:26 -04:00
func httpGetWithUA ( client * http . Client , url string ) ( * http . Response , error ) {
req , err := http . NewRequest ( "GET" , url , nil )
if err != nil {
return nil , err
}
req . Header . Add ( "User-Agent" , "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.84 Safari/537.36" )
return client . Do ( req )
}