This is an automated email from the ASF dual-hosted git repository.

DaanHoogland pushed a commit to branch dnsProviderUrlValidate
in repository https://gitbox.apache.org/repos/asf/cloudstack.git

commit a3fab5a8ec9ebfba7f9d916958406f4eaca53930
Author: Daan Hoogland <[email protected]>
AuthorDate: Fri Aug 7 10:47:37 2026 +0200

    validate DNS server URLs in provider framework
---
 .../cloudstack/dns/DnsProviderManagerImpl.java     | 21 ++++++++++++++
 .../cloudstack/dns/DnsProviderManagerImplTest.java | 32 ++++++++++++++++++----
 2 files changed, 47 insertions(+), 6 deletions(-)

diff --git 
a/server/src/main/java/org/apache/cloudstack/dns/DnsProviderManagerImpl.java 
b/server/src/main/java/org/apache/cloudstack/dns/DnsProviderManagerImpl.java
index b451da1baf7..3718967ba5a 100644
--- a/server/src/main/java/org/apache/cloudstack/dns/DnsProviderManagerImpl.java
+++ b/server/src/main/java/org/apache/cloudstack/dns/DnsProviderManagerImpl.java
@@ -96,6 +96,7 @@ import com.cloud.user.User;
 import com.cloud.user.dao.AccountDao;
 import com.cloud.utils.Pair;
 import com.cloud.utils.StringUtils;
+import com.cloud.utils.UriUtils;
 import com.cloud.utils.component.ManagerBase;
 import com.cloud.utils.component.PluggableService;
 import com.cloud.utils.db.Filter;
@@ -162,9 +163,28 @@ public class DnsProviderManagerImpl extends ManagerBase 
implements DnsProviderMa
         throw new CloudRuntimeException("No plugin found for DNS provider 
type: " + type);
     }
 
