commit ca1c05bf13a9110c21f7afd3cf619efb56471ad6
Author: Kevin Harwell <kharwell@digium.com>
Date:   Tue Nov 24 15:23:22 2015 -0600

    Test DNS negative cache entries
    
    Checks to make sure that domain names are added, expired, and updated in
    the DNS cache.
    
    ASTERISK-25570 #close
    
    Change-Id: I01d0dd9dfa8d565f1415f56375f2fa7976e4309f

diff --git a/tests/dns_cache/configs/ast1/extconfig.conf b/tests/dns_cache/configs/ast1/extconfig.conf
new file mode 100644
index 0000000..f642fb0
--- /dev/null
+++ b/tests/dns_cache/configs/ast1/extconfig.conf
@@ -0,0 +1,2 @@
+[settings]
+identifies => curl,http://localhost:46821/identify
diff --git a/tests/dns_cache/configs/ast1/extensions.conf b/tests/dns_cache/configs/ast1/extensions.conf
new file mode 100644
index 0000000..8dae664
--- /dev/null
+++ b/tests/dns_cache/configs/ast1/extensions.conf
@@ -0,0 +1,2 @@
+[default]
+
diff --git a/tests/dns_cache/configs/ast1/pjsip.conf b/tests/dns_cache/configs/ast1/pjsip.conf
new file mode 100644
index 0000000..0a18ed7
--- /dev/null
+++ b/tests/dns_cache/configs/ast1/pjsip.conf
@@ -0,0 +1,9 @@
+[global]
+type=global
+debug=yes
+endpoint_identifier_order=ip
+
+[local-transport]
+type=transport
+bind=127.0.0.1
+protocol=udp
diff --git a/tests/dns_cache/configs/ast1/sorcery.conf b/tests/dns_cache/configs/ast1/sorcery.conf
new file mode 100644
index 0000000..246aed2
--- /dev/null
+++ b/tests/dns_cache/configs/ast1/sorcery.conf
@@ -0,0 +1,2 @@
+[res_pjsip_endpoint_identifier_ip]
+identify=realtime,identifies
diff --git a/tests/dns_cache/dns_test.py b/tests/dns_cache/dns_test.py
new file mode 100644
index 0000000..f9d8fcb
--- /dev/null
+++ b/tests/dns_cache/dns_test.py
@@ -0,0 +1,66 @@
+#!/usr/bin/env python
+'''
+Copyright (C) 2015, Digium, Inc.
+Kevin Harwell <kharwell@digium.com>
+
+This program is free software, distributed under the terms of
+the GNU General Public License Version 2.
+'''
+
+import logging
+import sys
+import time
+
+sys.path.append("lib/python/asterisk")
+
+from twisted.internet import reactor
+from sipp import SIPpScenario
+from test_case import TestCaseModule
+
+LOGGER = logging.getLogger(__name__)
+
+
+class DNSCacheTestModule(TestCaseModule):
+    """Test case for dns cache"""
+
+    def __init__(self, test_path='', test_config=None):
+        """Constructor."""
+
+	test_config['connect-ami'] = True
+        super(DNSCacheTestModule, self).__init__(test_path, test_config)
+
+    def ami_connect(self, ami):
+        """Wait for the presence state to change then issue a subscribe."""
+
+        def __originate_call(num=[0]):
+            """Originate a call using sipp"""
+            num[0] += 1
+            sipp = SIPpScenario(self.test_name, {
+                'scenario':'invite.xml', '-p':'506' + str(num[0])})
+            return sipp.run(self)
+
+        def __check_output(cli):
+            """Verify the CLI show cache command works"""
+            LOGGER.info("\n%s\n", cli.output)
+            self.set_passed('invalid.dns' in cli.output)
+            self.stop_reactor()
+
+        def __on_test_event(ami, event, num=[0]):
+            if event['state'] == 'DNS_CACHE_HIT':
+                num[0] += 1
+                if num[0] == 2:
+                    # on first "hit" - wait for entry to expire (should be 60
+                    # secs) and then try again (should cause a "miss")
+                    reactor.callLater(65, __originate_call)
+                elif num[0] == 4 and event['numattempts'] == '2':
+                    self.ast[0].cli_exec(
+                        'dns cache show').addCallback(__check_output)
+            if event.get('state') == 'DNS_CACHE_UPDATE':
+                # the domain name should be in the cache now (or updated),
+                # so try again and this time we should get a "hit"
+                __originate_call()
+
+        ami.registerEvent('TestEvent', __on_test_event)
+
+        # the first call should not resolve, thus get added to the cache
+        __originate_call()
diff --git a/tests/dns_cache/sipp/invite.xml b/tests/dns_cache/sipp/invite.xml
new file mode 100644
index 0000000..77e2448
--- /dev/null
+++ b/tests/dns_cache/sipp/invite.xml
@@ -0,0 +1,71 @@
+<?xml version="1.0" encoding="ISO-8859-1" ?>
+<!DOCTYPE scenario SYSTEM "sipp.dtd">
+
+<scenario name="Invite with variables">
+	<send retrans="500">
+		<![CDATA[
+                        INVITE sip:alice@[remote_ip]:[remote_port] SIP/2.0
+                        Via: SIP/2.0/[transport] [local_ip]:[local_port];branch=[branch]
+                        From: "alice" <sip:alice@[local_ip]:[local_port]>;tag=[call_number]
+                        To: "alice" <sip:alice@[remote_ip]:[remote_port]>
+                        Call-ID: [call_id]
+                        CSeq: [cseq] INVITE
+                        Contact: sip:alice@[local_ip]:[local_port]
+                        Max-Forwards: 70
+                        Content-Type: application/sdp
+                        Content-Length: [len]
+
+			v=0
+			o=- 1324901698 1324901698 IN IP4 [local_ip]
+			s=-
+			c=IN IP4 [local_ip]
+			t=0 0
+			m=audio 2226 RTP/AVP 0 101
+			a=sendrecv
+			a=rtpmap:0 PCMU/8000
+			a=rtpmap:101 telephone-event/8000
+
+		]]>
+	</send>
+
+	<recv response="100" optional="true" />
+
+	<recv response="180" optional="true" />
+
+	<recv response="401" />
+
+	<send>
+		<![CDATA[
+			ACK sip:alice@[remote_ip]:[remote_port] SIP/2.0
+			Via: SIP/2.0/[transport] [local_ip]:[local_port];branch=[branch]
+			From: <sip:alice@[local_ip]>;tag=[call_number]
+			To: <sip:alice@[remote_ip]:[remote_port]>[peer_tag_param]
+			CSeq: [cseq] ACK
+			Call-ID: [call_id]
+			Contact: <sip:alice@[local_ip]>
+                        Allow: INVITE, ACK, MESSAGE, BYE
+			Max-Forwards: 70
+			Content-Length: 0
+
+		]]>
+	</send>
+
+	<recv request="BYE" crlf="true" />
+
+	<send retrans="500">
+		<![CDATA[
+			SIP/2.0 200 OK
+			[last_Via:]
+			[last_From:]
+			[last_To:];tag=[call_number]
+			[last_Call-ID:]
+			[last_CSeq:]
+			Contact: <sip:alice@[local_ip]:[local_port];transport=[transport]>
+                        Allow: INVITE, ACK, MESSAGE, BYE
+			Content-Type: application/sdp
+			Content-Length: 0
+
+		]]>
+	</send>
+
+</scenario>
diff --git a/tests/dns_cache/test-config.yaml b/tests/dns_cache/test-config.yaml
new file mode 100644
index 0000000..e52a96d
--- /dev/null
+++ b/tests/dns_cache/test-config.yaml
@@ -0,0 +1,57 @@
+testinfo:
+    summary: 'Check DNS negative cache entries'
+    description: |
+        "Checks to make sure that items are added, expired, and updated in
+        the DNS cache.
+
+        This test starts by sending an invite to Asterisk, which then attempts
+        to resolve an invalid host name on an endpoint IP identifier. When
+        resolution fails the attempted domain name is added to the cache.
+        Another invite is then sent to Asterisk, which again tries to resolve
+        the invalid host, but this time the dns cache immediately rejects the
+        host since it exists in the cache and has not expired.
+
+        The test then waits for the current entry to expire in the cache. It
+        once again sends an invite. The expected cache "miss" is processed, the
+        host name tries to resolve, fails, and then the host name (since it
+        still resides in the cache, but just expired) is updated for the entry
+        with an extended expiration. The test then verifies again that the host
+        exists in the cache in an unexpired state.
+
+        Finally the test completes after running a cli command to show the dns
+        cache entries and verifying that the invalid host is present."
+
+properties:
+    minversion: '13.7.0'
+    dependencies:
+        - sipp :
+            version : 'v3.0'
+        - python: 'twisted'
+        - python: 'starpy'
+        - asterisk: 'res_pjsip'
+        - asterisk: 'res_config_curl'
+        - asterisk: 'res_sorcery_realtime'
+    tags:
+        - pjsip
+        - realtime
+
+test-modules:
+    add-test-to-search-path: 'True'
+    test-object:
+        config-section: 'test-object-config'
+        typename: 'dns_test.DNSCacheTestModule'
+    modules:
+        -
+            config-section: 'realtime-config'
+            typename: 'realtime_test_module.RealtimeTestModule'
+
+test-object-config:
+    reactor-timeout: 120
+
+realtime-config:
+    data:
+        identify:
+            -
+                id: 'alice'
+                endpoint: 'alice'
+                match: 'invalid.dns'
diff --git a/tests/tests.yaml b/tests/tests.yaml
index 3455387..922a924 100644
--- a/tests/tests.yaml
+++ b/tests/tests.yaml
@@ -36,3 +36,5 @@ tests:
     - dir: 'http_server'
     - dir: 'sorcery'
     - test: 'remote-test'
+    - test: 'dns_cache'
+
