package main // import "vpngate"

import (
    "bufio"
    "bytes"
    "encoding/base64"
    "encoding/json"
    "flag"
    "fmt"
    "io"
    "net/http"
    "os"
    "os/exec"
    "path/filepath"
    "sort"
    "strconv"
    "strings"
    "time"
)

const (
    configFile = "vpn_server.config"
)

type VPNServer struct {
    Hostname string
    IP       string
    Port     string
    Country  string
    Ping     int
    Score    int
    Config   string
}

var (
    // scoreMinimum = 2000000
    scoreMinimum   = 1
    pingMinimum    = 15
    connectionName = "VPNgateAutoConnection"

    rank       int
    disconnect bool

    notUseIPs = []string{}
)

func extractIPAndPort(config string) (string, string) {
    scanner := bufio.NewScanner(strings.NewReader(config))
    for scanner.Scan() {
        line := scanner.Text()
        if strings.HasPrefix(line, "remote ") {
            parts := strings.Fields(line)
            if len(parts) >= 3 {
                return parts[1], parts[2]
            }
        }
    }
    return "", ""
}

func findBestVPNServer(rank int) (*VPNServer, error) {
    var err error
    var servers []*VPNServer

    response, err := http.Get(vpnGateURL)
    if err != nil {
        return nil, fmt.Errorf("서버 목록 가져오기 실패: %v", err)
    }
    defer response.Body.Close()

    scanner := bufio.NewScanner(response.Body)

    for scanner.Scan() {
        line := scanner.Text()
        splits := strings.Split(line, ",")
        if len(splits) < 15 {
            continue
        }

        ping, err := strconv.Atoi(splits[3])
        if err != nil || ping > pingMinimum {
            continue
        }

        score, err := strconv.Atoi(splits[2])
        if err != nil || score < scoreMinimum {
            continue
        }

        decodedConfig, err := base64.StdEncoding.DecodeString(splits[14])
        if err != nil {
            continue
        }

        ip, port := extractIPAndPort(string(decodedConfig))
        server := &VPNServer{
            Hostname: splits[0],
            IP:       ip,
            Port:     port,
            Country:  splits[5],
            Ping:     ping,
            Score:    score,
            Config:   string(decodedConfig),
        }
        servers = append(servers, server)
    }

    if len(servers) == 0 {
        return nil, fmt.Errorf("적합한 서버를 찾을 수 없음")
    }

    if rank > len(servers) {
        return nil, fmt.Errorf("지정된 순위의 서버를 찾을 수 없음")
    }

    sort.Slice(servers, func(i, j int) bool {
        return servers[i].Ping < servers[j].Ping
    })

    // for i, s := range servers {
    //  // fmt.Println("    ip, ping, score:", ip, ping, score)
    //  fmt.Println("   i, ip, ping, score:", i+1, s.IP, s.Ping, s.Score)
    // }

    return servers[rank-1], nil
}

func saveVPNConfig(config string) (string, error) {
    absPath, err := filepath.Abs(configFile)
    if err != nil {
        return "", err
    }

    err = os.WriteFile(absPath, []byte(config), 0664)
    return absPath, err
}

func connectVPN(server *VPNServer) error {
    cmd := exec.Command("vpncmd", "/client")
    var input bytes.Buffer
    input.WriteString("\n") // localhost
    // input.WriteString("2\n") // Client mode
    input.WriteString("AccountCreate " + connectionName + " /hub:VPNGATE /username:vpn /nicname:VPN\n")
    input.WriteString(server.IP + ":" + server.Port + "\n")
    input.WriteString("AccountConnect " + connectionName + "\n")
    input.WriteString("exit\n")

    cmd.Stdin = &input

    done := make(chan error, 1)
    var output []byte
    go func() {
        var err error
        output, err = cmd.CombinedOutput()
        done <- err
    }()

    select {
    case err := <-done:
        fmt.Printf("VPN 연결 출력:\n%s", string(output))
        if err != nil {
            return fmt.Errorf("VPN 연결 실패: %s - %v", string(output), err)
        }
        return nil
    case <-time.After(30 * time.Second):
        return fmt.Errorf("VPN 연결 시간 초과")
    }
}

func getPublicIP() (string, error) {
    apis := []string{
        "https://ipinfo.io/json",
        "https://api.ip.sb/json",
    }

    client := &http.Client{Timeout: 2 * time.Second}

    for _, url := range apis {
        resp, err := client.Get(url)
        if err != nil {
            continue
        }
        defer resp.Body.Close()

        body, err := io.ReadAll(resp.Body)
        if err != nil {
            fmt.Println("ReadAll:", err)
            continue
        }

        var ipData map[string]interface{}
        err = json.Unmarshal(body, &ipData)
        if err != nil {
            fmt.Println("Unmarshall:", err)
            continue
        }

        if ip, ok := ipData["ip"].(string); ok {
            return ip, nil
        }
    }

    return "", fmt.Errorf("IP 주소를 찾을 수 없음")
}