+    /**
+     * Rejects DNS provider URLs that resolve to an illegal address (per 
{@link UriUtils#validateUrl(String)},
+     * currently any-local/link-local/loopback/multicast; RFC1918 site-local 
coverage follows once #271/#277
+     * lands) before any provider client is given the chance to connect to it. 
A scheme is assumed to be
+     * `http` when the caller omits one, matching how DNS provider clients 
(e.g. PowerDnsClient) already
+     * tolerate bare host/IP values.
+     */
+    private void validateDnsServerUrl(String url) {
+        if (StringUtils.isBlank(url)) {
+            return;
+        }
+        String urlToValidate = url.trim();
+        if (!urlToValidate.startsWith("http://";) && 
!urlToValidate.startsWith("https://";)) {
+            urlToValidate = "http://"; + urlToValidate;
+        }
+        UriUtils.validateUrl(urlToValidate);
+    }
+
     @Override
     @ActionEvent(eventType = EventTypes.EVENT_DNS_SERVER_ADD, eventDescription 
= "Adding a DNS Server")
     public DnsServer addDnsServer(AddDnsServerCmd cmd) {
+        validateDnsServerUrl(cmd.getUrl());
         Account caller = CallContext.current().getCallingAccount();
         DnsServer existing = dnsServerDao.findByUrlAndAccount(cmd.getUrl(), 
caller.getId());
         if (existing != null) {
@@ -252,6 +272,7 @@ public class DnsProviderManagerImpl extends ManagerBase 
implements DnsProviderMa
 
         if (cmd.getUrl() != null) {
             if (!cmd.getUrl().equals(originalUrl)) {
+                validateDnsServerUrl(cmd.getUrl());
                 DnsServer duplicate = 
dnsServerDao.findByUrlAndAccount(cmd.getUrl(), dnsServer.getAccountId());
                 if (duplicate != null && duplicate.getId() != 
dnsServer.getId()) {
                     throw new InvalidParameterValueException("Another DNS 
server with this URL already exists.");
diff --git 
a/server/src/test/java/org/apache/cloudstack/dns/DnsProviderManagerImplTest.java
 
b/server/src/test/java/org/apache/cloudstack/dns/DnsProviderManagerImplTest.java
index 309f5e5d9cf..ec239239abd 100644
--- 
a/server/src/test/java/org/apache/cloudstack/dns/DnsProviderManagerImplTest.java
+++ 
b/server/src/test/java/org/apache/cloudstack/dns/DnsProviderManagerImplTest.java
@@ -718,7 +718,7 @@ public class DnsProviderManagerImplTest {
         org.apache.cloudstack.api.command.user.dns.AddDnsServerCmd cmd = mock(
                 
org.apache.cloudstack.api.command.user.dns.AddDnsServerCmd.class);
         when(accountMgr.isRootAdmin(callerMock.getId())).thenReturn(true);
-        when(cmd.getUrl()).thenReturn("http://newpdns:8081";);
+        when(cmd.getUrl()).thenReturn("http://93.184.216.34:8081";);
         when(cmd.getProvider()).thenReturn(DnsProviderType.PowerDNS);
         when(dnsServerDao.findByUrlAndAccount(anyString(), 
anyLong())).thenReturn(null);
         
when(dnsProviderMock.validateAndResolveServer(any())).thenReturn("resolved-id");
@@ -781,18 +781,26 @@ public class DnsProviderManagerImplTest {
     public void testAddDnsServerAlreadyExists() {
         org.apache.cloudstack.api.command.user.dns.AddDnsServerCmd cmd = mock(
                 
org.apache.cloudstack.api.command.user.dns.AddDnsServerCmd.class);
-        when(cmd.getUrl()).thenReturn("http://newpdns:8081";);
+        when(cmd.getUrl()).thenReturn("http://93.184.216.34:8081";);
         when(dnsServerDao.findByUrlAndAccount(anyString(), 
anyLong())).thenReturn(serverVO);
         manager.addDnsServer(cmd);
     }
 
+    @Test(expected = IllegalArgumentException.class)
+    public void testAddDnsServerRejectsLoopbackUrl() {
+        org.apache.cloudstack.api.command.user.dns.AddDnsServerCmd cmd = mock(
+                
org.apache.cloudstack.api.command.user.dns.AddDnsServerCmd.class);
+        when(cmd.getUrl()).thenReturn("http://127.0.0.1:8081";);
+        manager.addDnsServer(cmd);
+    }
+
     @Test
     public void testAddDnsServerNormalUser() throws Exception {
         org.apache.cloudstack.api.command.user.dns.AddDnsServerCmd cmd = mock(
                 
org.apache.cloudstack.api.command.user.dns.AddDnsServerCmd.class);
         when(accountMgr.isRootAdmin(callerMock.getId())).thenReturn(false);
         when(accountMgr.isDomainAdmin(callerMock.getId())).thenReturn(false);
-        when(cmd.getUrl()).thenReturn("http://newpdns:8081";);
+        when(cmd.getUrl()).thenReturn("http://93.184.216.34:8081";);
         when(cmd.getProvider()).thenReturn(DnsProviderType.PowerDNS);
         when(cmd.getNameServers()).thenReturn(Collections.emptyList());
         when(cmd.isPublic()).thenReturn(true);
@@ -811,7 +819,7 @@ public class DnsProviderManagerImplTest {
         org.apache.cloudstack.api.command.user.dns.AddDnsServerCmd cmd = mock(
                 
org.apache.cloudstack.api.command.user.dns.AddDnsServerCmd.class);
         when(accountMgr.isRootAdmin(callerMock.getId())).thenReturn(true);
-        when(cmd.getUrl()).thenReturn("http://newpdns:8081";);
+        when(cmd.getUrl()).thenReturn("http://93.184.216.34:8081";);
         when(cmd.getProvider()).thenReturn(DnsProviderType.PowerDNS);
         when(cmd.getNameServers()).thenReturn(Collections.emptyList());
         when(dnsServerDao.findByUrlAndAccount(anyString(), 
anyLong())).thenReturn(null);
@@ -824,7 +832,7 @@ public class DnsProviderManagerImplTest {
         org.apache.cloudstack.api.command.user.dns.UpdateDnsServerCmd cmd = 
mock(
                 
org.apache.cloudstack.api.command.user.dns.UpdateDnsServerCmd.class);
         when(cmd.getId()).thenReturn(SERVER_ID);
-        when(cmd.getUrl()).thenReturn("http://duplicate:8081";);
+        when(cmd.getUrl()).thenReturn("http://93.184.216.34:8081";);
         DnsServerVO existingServer = mock(DnsServerVO.class);
         when(existingServer.getId()).thenReturn(SERVER_ID + 1); // Different 
ID implies duplicate
 
@@ -835,12 +843,24 @@ public class DnsProviderManagerImplTest {
         manager.updateDnsServer(cmd);
     }
 
+    @Test(expected = IllegalArgumentException.class)
+    public void testUpdateDnsServerRejectsLoopbackUrl() {
+        org.apache.cloudstack.api.command.user.dns.UpdateDnsServerCmd cmd = 
mock(
+                
org.apache.cloudstack.api.command.user.dns.UpdateDnsServerCmd.class);
+        when(cmd.getId()).thenReturn(SERVER_ID);
+        when(cmd.getUrl()).thenReturn("http://127.0.0.1:8081";);
+        when(dnsServerDao.findById(SERVER_ID)).thenReturn(serverVO);
+        Mockito.doReturn("http://original:8081";).when(serverVO).getUrl();
+
+        manager.updateDnsServer(cmd);
+    }
+
     @Test
     public void testUpdateDnsServerUrlValid() throws Exception {
         org.apache.cloudstack.api.command.user.dns.UpdateDnsServerCmd cmd = 
mock(
                 
org.apache.cloudstack.api.command.user.dns.UpdateDnsServerCmd.class);
         when(cmd.getId()).thenReturn(SERVER_ID);
-        when(cmd.getUrl()).thenReturn("http://new-url:8081";);
+        when(cmd.getUrl()).thenReturn("http://93.184.216.34:8081";);
         when(dnsServerDao.findById(SERVER_ID)).thenReturn(serverVO);
 
         Mockito.doReturn("http://original:8081";).when(serverVO).getUrl();

Reply via email to