334 lines
12 KiB
Go
Raw Normal View History

2025-03-11 14:07:01 +03:00
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
}