Better DNS error handling
diff --git a/CHANGELOG.md b/CHANGELOG.md
index b5778b4a78..c02c9c07e4 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -15,6 +15,7 @@ This project adheres to [Semantic Versioning](https://semver.org/).
 - Added support for basic JSON types on `--form`/`--multipart` when using JSON only operators (`:=`/`:=@`). ([#1212](https://github.com/httpie/httpie/issues/1212))
 - Added support for automatically enabling `--stream` when `Content-Type` is `text/event-stream`. ([#376](https://github.com/httpie/httpie/issues/376))
 - Added new `pie-dark`/`pie-light` (and `pie`) styles that match with [HTTPie for Web and Desktop](https://httpie.io/product). ([#1237](https://github.com/httpie/httpie/issues/1237))
+- Added support for better error handling on DNS failures. ([#1248](https://github.com/httpie/httpie/issues/1248))
 - Broken plugins will no longer crash the whole application. ([#1204](https://github.com/httpie/httpie/issues/1204))
 - Fixed auto addition of XML declaration to every formatted XML response. ([#1156](https://github.com/httpie/httpie/issues/1156))
 - Fixed highlighting when `Content-Type` specifies `charset`. ([#1242](https://github.com/httpie/httpie/issues/1242))
diff --git a/httpie/core.py b/httpie/core.py
index 48d21bc4f0..bc03686b14 100644
--- a/httpie/core.py
+++ b/httpie/core.py
@@ -2,6 +2,7 @@
 import os
 import platform
 import sys
+import socket
 from typing import List, Optional, Tuple, Union, Callable
 
 import requests
@@ -21,6 +22,7 @@
 from .output.writer import write_message, write_stream, MESSAGE_SEPARATOR_BYTES
 from .plugins.registry import plugin_manager
 from .status import ExitStatus, http_status_to_exit_status
+from .utils import unwrap_context
 
 
 # noinspection PyDefaultArgument
@@ -41,6 +43,21 @@ def raw_main(
     include_debug_info = '--debug' in args
     include_traceback = include_debug_info or '--traceback' in args
 
+    def handle_generic_error(e, annotation=None):
+        msg = str(e)
+        if hasattr(e, 'request'):
+            request = e.request
+            if hasattr(request, 'url'):
+                msg = (
+                    f'{msg} while doing a {request.method}'
+                    f' request to URL: {request.url}'
+                )
+        if annotation:
+            msg += annotation
+        env.log_error(f'{type(e).__name__}: {msg}')
+        if include_traceback:
+            raise
+
     if include_debug_info:
         print_debug_info(env)
         if args == ['--debug']:
@@ -90,19 +107,23 @@ def raw_main(
                 f'Too many redirects'
                 f' (--max-redirects={parsed_args.max_redirects}).'
             )
+        except requests.exceptions.ConnectionError as exc:
+            annotation = None
+            original_exc = unwrap_context(exc)
+            if isinstance(original_exc, socket.gaierror):
+                if original_exc.errno == socket.EAI_AGAIN:
+                    annotation = '\nCouldn\'t connect to a DNS server. Perhaps check your connection and try again.'
+                elif original_exc.errno == socket.EAI_NONAME:
+                    annotation = '\nCouldn\'t resolve the given hostname. Perhaps check it and try again.'
+                propagated_exc = original_exc
+            else:
+                propagated_exc = exc
+
+            handle_generic_error(propagated_exc, annotation=annotation)
+            exit_status = ExitStatus.ERROR
         except Exception as e:
             # TODO: Further distinction between expected and unexpected errors.
-            msg = str(e)
-            if hasattr(e, 'request'):
-                request = e.request
-                if hasattr(request, 'url'):
-                    msg = (
-                        f'{msg} while doing a {request.method}'
-                        f' request to URL: {request.url}'
-                    )
-            env.log_error(f'{type(e).__name__}: {msg}')
-            if include_traceback:
-                raise
+            handle_generic_error(e)
             exit_status = ExitStatus.ERROR
 
     return exit_status
diff --git a/httpie/utils.py b/httpie/utils.py
index 8669de8caf..fa19fa7cde 100644
--- a/httpie/utils.py
+++ b/httpie/utils.py
@@ -229,3 +229,11 @@ def split(iterable: Iterable[T], key: Callable[[T], bool]) -> Tuple[List[T], Lis
         else:
             right.append(item)
     return left, right
+
+
+def unwrap_context(exc: Exception) -> Optional[Exception]:
+    context = exc.__context__
+    if isinstance(context, Exception):
+        return unwrap_context(context)
+    else:
+        return exc
diff --git a/tests/test_errors.py b/tests/test_errors.py
index 5a1a0f2476..fca48fff15 100644
--- a/tests/test_errors.py
+++ b/tests/test_errors.py
@@ -1,3 +1,5 @@
+import pytest
+import socket
 from unittest import mock
 from pytest import raises
 from requests import Request
@@ -31,6 +33,21 @@ def test_error_traceback(program):
         http('--traceback', 'www.google.com')
 
 
+@mock.patch('httpie.core.program')
+@pytest.mark.parametrize("error_code, expected_message", [
+    (socket.EAI_AGAIN, "check your connection"),
+    (socket.EAI_NONAME, "check the URL"),
+])
+def test_error_custom_dns(program, error_code, expected_message):
+    exc = ConnectionError('Connection aborted')
+    exc.__context__ = socket.gaierror(error_code, "<test>")
+    program.side_effect = exc
+
+    r = http('www.google.com', tolerate_error_exit_status=True)
+    assert r.exit_status == ExitStatus.ERROR
+    assert expected_message in r.stderr
+
+
 def test_max_headers_limit(httpbin_both):
     with raises(ConnectionError) as e:
         http('--max-headers=1', httpbin_both + '/get')

E.g
```
$ http non.existent.url
http: error: gaierror: [Errno -2] Name or service not known
Couldn't resolve the given URL. Perhaps check the URL and try again.
```
or 
```
http: error: gaierror: [Errno -3] Temporary failure in name resolution
Couldn't connect to a DNS server. Perhaps check your connection and try again.
```
Fixes #1248