2025-03-11 14:07:01 +03:00
|
|
|
|
package wlan
|
|
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
|
"encoding/json"
|
|
|
|
|
|
"fmt"
|
2025-03-12 22:35:36 +03:00
|
|
|
|
"gopkg.in/yaml.v3"
|
|
|
|
|
|
"log"
|
2025-03-11 14:07:01 +03:00
|
|
|
|
"net"
|
|
|
|
|
|
"net/http"
|
2025-03-12 22:35:36 +03:00
|
|
|
|
"network_configurator/models"
|
|
|
|
|
|
"os"
|
2025-03-11 14:07:01 +03:00
|
|
|
|
"os/exec"
|
|
|
|
|
|
"strings"
|
|
|
|
|
|
)
|
|
|
|
|
|
|
2025-03-12 22:35:36 +03:00
|
|
|
|
const netplanFilePath = "/etc/netplan/wlan.yaml"
|
2025-03-11 14:07:01 +03:00
|
|
|
|
|
2025-03-12 22:35:36 +03:00
|
|
|
|
// MacAdresWlan0 HTTP handler для получения MAC адреса интерфейса wlan0
|
|
|
|
|
|
func MacAdresWlan0(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
|
interfaces, err := net.Interfaces()
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
response := models.MacAddressResponse{
|
|
|
|
|
|
Error: fmt.Sprintf("Failed to get network interfaces: %v", err),
|
|
|
|
|
|
}
|
|
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
|
|
|
|
w.WriteHeader(http.StatusInternalServerError)
|
|
|
|
|
|
json.NewEncoder(w).Encode(response)
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
2025-03-11 14:07:01 +03:00
|
|
|
|
|
2025-03-12 22:35:36 +03:00
|
|
|
|
var wlan0Interface *net.Interface
|
|
|
|
|
|
for _, iface := range interfaces {
|
|
|
|
|
|
if iface.Name == "wlan0" {
|
|
|
|
|
|
wlan0Interface = &iface
|
|
|
|
|
|
break
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-03-11 14:07:01 +03:00
|
|
|
|
|
2025-03-12 22:35:36 +03:00
|
|
|
|
if wlan0Interface == nil {
|
|
|
|
|
|
response := models.MacAddressResponse{
|
|
|
|
|
|
Error: "Interface wlan0 not found",
|
|
|
|
|
|
Interface: "wlan0",
|
|
|
|
|
|
}
|
|
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
|
|
|
|
w.WriteHeader(http.StatusNotFound)
|
|
|
|
|
|
json.NewEncoder(w).Encode(response)
|
2025-03-11 14:07:01 +03:00
|
|
|
|
return
|
|
|
|
|
|
}
|
2025-03-12 22:35:36 +03:00
|
|
|
|
|
|
|
|
|
|
macAddress := wlan0Interface.HardwareAddr.String()
|
|
|
|
|
|
|
|
|
|
|
|
response := models.MacAddressResponse{
|
|
|
|
|
|
MacAddress: macAddress,
|
|
|
|
|
|
Interface: "wlan0",
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
|
|
|
|
w.WriteHeader(http.StatusOK)
|
|
|
|
|
|
json.NewEncoder(w).Encode(response)
|
2025-03-11 14:07:01 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-03-12 22:35:36 +03:00
|
|
|
|
// InterfaceIPsWlan0 HTTP handler для получения списка IP адресов интерфейса wlan0
|
|
|
|
|
|
func InterfaceIPsWlan0(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
|
interfaces, err := net.Interfaces()
|
2025-03-11 14:07:01 +03:00
|
|
|
|
if err != nil {
|
2025-03-12 22:35:36 +03:00
|
|
|
|
response := models.InterfaceIPsResponse{
|
|
|
|
|
|
Error: fmt.Sprintf("Failed to get network interfaces: %v", err),
|
|
|
|
|
|
}
|
|
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
|
|
|
|
w.WriteHeader(http.StatusInternalServerError)
|
|
|
|
|
|
json.NewEncoder(w).Encode(response)
|
2025-03-11 14:07:01 +03:00
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-03-12 22:35:36 +03:00
|
|
|
|
var wlan0Interface *net.Interface
|
|
|
|
|
|
for _, iface := range interfaces {
|
|
|
|
|
|
if iface.Name == "wlan0" {
|
|
|
|
|
|
wlan0Interface = &iface
|
|
|
|
|
|
break
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if wlan0Interface == nil {
|
|
|
|
|
|
response := models.InterfaceIPsResponse{
|
|
|
|
|
|
Interface: "wlan0",
|
|
|
|
|
|
Error: "Interface wlan0 not found",
|
|
|
|
|
|
}
|
|
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
|
|
|
|
w.WriteHeader(http.StatusNotFound)
|
|
|
|
|
|
json.NewEncoder(w).Encode(response)
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
2025-03-11 14:07:01 +03:00
|
|
|
|
|
2025-03-12 22:35:36 +03:00
|
|
|
|
addrs, err := wlan0Interface.Addrs()
|
2025-03-11 14:07:01 +03:00
|
|
|
|
if err != nil {
|
2025-03-12 22:35:36 +03:00
|
|
|
|
response := models.InterfaceIPsResponse{
|
|
|
|
|
|
Interface: "wlan0",
|
|
|
|
|
|
Error: fmt.Sprintf("Failed to get addresses for wlan0: %v", err),
|
|
|
|
|
|
}
|
|
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
|
|
|
|
w.WriteHeader(http.StatusInternalServerError)
|
|
|
|
|
|
json.NewEncoder(w).Encode(response)
|
2025-03-11 14:07:01 +03:00
|
|
|
|
return
|
|
|
|
|
|
}
|
2025-03-12 22:35:36 +03:00
|
|
|
|
|
|
|
|
|
|
ipAddresses := make([]string, 0)
|
|
|
|
|
|
for _, addr := range addrs {
|
|
|
|
|
|
if ipnet, ok := addr.(*net.IPNet); ok && !ipnet.IP.IsLoopback() {
|
|
|
|
|
|
if ipnet.IP.To4() != nil || ipnet.IP.To16() != nil {
|
|
|
|
|
|
ipAddresses = append(ipAddresses, ipnet.IP.String())
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
response := models.InterfaceIPsResponse{
|
|
|
|
|
|
Interface: "wlan0",
|
|
|
|
|
|
IPAddresses: ipAddresses,
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
|
|
|
|
w.WriteHeader(http.StatusOK)
|
|
|
|
|
|
json.NewEncoder(w).Encode(response)
|
2025-03-11 14:07:01 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-03-12 22:35:36 +03:00
|
|
|
|
// SSIDWlan0 HTTP handler для получения SSID интерфейса wlan0
|
|
|
|
|
|
func SSIDWlan0(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
|
// Command to get SSID (Linux - iwgetid, macOS - airport, Windows - needs different approach)
|
|
|
|
|
|
cmd := "iwgetid" // For Linux - you might need to install 'wireless-tools' package
|
|
|
|
|
|
out, err := exec.Command(cmd, "wlan0", "-r").Output()
|
2025-03-11 14:07:01 +03:00
|
|
|
|
if err != nil {
|
2025-03-12 22:35:36 +03:00
|
|
|
|
response := models.SSIDResponse{
|
|
|
|
|
|
Interface: "wlan0",
|
|
|
|
|
|
Error: fmt.Sprintf("Failed to get SSID using %s: %v", cmd, err),
|
|
|
|
|
|
}
|
|
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
|
|
|
|
w.WriteHeader(http.StatusInternalServerError)
|
|
|
|
|
|
json.NewEncoder(w).Encode(response)
|
2025-03-11 14:07:01 +03:00
|
|
|
|
return
|
|
|
|
|
|
}
|
2025-03-12 22:35:36 +03:00
|
|
|
|
|
|
|
|
|
|
ssid := strings.TrimSpace(string(out))
|
|
|
|
|
|
|
|
|
|
|
|
if ssid == "" {
|
|
|
|
|
|
response := models.SSIDResponse{
|
|
|
|
|
|
Interface: "wlan0",
|
|
|
|
|
|
Error: "SSID is empty or not connected",
|
|
|
|
|
|
}
|
|
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
|
|
|
|
w.WriteHeader(http.StatusNotFound)
|
|
|
|
|
|
json.NewEncoder(w).Encode(response)
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
response := models.SSIDResponse{
|
|
|
|
|
|
Interface: "wlan0",
|
|
|
|
|
|
SSID: ssid,
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
|
|
|
|
w.WriteHeader(http.StatusOK)
|
|
|
|
|
|
json.NewEncoder(w).Encode(response)
|
2025-03-11 14:07:01 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-03-12 22:35:36 +03:00
|
|
|
|
func createTestNetplanFile() {
|
|
|
|
|
|
initialYaml := `
|
|
|
|
|
|
network:
|
|
|
|
|
|
version: 2
|
|
|
|
|
|
renderer: networkd
|
|
|
|
|
|
wifis:
|
|
|
|
|
|
wlan0:
|
|
|
|
|
|
dhcp4: yes
|
|
|
|
|
|
`
|
|
|
|
|
|
err := os.WriteFile(netplanFilePath, []byte(initialYaml), 0644)
|
2025-03-11 14:07:01 +03:00
|
|
|
|
if err != nil {
|
2025-03-12 22:35:36 +03:00
|
|
|
|
log.Fatalf("Failed to create test netplan file: %v", err)
|
2025-03-11 14:07:01 +03:00
|
|
|
|
}
|
2025-03-12 22:35:36 +03:00
|
|
|
|
fmt.Println("Test netplan file created at:", netplanFilePath)
|
2025-03-11 14:07:01 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-03-12 22:35:36 +03:00
|
|
|
|
func init() {
|
|
|
|
|
|
if _, err := os.Stat(netplanFilePath); os.IsNotExist(err) {
|
|
|
|
|
|
createTestNetplanFile()
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func NetplanHandler(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
|
switch r.Method {
|
|
|
|
|
|
case http.MethodGet:
|
|
|
|
|
|
getNetplanConfig(w, r)
|
|
|
|
|
|
case http.MethodPut:
|
|
|
|
|
|
updateNetplanConfig(w, r)
|
|
|
|
|
|
default:
|
|
|
|
|
|
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func getNetplanConfig(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
|
config, err := readNetplanConfig()
|
2025-03-11 14:07:01 +03:00
|
|
|
|
if err != nil {
|
2025-03-12 22:35:36 +03:00
|
|
|
|
http.Error(w, fmt.Sprintf("Failed to read config: %v", err), http.StatusInternalServerError)
|
2025-03-11 14:07:01 +03:00
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
2025-03-12 22:35:36 +03:00
|
|
|
|
json.NewEncoder(w).Encode(config)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func updateNetplanConfig(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
|
var updatedConfig models.NetplanConfigWlan
|
|
|
|
|
|
err := json.NewDecoder(r.Body).Decode(&updatedConfig)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
http.Error(w, fmt.Sprintf("Failed to decode request body: %v", err), http.StatusBadRequest)
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
err = writeNetplanConfig(updatedConfig)
|
2025-03-11 14:07:01 +03:00
|
|
|
|
if err != nil {
|
2025-03-12 22:35:36 +03:00
|
|
|
|
http.Error(w, fmt.Sprintf("Failed to write config: %v", err), http.StatusInternalServerError)
|
2025-03-11 14:07:01 +03:00
|
|
|
|
return
|
|
|
|
|
|
}
|
2025-03-12 22:35:36 +03:00
|
|
|
|
|
|
|
|
|
|
w.WriteHeader(http.StatusOK)
|
|
|
|
|
|
w.Write([]byte("Netplan configuration updated successfully"))
|
2025-03-11 14:07:01 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-03-12 22:35:36 +03:00
|
|
|
|
func readNetplanConfig() (*models.NetplanConfigWlan, error) {
|
|
|
|
|
|
yamlFile, err := os.ReadFile(netplanFilePath)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return nil, fmt.Errorf("failed to read netplan file: %w", err)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var config models.NetplanConfigWlan
|
|
|
|
|
|
err = yaml.Unmarshal(yamlFile, &config)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return nil, fmt.Errorf("failed to unmarshal yaml: %w", err)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return &config, nil
|
2025-03-11 14:07:01 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-03-12 22:35:36 +03:00
|
|
|
|
func writeNetplanConfig(config models.NetplanConfigWlan) error {
|
|
|
|
|
|
savedConfig, err := readNetplanConfig()
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return fmt.Errorf("failed to read eth yaml: %w", err)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
yamlData, err := yaml.Marshal(config)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return fmt.Errorf("failed to marshal yaml: %w", err)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
err = os.WriteFile(netplanFilePath, yamlData, 0644)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return fmt.Errorf("failed to write netplan file: %w", err)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
cmd := exec.Command("netplan", "apply")
|
|
|
|
|
|
output, err := cmd.CombinedOutput()
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
|
|
|
|
|
|
yamlData, err := yaml.Marshal(*savedConfig)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return fmt.Errorf("failed to marshal yaml: %w", err)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
err = os.WriteFile(netplanFilePath, yamlData, 0644)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return fmt.Errorf("failed to write netplan file: %w", err)
|
|
|
|
|
|
}
|
2025-03-11 14:07:01 +03:00
|
|
|
|
|
2025-03-12 22:35:36 +03:00
|
|
|
|
return fmt.Errorf("Ошибка при выполнении команды: %s\n Вывод команды: %s\n", err, output)
|
2025-03-11 14:07:01 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-03-12 22:35:36 +03:00
|
|
|
|
return nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// WifiNetworks HTTP handler для получения списка видимых WiFi сетей с уровнем сигнала и типом безопасности
|
|
|
|
|
|
func WifiNetworks(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
|
interfaceName := "wlan0" // Жестко задан интерфейс wlan0 для сканирования
|
|
|
|
|
|
cmd := "iwlist" // Using 'iwlist' command to scan WiFi networks (Linux specific)
|
|
|
|
|
|
out, err := exec.Command(cmd, interfaceName, "scan").Output()
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
response := models.WifiNetworksResponse{
|
|
|
|
|
|
Error: fmt.Sprintf("Failed to scan WiFi networks using %s: %v", cmd, err),
|
|
|
|
|
|
}
|
|
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
|
|
|
|
w.WriteHeader(http.StatusInternalServerError)
|
|
|
|
|
|
json.NewEncoder(w).Encode(response)
|
|
|
|
|
|
return
|
2025-03-11 14:07:01 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-03-12 22:35:36 +03:00
|
|
|
|
output := string(out)
|
|
|
|
|
|
lines := strings.Split(output, "\n")
|
|
|
|
|
|
networks := make([]models.NetworkInfo, 0)
|
|
|
|
|
|
seenSSIDs := make(map[string]bool)
|
|
|
|
|
|
|
|
|
|
|
|
var currentNetworkInfo models.NetworkInfo
|
|
|
|
|
|
var signal string
|
|
|
|
|
|
for _, line := range lines {
|
|
|
|
|
|
line = strings.TrimSpace(line)
|
|
|
|
|
|
|
|
|
|
|
|
if strings.Contains(line, "Signal level=") {
|
|
|
|
|
|
signalLevel := strings.SplitN(line, "=", 2)[1]
|
|
|
|
|
|
signalLevel = strings.Fields(signalLevel)[0]
|
|
|
|
|
|
signal = signalLevel
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if strings.Contains(line, "ESSID:") {
|
|
|
|
|
|
ssid := strings.SplitN(line, ":", 2)[1]
|
|
|
|
|
|
ssid = strings.Trim(ssid, "\"")
|
|
|
|
|
|
if !seenSSIDs[ssid] {
|
|
|
|
|
|
if currentNetworkInfo.SSID != "" {
|
|
|
|
|
|
networks = append(networks, currentNetworkInfo)
|
|
|
|
|
|
}
|
|
|
|
|
|
currentNetworkInfo = models.NetworkInfo{SSID: ssid, Signal: signal}
|
|
|
|
|
|
seenSSIDs[ssid] = true
|
|
|
|
|
|
}
|
|
|
|
|
|
} else if strings.Contains(line, "Encryption key:") && strings.Contains(line, "off") {
|
|
|
|
|
|
currentNetworkInfo.Security = "Open" // No encryption, Open network
|
|
|
|
|
|
} else if strings.Contains(line, "IE:") { // Information Element - for security type
|
|
|
|
|
|
if strings.Contains(line, "WPA2") {
|
|
|
|
|
|
currentNetworkInfo.Security = "WPA2"
|
|
|
|
|
|
} else if strings.Contains(line, "WPA") {
|
|
|
|
|
|
currentNetworkInfo.Security = "WPA"
|
|
|
|
|
|
} else if strings.Contains(line, "WEP") {
|
|
|
|
|
|
currentNetworkInfo.Security = "WEP"
|
|
|
|
|
|
} else {
|
|
|
|
|
|
if currentNetworkInfo.Security == "" { // If no specific WPA/WEP found and not marked as Open
|
|
|
|
|
|
currentNetworkInfo.Security = "Secured" // Generic secured if IE present but type not identified
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
if currentNetworkInfo.SSID != "" {
|
|
|
|
|
|
networks = append(networks, currentNetworkInfo)
|
2025-03-11 14:07:01 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-03-12 22:35:36 +03:00
|
|
|
|
// If Security is still empty after all checks, and it's not Open, assume it's some form of Secured
|
|
|
|
|
|
for i := range networks {
|
|
|
|
|
|
if networks[i].Security == "" && networks[i].SSID != "" {
|
|
|
|
|
|
networks[i].Security = "Secured" // Default to Secured if type not determined by IE and not explicitly Open
|
|
|
|
|
|
}
|
2025-03-11 14:07:01 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-03-12 22:35:36 +03:00
|
|
|
|
response := models.WifiNetworksResponse{
|
|
|
|
|
|
Networks: networks,
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
|
|
|
|
w.WriteHeader(http.StatusOK)
|
|
|
|
|
|
json.NewEncoder(w).Encode(response)
|
2025-03-11 14:07:01 +03:00
|
|
|
|
}
|