func connectMain(rank int) error {
    var err error

    kurrentIP, err := getPublicIP()
    if err != nil {
        return fmt.Errorf("IP 주소 확인 실패: %v", err)
    }
    fmt.Println("현재 IP:", kurrentIP)
    previousIP := kurrentIP

    fmt.Println("VPN Gate 서버 목록 가져오는 중...")

    bestServer, err := findBestVPNServer(rank)
    if err != nil {
        return fmt.Errorf("서버 선택 실패: %v", err)
    }

    fmt.Printf("선택된 서버: %s (%s) - %s:%s\n",
        bestServer.Hostname,
        bestServer.Country,
        bestServer.IP,
        bestServer.Port,
    )

    configPath, err := saveVPNConfig(bestServer.Config)
    if err != nil {
        return fmt.Errorf("서버 설정 저장 실패: %v", err)
    }
    fmt.Printf("서버 설정이 %s에 저장되었습니다.", configPath)

    fmt.Println("VPN 연결 시도 중...")
    err = connectVPN(bestServer)
    if err != nil {
        return fmt.Errorf("VPN 연결 실패: %v", err)
    }

    fmt.Println("\nVPN에 연결되었습니다! 아이피 할당을 시도합니다.")

    count := 0
    for {
        kurrentIP, err = getPublicIP()
        if err != nil {
            return fmt.Errorf("IP 주소 확인 실패: %v", err)
        }

        if kurrentIP != previousIP {
            fmt.Printf("\nIP 주소가 변경되었습니다: %s", kurrentIP)
            previousIP = kurrentIP
            break
        }

        fmt.Print(".")

        count++
        if count > 5 {
            fmt.Println("VPN 연결 실패! 설정을 삭제합니다.")
            return fmt.Errorf("VPN 연결 시간 초과")
        }

        time.Sleep(time.Duration(1 * time.Second))
    }

    return nil
}

func disconnectAndDelete() error {
    // configFile 파일 삭제
    if err := os.Remove(configFile); err != nil {
        return err
    }

    cmd := exec.Command("vpncmd", "/client")
    var input bytes.Buffer
    input.WriteString("\n") // localhost
    // input.WriteString("2\n") // Client mode
    input.WriteString("AccountDisconnect " + connectionName + "\n")
    input.WriteString("AccountDelete " + connectionName + "\n")
    input.WriteString("exit\n")

    cmd.Stdin = &input

    done := make(chan error, 1)
    var output []byte
    go func() {
        var err error
        output, err = cmd.CombinedOutput()
        done <- err
    }()

    select {
    case err := <-done:
        fmt.Printf("VPN 연결 해제 출력:\n%s", string(output))
        if err != nil {
            return fmt.Errorf("VPN 연결 해제 실패: %s - %v", string(output), err)
        }
        return nil
    case <-time.After(30 * time.Second):
        return fmt.Errorf("VPN 연결 해제 시간 초과")
    }
}

func init() {
    flag.IntVar(&rank, "rank", 1, "VPN server rank to connect (default: 1)")
    flag.BoolVar(&disconnect, "disconnect", false, "Disconnect and delete VPN connection")
    flag.Parse()
}

func main() {
    var err error

    if disconnect {
        if err := disconnectAndDelete(); err != nil {
            fmt.Printf("VPN 연결 해제 실패: %v", err)
            return
        }
        fmt.Println("VPN 연결이 성공적으로 해제되었습니다!")
        return
    }

    count := 0
    for {
        err = connectMain(rank)
        if err == nil {
            break
        }

        count++
        rank++
        fmt.Println("VPN 연결 실패! 설정을 삭제합니다.", count)
        if err := disconnectAndDelete(); err != nil {
            fmt.Printf("VPN 설정 삭제 실패: %v", err)
            return
        }

        if count >= 10 {
            fmt.Println("VPN 연결 실패! 참조하는 서버를 찾을 수 없습니다.")
            return
        }

        time.Sleep(time.Duration(250 * time.Millisecond))
    }
}




vpn이다. vpn


중공 1등 인민 주인님들꼐서 사랑하시는 바둑언어인 golang을 깔고 중공사랑 실천하자.


일제 무른모 vpngate 도 필요해.


cu는 왜 금칙어야 시발거


ㅇㅅㅇ