github.com/kelleygo/clashcore@v1.0.2/component/mmdb/mmdb.go (about)

     1  package mmdb
     2  
     3  import (
     4  	"context"
     5  	"io"
     6  	"net/http"
     7  	"os"
     8  	"sync"
     9  	"time"
    10  
    11  	yiclashcoreOnce "github.com/kelleygo/clashcore/common/once"
    12  	yiclashcoreHttp "github.com/kelleygo/clashcore/component/http"
    13  	C "github.com/kelleygo/clashcore/constant"
    14  	"github.com/kelleygo/clashcore/log"
    15  
    16  	"github.com/oschwald/maxminddb-golang"
    17  )
    18  
    19  type databaseType = uint8
    20  
    21  const (
    22  	typeMaxmind databaseType = iota
    23  	typeSing
    24  	typeMetaV0
    25  )
    26  
    27  var (
    28  	IPreader  IPReader
    29  	ASNreader ASNReader
    30  	IPonce    sync.Once
    31  	ASNonce   sync.Once
    32  )
    33  
    34  func LoadFromBytes(buffer []byte) {
    35  	IPonce.Do(func() {
    36  		mmdb, err := maxminddb.FromBytes(buffer)
    37  		if err != nil {
    38  			log.Fatalln("Can't load mmdb: %s", err.Error())
    39  		}
    40  		IPreader = IPReader{Reader: mmdb}
    41  		switch mmdb.Metadata.DatabaseType {
    42  		case "sing-geoip":
    43  			IPreader.databaseType = typeSing
    44  		case "Meta-geoip0":
    45  			IPreader.databaseType = typeMetaV0
    46  		default:
    47  			IPreader.databaseType = typeMaxmind
    48  		}
    49  	})
    50  }
    51  
    52  func Verify(path string) bool {
    53  	instance, err := maxminddb.Open(path)
    54  	if err == nil {
    55  		instance.Close()
    56  	}
    57  	return err == nil
    58  }
    59  
    60  func IPInstance() IPReader {
    61  	IPonce.Do(func() {
    62  		mmdbPath := C.Path.MMDB()
    63  		log.Infoln("Load MMDB file: %s", mmdbPath)
    64  		mmdb, err := maxminddb.Open(mmdbPath)
    65  		if err != nil {
    66  			log.Fatalln("Can't load MMDB: %s", err.Error())
    67  		}
    68  		IPreader = IPReader{Reader: mmdb}
    69  		switch mmdb.Metadata.DatabaseType {
    70  		case "sing-geoip":
    71  			IPreader.databaseType = typeSing
    72  		case "Meta-geoip0":
    73  			IPreader.databaseType = typeMetaV0
    74  		default:
    75  			IPreader.databaseType = typeMaxmind
    76  		}
    77  	})
    78  
    79  	return IPreader
    80  }
    81  
    82  func DownloadMMDB(path string) (err error) {
    83  	ctx, cancel := context.WithTimeout(context.Background(), time.Second*90)
    84  	defer cancel()
    85  	resp, err := yiclashcoreHttp.HttpRequest(ctx, C.MmdbUrl, http.MethodGet, http.Header{"User-Agent": {C.UA}}, nil)
    86  	if err != nil {
    87  		return
    88  	}
    89  	defer resp.Body.Close()
    90  
    91  	f, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY, 0o644)
    92  	if err != nil {
    93  		return err
    94  	}
    95  	defer f.Close()
    96  	_, err = io.Copy(f, resp.Body)
    97  
    98  	return err
    99  }
   100  
   101  func ASNInstance() ASNReader {
   102  	ASNonce.Do(func() {
   103  		ASNPath := C.Path.ASN()
   104  		log.Infoln("Load ASN file: %s", ASNPath)
   105  		asn, err := maxminddb.Open(ASNPath)
   106  		if err != nil {
   107  			log.Fatalln("Can't load ASN: %s", err.Error())
   108  		}
   109  		ASNreader = ASNReader{Reader: asn}
   110  	})
   111  
   112  	return ASNreader
   113  }
   114  
   115  func DownloadASN(path string) (err error) {
   116  	ctx, cancel := context.WithTimeout(context.Background(), time.Second*90)
   117  	defer cancel()
   118  	resp, err := yiclashcoreHttp.HttpRequest(ctx, C.ASNUrl, http.MethodGet, http.Header{"User-Agent": {C.UA}}, nil)
   119  	if err != nil {
   120  		return
   121  	}
   122  	defer resp.Body.Close()
   123  
   124  	f, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY, 0o644)
   125  	if err != nil {
   126  		return err
   127  	}
   128  	defer f.Close()
   129  	_, err = io.Copy(f, resp.Body)
   130  
   131  	return err
   132  }
   133  
   134  func ReloadIP() {
   135  	yiclashcoreOnce.Reset(&IPonce)
   136  }
   137  
   138  func ReloadASN() {
   139  	yiclashcoreOnce.Reset(&ASNonce)
   140  }