Added DNS
This commit is contained in:
@@ -82,7 +82,7 @@ class DHCPServer:
|
||||
self.interface: Optional[str] = None
|
||||
self.ip_start: Optional[str] = None
|
||||
self.subnet_mask: Optional[str] = None
|
||||
self.server_ip: Optional[str] = None # inferred from interface IP
|
||||
self.server_ip: Optional[str] = None # we use start_ip as server identifier in this lab build
|
||||
|
||||
self._leases: Dict[str, Tuple[str, float]] = {} # MAC -> (IP, expiry_ts)
|
||||
self._active_clients: List[str] = [] # list of MACs
|
||||
@@ -92,8 +92,11 @@ class DHCPServer:
|
||||
self._log = deque(maxlen=500)
|
||||
self._status = "idle"
|
||||
|
||||
# NEW: configurable DNS servers for DHCP Option 6
|
||||
self.dns_servers: List[str] = []
|
||||
|
||||
# -------------------- Public API --------------------
|
||||
def start(self, interface: str, start_ip: str, mask: str) -> None:
|
||||
def start(self, interface: str, start_ip: str, mask: str, dns_servers: Optional[List[str]] = None) -> None:
|
||||
if self.is_running():
|
||||
raise RuntimeError("Server is already running.")
|
||||
self.interface = interface
|
||||
@@ -101,9 +104,21 @@ class DHCPServer:
|
||||
self.subnet_mask = mask
|
||||
|
||||
# build pool based on interface network
|
||||
self.server_ip = start_ip # We use the provided IP as "server identifier"; typically you'd use iface IP
|
||||
self.server_ip = start_ip # in this lab version we use the provided IP as "server identifier"
|
||||
self._pool = self._build_pool(start_ip, mask)
|
||||
|
||||
# configure DNS (filter invalid entries); default to [server_ip, 8.8.8.8]
|
||||
self.dns_servers = []
|
||||
for d in (dns_servers or []):
|
||||
try:
|
||||
socket.inet_aton(d)
|
||||
self.dns_servers.append(d)
|
||||
except Exception:
|
||||
# ignore invalid entries silently
|
||||
continue
|
||||
if not self.dns_servers and self.server_ip:
|
||||
self.dns_servers = [self.server_ip, "8.8.8.8"]
|
||||
|
||||
self._stop_evt.clear()
|
||||
self._thread = threading.Thread(target=self._run_loop, name="DHCPWorker", daemon=True)
|
||||
self._thread.start()
|
||||
@@ -227,18 +242,16 @@ class DHCPServer:
|
||||
# Build pool from the network containing start_ip with provided mask
|
||||
net = ipaddress.IPv4Network((start_ip, mask), strict=False)
|
||||
all_hosts = list(net.hosts())
|
||||
# Begin at start_ip position (or nearest host) and go upwards, excluding start_ip itself for server identity
|
||||
# Begin at/after start_ip; exclude start_ip (we use it as server/gateway)
|
||||
try:
|
||||
start_idx = all_hosts.index(ipaddress.IPv4Address(start_ip))
|
||||
except ValueError:
|
||||
# pick the first host >= start_ip, else 0
|
||||
start_idx = 0
|
||||
for i, h in enumerate(all_hosts):
|
||||
if int(h) >= int(ipaddress.IPv4Address(start_ip)):
|
||||
start_idx = i
|
||||
break
|
||||
pool = [str(h) for h in all_hosts[start_idx:]]
|
||||
# Ensure server_ip isn't handed out
|
||||
if start_ip in pool:
|
||||
pool.remove(start_ip)
|
||||
return pool
|
||||
@@ -346,16 +359,21 @@ class DHCPServer:
|
||||
op, htype, hlen, hops, xid, secs, flags,
|
||||
ciaddr, yiaddr_b, siaddr, giaddr_b, chaddr, sname, filef)
|
||||
|
||||
# Options: message type, server id, subnet mask, router (server), lease time
|
||||
options = [
|
||||
# Options: message type, server id, subnet mask, router (server), DNS list, lease time
|
||||
options: List[Tuple[int, bytes]] = [
|
||||
(53, bytes([msg_type])),
|
||||
(54, siaddr), # server identifier
|
||||
]
|
||||
if self.subnet_mask:
|
||||
options.append((1, ip2bytes(self.subnet_mask))) # subnet mask
|
||||
if self.server_ip:
|
||||
options.append((3, ip2bytes(self.server_ip))) # router
|
||||
options.append((6, ip2bytes(self.server_ip))) # DNS (for lab convenience)
|
||||
options.append((3, ip2bytes(self.server_ip))) # router (gateway)
|
||||
|
||||
# DHCP Option 6: one option with N*4 bytes for N DNS servers
|
||||
if self.dns_servers:
|
||||
dns_bytes = b"".join(ip2bytes(ip) for ip in self.dns_servers)
|
||||
options.append((6, dns_bytes))
|
||||
|
||||
options.append((51, struct.pack("!I", LEASE_SECONDS))) # lease time
|
||||
|
||||
return hdr + build_options(options)
|
||||
|
39
src/gui.py
39
src/gui.py
@@ -2,7 +2,7 @@ import os
|
||||
import socket
|
||||
import tkinter as tk
|
||||
from tkinter import messagebox, ttk
|
||||
from typing import Optional
|
||||
from typing import Optional, List
|
||||
|
||||
from dhcp_server import DHCPServer
|
||||
import utils
|
||||
@@ -55,7 +55,7 @@ class DHCPApp(tk.Frame):
|
||||
def __init__(self, master: tk.Tk) -> None:
|
||||
super().__init__(master)
|
||||
master.title("Simple DHCP Server (Lab)")
|
||||
master.minsize(680, 460)
|
||||
master.minsize(700, 500)
|
||||
self.pack(fill="both", expand=True, padx=12, pady=12)
|
||||
|
||||
# Single server instance
|
||||
@@ -65,10 +65,12 @@ class DHCPApp(tk.Frame):
|
||||
form = ttk.Frame(self)
|
||||
form.pack(fill="x", pady=(0, 8))
|
||||
|
||||
# Zeile 0
|
||||
ttk.Label(form, text="Interface:").grid(row=0, column=0, sticky="w")
|
||||
self.if_var = tk.StringVar()
|
||||
self.if_menu = ttk.OptionMenu(form, self.if_var, None, *utils.get_network_interfaces(), command=self.on_iface_change)
|
||||
self.if_menu.grid(row=0, column=1, sticky="ew", padx=(6, 16))
|
||||
form.columnconfigure(1, weight=1)
|
||||
|
||||
ttk.Label(form, text="Server/Start-IP:").grid(row=0, column=2, sticky="w")
|
||||
self.ip_var = tk.StringVar(value="")
|
||||
@@ -80,11 +82,20 @@ class DHCPApp(tk.Frame):
|
||||
self.mask_entry = ttk.Entry(form, textvariable=self.mask_var, width=16)
|
||||
self.mask_entry.grid(row=0, column=5, sticky="w", padx=(6, 16))
|
||||
|
||||
form.columnconfigure(1, weight=1)
|
||||
# Zeile 1 – DNS Felder
|
||||
ttk.Label(form, text="Primary DNS:").grid(row=1, column=0, sticky="w", pady=(6, 0))
|
||||
self.dns1_var = tk.StringVar(value="")
|
||||
self.dns1_entry = ttk.Entry(form, textvariable=self.dns1_var, width=16)
|
||||
self.dns1_entry.grid(row=1, column=1, sticky="w", padx=(6, 16), pady=(6, 0))
|
||||
|
||||
ttk.Label(form, text="Secondary DNS:").grid(row=1, column=2, sticky="w", pady=(6, 0))
|
||||
self.dns2_var = tk.StringVar(value="")
|
||||
self.dns2_entry = ttk.Entry(form, textvariable=self.dns2_var, width=16)
|
||||
self.dns2_entry.grid(row=1, column=3, sticky="w", padx=(6, 16), pady=(6, 0))
|
||||
|
||||
# --- Buttons ---
|
||||
btns = ttk.Frame(self)
|
||||
btns.pack(fill="x", pady=(0, 8))
|
||||
btns.pack(fill="x", pady=(8, 8))
|
||||
self.btn_start = ttk.Button(btns, text="Start", command=self.start_server)
|
||||
self.btn_stop = ttk.Button(btns, text="Stop", command=self.stop_server, state="disabled")
|
||||
self.btn_start.pack(side="left")
|
||||
@@ -126,14 +137,19 @@ class DHCPApp(tk.Frame):
|
||||
ip, mask = utils.get_iface_ipv4_config(iface)
|
||||
if ip:
|
||||
self.ip_var.set(ip)
|
||||
# sinnvolles Default: Primary DNS = Interface-IP
|
||||
self.dns1_var.set(ip)
|
||||
if mask:
|
||||
self.mask_var.set(mask)
|
||||
# Secondary DNS bleibt leer, damit du frei wählen kannst
|
||||
|
||||
def _set_controls_enabled(self, enabled: bool):
|
||||
state = "normal" if enabled else "disabled"
|
||||
self.if_menu.configure(state=state)
|
||||
self.ip_entry.configure(state=state)
|
||||
self.mask_entry.configure(state=state)
|
||||
self.dns1_entry.configure(state=state)
|
||||
self.dns2_entry.configure(state=state)
|
||||
self.btn_start.configure(state="normal" if enabled else "disabled")
|
||||
self.btn_stop.configure(state="disabled" if enabled else "normal")
|
||||
|
||||
@@ -141,19 +157,29 @@ class DHCPApp(tk.Frame):
|
||||
if self.server and self.server.is_running():
|
||||
messagebox.showinfo("Info", "Server läuft bereits.")
|
||||
return
|
||||
# Big fat warning
|
||||
# Fette Warnung
|
||||
if not messagebox.askyesno("Warnung", WARNING_TEXT, icon="warning"):
|
||||
return
|
||||
|
||||
iface = self.if_var.get().strip()
|
||||
ip = self.ip_var.get().strip()
|
||||
mask = self.mask_var.get().strip()
|
||||
dns1 = self.dns1_var.get().strip()
|
||||
dns2 = self.dns2_var.get().strip()
|
||||
|
||||
if not iface:
|
||||
messagebox.showerror("Fehler", "Bitte ein Netzwerk-Interface wählen.")
|
||||
return
|
||||
|
||||
# Eingaben prüfen/normalisieren
|
||||
try:
|
||||
ip = utils.format_ip_address(ip)
|
||||
utils.validate_subnet_mask(mask)
|
||||
dns_list: List[str] = []
|
||||
if dns1:
|
||||
dns_list.append(utils.format_ip_address(dns1))
|
||||
if dns2:
|
||||
dns_list.append(utils.format_ip_address(dns2))
|
||||
except Exception as e:
|
||||
messagebox.showerror("Ungültige Eingabe", str(e))
|
||||
return
|
||||
@@ -173,7 +199,8 @@ class DHCPApp(tk.Frame):
|
||||
# --- Start ---
|
||||
try:
|
||||
self.server = DHCPServer()
|
||||
self.server.start(iface, ip, mask)
|
||||
# NEU: DNS-Liste an den Server übergeben
|
||||
self.server.start(iface, ip, mask, dns_servers=dns_list)
|
||||
self.status_var.set("Server gestartet.")
|
||||
self._set_controls_enabled(False)
|
||||
except Exception as e:
|
||||
|
Reference in New Issue
Block a user