From 4eaceaf9af5964e981da7cc3b0910bbe97eaafc3 Mon Sep 17 00:00:00 2001 From: Hiroki Tokunaga Date: Wed, 25 Feb 2026 16:04:18 +0900 Subject: [PATCH] fix: show error modal when private DNS startup fails Show an error modal and roll back UI state when private DNS start or restart fails, with Japanese i18n text and dialog parenting to the private DNS panel for multi-display environments. Made-with: Cursor --- .../java/core/packetproxy/PrivateDNS.java | 26 +++++++++++++--- .../packetproxy/gui/GUIOptionPrivateDNS.java | 31 +++++++++++++++---- src/main/resources/strings_ja.properties | 1 + 3 files changed, 48 insertions(+), 10 deletions(-) diff --git a/src/main/java/core/packetproxy/PrivateDNS.java b/src/main/java/core/packetproxy/PrivateDNS.java index 13f3bd9f..c849b2fc 100644 --- a/src/main/java/core/packetproxy/PrivateDNS.java +++ b/src/main/java/core/packetproxy/PrivateDNS.java @@ -149,7 +149,7 @@ public boolean isRunning() throws Exception { return state.getState(); } - public void start(DNSSpoofingIPGetter dnsSpoofingIPGetter) { + public boolean start(DNSSpoofingIPGetter dnsSpoofingIPGetter) { synchronized (lock) { if (dns == null) { @@ -162,16 +162,26 @@ public void start(DNSSpoofingIPGetter dnsSpoofingIPGetter) { state.setState(true); } else { + dns = null; + state.setState(false); + return false; } } catch (Exception e) { errWithStackTrace(e); + dns = null; + try { + state.setState(false); + } catch (Exception ignored) { + } + return false; } } } + return true; } - public void restart(DNSSpoofingIPGetter dnsSpoofingIPGetter) { + public boolean restart(DNSSpoofingIPGetter dnsSpoofingIPGetter) { synchronized (lock) { if (dns != null) { @@ -190,12 +200,20 @@ public void restart(DNSSpoofingIPGetter dnsSpoofingIPGetter) { dns = null; state.setState(false); + return false; } } catch (Exception e) { errWithStackTrace(e); + dns = null; + try { + state.setState(false); + } catch (Exception ignored) { + } + return false; } } + return true; } public int getConfiguredPort() { @@ -236,8 +254,8 @@ public void setPort(int port, DNSSpoofingIPGetter dnsSpoofingIPGetter) { } try { - if (isRunning()) { - restart(dnsSpoofingIPGetter); + if (isRunning() && !restart(dnsSpoofingIPGetter)) { + state.setState(false); } } catch (Exception e) { errWithStackTrace(e); diff --git a/src/main/java/core/packetproxy/gui/GUIOptionPrivateDNS.java b/src/main/java/core/packetproxy/gui/GUIOptionPrivateDNS.java index 7d19bb5c..6ffd589e 100644 --- a/src/main/java/core/packetproxy/gui/GUIOptionPrivateDNS.java +++ b/src/main/java/core/packetproxy/gui/GUIOptionPrivateDNS.java @@ -42,6 +42,7 @@ import javax.swing.JComboBox; import javax.swing.JComponent; import javax.swing.JLabel; +import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JRadioButton; import javax.swing.JTextField; @@ -384,10 +385,15 @@ public String getSpoofingIP6() { private JCheckBox createCheckBox() { checkBox = new JCheckBox(I18nString.get("Use private DNS server")); checkBox.addActionListener(e -> { - if (checkBox.isSelected()) - privateDNS.start(new DNSSpoofingIPGetter(this)); - else + if (!checkBox.isSelected()) { privateDNS.stop(); + return; + } + + if (!privateDNS.start(new DNSSpoofingIPGetter(this))) { + checkBox.setSelected(false); + showPrivateDnsStartErrorDialog(); + } }); checkBox.setMinimumSize(new Dimension(Short.MAX_VALUE, checkBox.getMaximumSize().height)); return checkBox; @@ -428,8 +434,13 @@ public void updateState() { checkBox.setSelected(new ConfigBoolean("PrivateDNS").getState()); updateDnsPortFieldText(Integer.toString(privateDNS.getConfiguredPort())); - if (checkBox.isSelected()) - privateDNS.start(new DNSSpoofingIPGetter(this)); + if (!checkBox.isSelected()) { + return; + } + if (!privateDNS.start(new DNSSpoofingIPGetter(this))) { + checkBox.setSelected(false); + showPrivateDnsStartErrorDialog(); + } } catch (Exception e) { errWithStackTrace(e); @@ -493,12 +504,20 @@ private void restartPrivateDnsForBindingInterfaceChange() { if (!privateDNS.isRunning()) { return; } - privateDNS.restart(new DNSSpoofingIPGetter(this)); + if (!privateDNS.restart(new DNSSpoofingIPGetter(this))) { + checkBox.setSelected(false); + showPrivateDnsStartErrorDialog(); + } } catch (Exception e) { errWithStackTrace(e); } } + private void showPrivateDnsStartErrorDialog() { + var message = I18nString.get("Failed to start private DNS server. Please check permissions and listen port."); + JOptionPane.showMessageDialog(base, message, I18nString.get("Error"), JOptionPane.ERROR_MESSAGE); + } + @Override public void propertyChange(PropertyChangeEvent evt) { if (CONFIGS.matches(evt)) { diff --git a/src/main/resources/strings_ja.properties b/src/main/resources/strings_ja.properties index 934c2353..e4d4b995 100644 --- a/src/main/resources/strings_ja.properties +++ b/src/main/resources/strings_ja.properties @@ -142,6 +142,7 @@ Use_*_to_apply_all_ports=全てのポートに適用するために * が使え Use_SSL/TLS\:=SSL/TLSを利用: Use_private_DNS_server=プライベートDNSサーバを起動する Use_private_DNS_server_that_resolves_server_name_to_the_IP_address_of_this_pc.=サーバの名前を自分自身のIPアドレスに名前解決したいときに利用します +Failed_to_start_private_DNS_server._Please_check_permissions_and_listen_port.=プライベートDNSサーバの起動に失敗しました。権限と待ち受けポートを確認してください。 Use_OpenVPN_Server_as_Docker_Container_to_proxy_HTTP/HTTPS_without_DNS_Spoofing.=OpenVPNサーバをDocker Containerとして利用し、DNS Spoofingを用いずに通信を取得する will_be_used_for_VPN=をOpenVPNのプロトコルとして使います Use_OpenVPN=OpenVPNサーバを起動する