334 lines
12 KiB
Go
334 lines
12 KiB
Go
package network
|
||
|
||
import (
|
||
"bufio"
|
||
"encoding/json"
|
||
"fmt"
|
||
"net"
|
||
"net/http"
|
||
"os"
|
||
"os/exec"
|
||
"regexp"
|
||
"strconv"
|
||
"strings"
|
||
)
|
||
|
||
// NetworkInterface представляет информацию о сетевом интерфейсе
|
||
type NetworkInterface struct {
|
||
Name string `json:"name"`
|
||
IP string `json:"ip"`
|
||
Netmask string `json:"netmask"`
|
||
Gateway string `json:"gateway"`
|
||
}
|
||
|
||
// WiFiNetwork представляет информацию о WiFi сети
|
||
type WiFiNetwork struct {
|
||
SSID string `json:"ssid"`
|
||
Signal int `json:"signal"` // Пример, можно добавить больше информации
|
||
Security string `json:"security"` // Пример, можно добавить больше информации
|
||
}
|
||
|
||
// ErrorResponse для JSON ответов с ошибками
|
||
type ErrorResponse struct {
|
||
Error string `json:"error"`
|
||
}
|
||
|
||
// ScanWiFiNetworks сканирует доступные WiFi сети
|
||
func ScanWiFiNetworks() ([]WiFiNetwork, error) {
|
||
networks, err := ScanWiFiNetworksImpl() // Реализация зависит от ОС
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
return networks, nil
|
||
}
|
||
|
||
// ConnectToWiFi подключается к WiFi сети
|
||
func ConnectToWiFi(ssid, password string) error {
|
||
return ConnectToWiFiImpl(ssid, password) // Реализация зависит от ОС
|
||
}
|
||
|
||
// GetNetworkStatus получает статус сетевых интерфейсов (wlan0, eth0)
|
||
func GetNetworkStatus() ([]NetworkInterface, error) {
|
||
interfaces, err := getNetworkStatusImpl() // Реализация зависит от ОС
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
return interfaces, nil
|
||
}
|
||
|
||
// ConfigureNetworkInterface настраивает статический IP для интерфейса
|
||
func ConfigureNetworkInterface(iface, ip, netmask, gateway string) error {
|
||
return configureNetworkInterfaceImpl(iface, ip, netmask, gateway) // Реализация зависит от ОС
|
||
}
|
||
|
||
// Функция для выполнения команд shell
|
||
func executeCommand(command string, args ...string) (string, error) {
|
||
cmd := exec.Command(command, args...)
|
||
output, err := cmd.CombinedOutput()
|
||
if err != nil {
|
||
return "", fmt.Errorf("ошибка выполнения команды: %s %s, вывод: %s, ошибка: %v", command, strings.Join(args, " "), string(output), err)
|
||
}
|
||
return string(output), nil
|
||
}
|
||
|
||
// Функция для отправки JSON ответа
|
||
func SendJSONResponse(w http.ResponseWriter, data interface{}, statusCode int) {
|
||
w.Header().Set("Content-Type", "application/json")
|
||
w.WriteHeader(statusCode)
|
||
json.NewEncoder(w).Encode(data)
|
||
}
|
||
|
||
// Функция для отправки JSON ошибки
|
||
func SendJSONError(w http.ResponseWriter, message string, statusCode int) {
|
||
SendJSONResponse(w, ErrorResponse{Error: message}, statusCode)
|
||
}
|
||
|
||
// GetConnectedWiFiSSID получает SSID текущей подключенной WiFi сети
|
||
func GetConnectedWiFiSSID() (string, error) {
|
||
ssid, err := getConnectedWiFiSSIDImpl() // Реализация зависит от ОС
|
||
if err != nil {
|
||
return "", err
|
||
}
|
||
return ssid, nil
|
||
}
|
||
|
||
// DisconnectWiFi отключается от текущей WiFi сети
|
||
func DisconnectWiFi() error {
|
||
return disconnectWiFiImpl() // Реализация зависит от ОС
|
||
}
|
||
|
||
// ScanWiFiNetworksImpl реализация для Linux
|
||
func ScanWiFiNetworksImpl() ([]WiFiNetwork, error) {
|
||
output, err := executeCommand("sudo", "iwlist", "wlan0", "scan")
|
||
if err != nil {
|
||
return nil, fmt.Errorf("ошибка сканирования WiFi: %v", err)
|
||
}
|
||
|
||
networks := []WiFiNetwork{}
|
||
scanner := bufio.NewScanner(strings.NewReader(output))
|
||
currentNetwork := WiFiNetwork{}
|
||
reSSID := regexp.MustCompile(`ESSID:"([^"]*)"`)
|
||
reSignal := regexp.MustCompile(`level=(-?\d+)`)
|
||
reEncryption := regexp.MustCompile(`IE: .*?(WPA|WPA2|WEP).*`)
|
||
|
||
for scanner.Scan() {
|
||
line := scanner.Text()
|
||
if strings.Contains(line, "ESSID:") {
|
||
matches := reSSID.FindStringSubmatch(line)
|
||
if len(matches) > 1 {
|
||
currentNetwork.SSID = matches[1]
|
||
}
|
||
}
|
||
if strings.Contains(line, "Signal level=") {
|
||
matches := reSignal.FindStringSubmatch(line)
|
||
if len(matches) > 1 {
|
||
signalLevel, _ := strconv.Atoi(matches[1])
|
||
currentNetwork.Signal = signalLevel
|
||
}
|
||
}
|
||
if strings.Contains(line, "IE:") && reEncryption.MatchString(line) {
|
||
matches := reEncryption.FindStringSubmatch(line)
|
||
if len(matches) > 1 {
|
||
currentNetwork.Security = matches[1]
|
||
} else {
|
||
currentNetwork.Security = "Open" // Если не найдено, считаем открытой
|
||
}
|
||
}
|
||
if currentNetwork.SSID != "" {
|
||
networks = append(networks, currentNetwork)
|
||
currentNetwork = WiFiNetwork{} // Сброс для следующей сети
|
||
}
|
||
}
|
||
|
||
return networks, nil
|
||
}
|
||
|
||
// ConnectToWiFiImpl реализация для Linux
|
||
func ConnectToWiFiImpl(ssid, password string) error {
|
||
// Создаем файл конфигурации wpa_supplicant.conf
|
||
conf := fmt.Sprintf(`ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev
|
||
update_config=1
|
||
country=RU
|
||
|
||
network={
|
||
ssid="%s"
|
||
psk="%s"
|
||
}
|
||
`, ssid, password)
|
||
|
||
err := os.WriteFile("/etc/wpa_supplicant/wpa_supplicant.conf", []byte(conf), 0600)
|
||
if err != nil {
|
||
return fmt.Errorf("ошибка записи в wpa_supplicant.conf: %v", err)
|
||
}
|
||
|
||
// Перезапускаем wpa_supplicant
|
||
_, err = executeCommand("sudo", "wpa_cli", "-i", "wlan0", "reconfigure")
|
||
if err != nil {
|
||
// Попробуем перезапустить службу wpa_supplicant, если wpa_cli не сработал
|
||
_, errRestartService := executeCommand("sudo", "systemctl", "restart", "wpa_supplicant")
|
||
if errRestartService != nil {
|
||
return fmt.Errorf("ошибка перезапуска wpa_supplicant: %v, также ошибка wpa_cli: %v", errRestartService, err)
|
||
}
|
||
}
|
||
|
||
// Переподключаем интерфейс wlan0
|
||
_, err = executeCommand("sudo", "ifdown", "wlan0")
|
||
if err != nil {
|
||
fmt.Printf("Предупреждение: ошибка ifdown wlan0: %v\n", err) // Не критическая ошибка
|
||
}
|
||
_, err = executeCommand("sudo", "ifup", "wlan0")
|
||
if err != nil {
|
||
return fmt.Errorf("ошибка ifup wlan0: %v", err)
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
// getNetworkStatusImpl реализация для Linux
|
||
func getNetworkStatusImpl() ([]NetworkInterface, error) {
|
||
interfaces := []NetworkInterface{}
|
||
|
||
// Получаем статус для eth0
|
||
eth0Status, err := getInterfaceStatus("eth0")
|
||
if err == nil {
|
||
interfaces = append(interfaces, eth0Status)
|
||
}
|
||
|
||
// Получаем статус для wlan0
|
||
wlan0Status, err := getInterfaceStatus("wlan0")
|
||
if err == nil {
|
||
interfaces = append(interfaces, wlan0Status)
|
||
}
|
||
|
||
return interfaces, nil
|
||
}
|
||
|
||
func getInterfaceStatus(ifaceName string) (NetworkInterface, error) {
|
||
ifconfigOutput, err := executeCommand("ifconfig", ifaceName)
|
||
if err != nil {
|
||
return NetworkInterface{}, fmt.Errorf("ошибка ifconfig %s: %v", ifaceName, err)
|
||
}
|
||
|
||
ip, netmask, err := parseIfconfigOutput(ifconfigOutput)
|
||
if err != nil && !strings.Contains(err.Error(), "interface is down") { // Игнорируем ошибку, если интерфейс выключен
|
||
fmt.Printf("Предупреждение: ошибка парсинга ifconfig %s: %v\n", ifaceName, err)
|
||
return NetworkInterface{Name: ifaceName}, nil // Возвращаем интерфейс без IP и маски, но без критической ошибки
|
||
}
|
||
|
||
gateway, err := getDefaultGateway(ifaceName)
|
||
if err != nil {
|
||
fmt.Printf("Предупреждение: ошибка получения шлюза для %s: %v\n", ifaceName, err)
|
||
gateway = "" // Шлюз может быть не задан
|
||
}
|
||
|
||
return NetworkInterface{
|
||
Name: ifaceName,
|
||
IP: ip,
|
||
Netmask: netmask,
|
||
Gateway: gateway,
|
||
}, nil
|
||
}
|
||
|
||
func parseIfconfigOutput(output string) (ip, netmask string, err error) {
|
||
reIP := regexp.MustCompile(`inet (\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})`)
|
||
reNetmask := regexp.MustCompile(`netmask (\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})`)
|
||
|
||
ipMatches := reIP.FindStringSubmatch(output)
|
||
if len(ipMatches) > 1 {
|
||
ip = ipMatches[1]
|
||
} else {
|
||
return "", "", fmt.Errorf("IP адрес не найден или интерфейс выключен") // Изменено сообщение об ошибке
|
||
}
|
||
|
||
netmaskMatches := reNetmask.FindStringSubmatch(output)
|
||
if len(netmaskMatches) > 1 {
|
||
netmask = netmaskMatches[1]
|
||
} else {
|
||
netmask = "" // Маска может быть не найдена, не критично
|
||
}
|
||
|
||
return ip, netmask, nil
|
||
}
|
||
|
||
func getDefaultGateway(ifaceName string) (string, error) {
|
||
routeOutput, err := executeCommand("route", "-n")
|
||
if err != nil {
|
||
return "", fmt.Errorf("ошибка route -n: %v", err)
|
||
}
|
||
|
||
scanner := bufio.NewScanner(strings.NewReader(routeOutput))
|
||
for scanner.Scan() {
|
||
line := scanner.Text()
|
||
fields := strings.Fields(line)
|
||
if len(fields) >= 4 && fields[0] == "0.0.0.0" && fields[5] == ifaceName {
|
||
return fields[1], nil // Шлюз находится во втором поле
|
||
}
|
||
}
|
||
return "", fmt.Errorf("шлюз по умолчанию для интерфейса %s не найден", ifaceName)
|
||
}
|
||
|
||
// configureNetworkInterfaceImpl реализация для Linux
|
||
func configureNetworkInterfaceImpl(iface, ip, netmask, gateway string) error {
|
||
// Проверяем валидность IP и маски
|
||
if net.ParseIP(ip) == nil {
|
||
return fmt.Errorf("неверный формат IP адреса: %s", ip)
|
||
}
|
||
if net.ParseIP(netmask) == nil {
|
||
return fmt.Errorf("неверный формат маски подсети: %s", netmask)
|
||
}
|
||
|
||
// Настраиваем IP и маску
|
||
_, err := executeCommand("sudo", "ifconfig", iface, ip, "netmask", netmask)
|
||
if err != nil {
|
||
return fmt.Errorf("ошибка настройки IP и маски: %v", err)
|
||
}
|
||
|
||
// Настраиваем шлюз, только если он указан
|
||
if gateway != "" {
|
||
if net.ParseIP(gateway) == nil {
|
||
return fmt.Errorf("неверный формат шлюза: %s", gateway)
|
||
}
|
||
_, err = executeCommand("sudo", "route", "del", "default", "gw", "0.0.0.0", "dev", iface) // Удаляем старый шлюз, если есть
|
||
if err != nil && !strings.Contains(err.Error(), "No such process") && !strings.Contains(err.Error(), "not in table") { // Игнорируем ошибки, если шлюз не был установлен
|
||
fmt.Printf("Предупреждение: ошибка удаления старого шлюза: %v\n", err)
|
||
}
|
||
_, err = executeCommand("sudo", "route", "add", "default", "gw", gateway, "dev", iface)
|
||
if err != nil {
|
||
return fmt.Errorf("ошибка настройки шлюза: %v", err)
|
||
}
|
||
} else {
|
||
// Если шлюз не указан, удаляем шлюз по умолчанию для этого интерфейса
|
||
_, err = executeCommand("sudo", "route", "del", "default", "gw", "0.0.0.0", "dev", iface)
|
||
if err != nil && !strings.Contains(err.Error(), "No such process") && !strings.Contains(err.Error(), "not in table") {
|
||
fmt.Printf("Предупреждение: ошибка удаления шлюза по умолчанию: %v\n", err)
|
||
}
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
// getConnectedWiFiSSIDImpl реализация для Linux
|
||
func getConnectedWiFiSSIDImpl() (string, error) {
|
||
output, err := executeCommand("iwconfig", "wlan0")
|
||
if err != nil {
|
||
return "", fmt.Errorf("ошибка выполнения iwconfig wlan0: %v", err)
|
||
}
|
||
|
||
reSSID := regexp.MustCompile(`ESSID:"([^"]*)"`)
|
||
matches := reSSID.FindStringSubmatch(output)
|
||
if len(matches) > 1 {
|
||
return matches[1], nil
|
||
}
|
||
return "", fmt.Errorf("не удалось получить SSID из вывода iwconfig, возможно, не подключено к WiFi")
|
||
}
|
||
|
||
// disconnectWiFiImpl реализация для Linux
|
||
func disconnectWiFiImpl() error {
|
||
// Отключаем интерфейс wlan0
|
||
_, err := executeCommand("sudo", "ifdown", "wlan0")
|
||
if err != nil {
|
||
return fmt.Errorf("ошибка ifdown wlan0: %v", err)
|
||
}
|
||
return nil
|
||
}
|