Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

psutil.net_connections() failure #623

Closed
crusaderky opened this issue Apr 28, 2015 · 11 comments
Closed

psutil.net_connections() failure #623

crusaderky opened this issue Apr 28, 2015 · 11 comments

Comments

@crusaderky
Copy link
Contributor

I'm running RedHat 5 x86-64, Python 2.6.2 and psutil-2.2.1 built from sources.

In [1]: import psutil

In [2]: psutil.net_connections()
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-2-0aa291b09876> in <module>()
----> 1 psutil.net_connections()

/algodata/software/thirdparty/lib/python2.6/site-packages/psutil-2.2.1-py2.6-linux-x86_64.egg/psutil/__init__.pyc in net_connections(kind)
   1814     all             the sum of all the possible families and protocols
   1815     """
-> 1816     return _psplatform.net_connections(kind)
   1817
   1818

/algodata/software/thirdparty/lib/python2.6/site-packages/psutil-2.2.1-py2.6-linux-x86_64.egg/psutil/_pslinux.pyc in net_connections(kind)
    528 def net_connections(kind='inet'):
    529     """Return system-wide open connections."""
--> 530     return _connections.retrieve(kind)
    531
    532

/algodata/software/thirdparty/lib/python2.6/site-packages/psutil-2.2.1-py2.6-linux-x86_64.egg/psutil/_pslinux.pyc in retrieve(self, kind, pid)
    512                 ls = self.process_unix(
    513                     "/proc/net/%s" % f, family, inodes, filter_pid=pid)
--> 514             for fd, family, type_, laddr, raddr, status, bound_pid in ls:
    515                 if pid:
    516                     conn = _common.pconn(fd, family, type_, laddr, raddr,

/algodata/software/thirdparty/lib/python2.6/site-packages/psutil-2.2.1-py2.6-linux-x86_64.egg/psutil/_pslinux.pyc in process_inet(self, file, family, type_, inodes, filter_pid)
    463                     else:
    464                         status = _common.CONN_NONE
--> 465                     laddr = self.decode_address(laddr, family)
    466                     raddr = self.decode_address(raddr, family)
    467                     yield (fd, family, type_, laddr, raddr, status, pid)

/algodata/software/thirdparty/lib/python2.6/site-packages/psutil-2.2.1-py2.6-linux-x86_64.egg/psutil/_pslinux.pyc in decode_address(self, addr, family)
    429                 ip = socket.inet_ntop(
    430                     socket.AF_INET6,
--> 431                     struct.pack('>4I', *struct.unpack('<4I', ip)))
    432             else:
    433                 ip = socket.inet_ntop(

ValueError: unknown address family 10
@giampaolo
Copy link
Owner

It looks like your system does not support IPv6. What happens if you run this?

import socket

def supports_ipv6():
    """Return True if IPv6 is supported on this platform."""
    if not socket.has_ipv6 or not hasattr(socket, "AF_INET6"):
        return False
    sock = None
    try:
        sock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
        sock.bind(("::1", 0))
    except (socket.error, socket.gaierror):
        return False
    else:
        return True
    finally:
        if sock is not None:
            sock.close()

print supports_ipv6()

@giampaolo
Copy link
Owner

If that returns None I'm going to apply the following patch, otherwise I'll have to think whether supports_ipv6 should tweak with inet_ntop.

diff --git a/HISTORY.rst b/HISTORY.rst
index 7eda302..58397c6 100644
--- a/HISTORY.rst
+++ b/HISTORY.rst
@@ -8,6 +8,8 @@ Bug tracker at https://github.com/giampaolo/psutil/issues
 - #340: [Windows] Process.open_files() no longer hangs. Instead it uses a
   thred which times out and skips the file handle in case it's taking too long
   to be retrieved.  (patch by Jeff Tang, PR #597)
+- #623: [Linux] process or system connections raises ValueError if IPv6 is not
+  supported by the system.
 - #627: [Windows] Process.name() no longer raises AccessDenied for pids owned
   by another user.
 - #636: [Windows] Process.memory_info() raise AccessDenied.
diff --git a/psutil/_common.py b/psutil/_common.py
index e9acf59..b58d278 100644
--- a/psutil/_common.py
+++ b/psutil/_common.py
@@ -7,6 +7,7 @@
 """Common objects shared by all _ps* modules."""

 from __future__ import division
+import contextlib
 import errno
 import functools
 import os
@@ -164,6 +165,19 @@ def socktype_to_enum(num):
         return num


+def supports_ipv6():
+    """Return True if IPv6 is supported on this platform."""
+    if not socket.has_ipv6 or not hasattr(socket, "AF_INET6"):
+        return False
+    try:
+        sock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
+        with contextlib.closing(sock):
+            sock.bind(("::1", 0))
+        return True
+    except socket.error:
+        return False
+
+
 # --- Process.connections() 'kind' parameter mapping

 conn_tmap = {
diff --git a/psutil/_pslinux.py b/psutil/_pslinux.py
index be443ef..ced2916 100644
--- a/psutil/_pslinux.py
+++ b/psutil/_pslinux.py
@@ -23,7 +23,7 @@ from . import _common
 from . import _psposix
 from . import _psutil_linux as cext
 from . import _psutil_posix as cext_posix
-from ._common import isfile_strict, usage_percent
+from ._common import isfile_strict, usage_percent, supports_ipv6
 from ._common import NIC_DUPLEX_FULL, NIC_DUPLEX_HALF, NIC_DUPLEX_UNKNOWN
 from ._compat import PY3, long

@@ -346,6 +346,10 @@ def pid_exists(pid):

 # --- network

+class _Ipv6UnsupportedError(Exception):
+    pass
+
+
 class Connections:
     """A wrapper on top of /proc/net/* files, retrieving per-process
     and system-wide open connections (TCP, UDP, UNIX) similarly to
@@ -456,14 +460,20 @@ class Connections:
             #          ''.join(ip[i:i+4][::-1] for i in xrange(0, 16, 4)))
             ip = base64.b16decode(ip)
             # see: https://github.com/giampaolo/psutil/issues/201
-            if sys.byteorder == 'little':
-                ip = socket.inet_ntop(
-                    socket.AF_INET6,
-                    struct.pack('>4I', *struct.unpack('<4I', ip)))
-            else:
-                ip = socket.inet_ntop(
-                    socket.AF_INET6,
-                    struct.pack('<4I', *struct.unpack('<4I', ip)))
+            try:
+                if sys.byteorder == 'little':
+                    ip = socket.inet_ntop(
+                        socket.AF_INET6,
+                        struct.pack('>4I', *struct.unpack('<4I', ip)))
+                else:
+                    ip = socket.inet_ntop(
+                        socket.AF_INET6,
+                        struct.pack('<4I', *struct.unpack('<4I', ip)))
+            except ValueError:
+                if not supports_ipv6():
+                    raise _Ipv6UnsupportedError
+                else:
+                    raise
         return (ip, port)

     def process_inet(self, file, family, type_, inodes, filter_pid=None):
@@ -498,8 +508,11 @@ class Connections:
                         status = TCP_STATUSES[status]
                     else:
                         status = _common.CONN_NONE
-                    laddr = self.decode_address(laddr, family)
-                    raddr = self.decode_address(raddr, family)
+                    try:
+                        laddr = self.decode_address(laddr, family)
+                        raddr = self.decode_address(raddr, family)
+                    except _Ipv6UnsupportedError:
+                        continue
                     yield (fd, family, type_, laddr, raddr, status, pid)

     def process_unix(self, file, family, inodes, filter_pid=None):
diff --git a/test/_linux.py b/test/_linux.py
index 493c149..6c4d14b 100644
--- a/test/_linux.py
+++ b/test/_linux.py
@@ -302,6 +302,18 @@ class LinuxSpecificTestCase(unittest.TestCase):
                 self.assertEqual(p.open_files(), [])
                 assert m.called

+    @mock.patch('psutil._pslinux.socket.inet_ntop', side_effect=ValueError)
+    @mock.patch('psutil._pslinux.supports_ipv6', return_value=False)
+    def test_connections_ipv6_not_supported(self, supports_ipv6, inet_ntop):
+        # see: https://github.com/giampaolo/psutil/issues/623
+        try:
+            s = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
+            self.addCleanup(s.close)
+            s.bind(("::1", 0))
+        except socket.error:
+            pass
+        psutil.net_connections(kind='inet6')
+
     def test_proc_terminal_mocked(self):
         with mock.patch('psutil._pslinux._psposix._get_terminal_map',
                         return_value={}) as m:
diff --git a/test/test_psutil.py b/test/test_psutil.py
index 3d9ae28..7e0cd15 100644
--- a/test/test_psutil.py
+++ b/test/test_psutil.py
@@ -52,6 +52,7 @@ except ImportError:
     import mock  # requires "pip install mock"

 import psutil
+from psutil._common import supports_ipv6
 from psutil._compat import PY3, callable, long, unicode

 if sys.version_info < (2, 7):
@@ -480,23 +481,6 @@ def skip_on_not_implemented(only_if=None):
     return decorator


-def supports_ipv6():
-    """Return True if IPv6 is supported on this platform."""
-    if not socket.has_ipv6 or not hasattr(socket, "AF_INET6"):
-        return False
-    sock = None
-    try:
-        sock = socket.socket(AF_INET6, SOCK_STREAM)
-        sock.bind(("::1", 0))
-    except (socket.error, socket.gaierror):
-        return False
-    else:
-        return True
-    finally:
-        if sock is not None:
-            sock.close()
-
-
 if WINDOWS:
     def get_winver():
         wv = sys.getwindowsversion()
@@ -2753,6 +2737,29 @@ class TestMisc(unittest.TestCase):
         # docstring
         self.assertEqual(foo.__doc__, "foo docstring")

+    def test_supports_ipv6(self):
+        if supports_ipv6():
+            with mock.patch('psutil._common.socket') as s:
+                s.has_ipv6 = False
+                assert not supports_ipv6()
+            with mock.patch('psutil._common.socket.socket',
+                            side_effect=socket.error) as s:
+                assert not supports_ipv6()
+                assert s.called
+            with mock.patch('psutil._common.socket.socket',
+                            side_effect=socket.gaierror) as s:
+                assert not supports_ipv6()
+                assert s.called
+            with mock.patch('psutil._common.socket.socket.bind',
+                            side_effect=socket.gaierror) as s:
+                assert not supports_ipv6()
+                assert s.called
+        else:
+            if hasattr(socket, 'AF_INET6'):
+                with self.assertRaises(Exception):
+                    sock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
+                    sock.bind(("::1", 0))
+
     def test_serialization(self):
         def check(ret):
             if json is not None:

@giampaolo
Copy link
Owner

@crusaderky what happens if you run this?

import socket

def supports_ipv6():
    """Return True if IPv6 is supported on this platform."""
    if not socket.has_ipv6 or not hasattr(socket, "AF_INET6"):
        return False
    sock = None
    try:
        sock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
        sock.bind(("::1", 0))
    except (socket.error, socket.gaierror):
        return False
    else:
        return True
    finally:
        if sock is not None:
            sock.close()

print supports_ipv6()

@crusaderky
Copy link
Contributor Author

Hi,

Sorry for the delay.

On my office environment (an ancient red hat 5) it returns false

On 30 August 2015 at 18:50, giampaolo notifications@github.com wrote:

@crusaderky https://github.com/crusaderky what happens if you run this?

import socket

def supports_ipv6():
"""Return True if IPv6 is supported on this platform."""
if not socket.has_ipv6 or not hasattr(socket, "AF_INET6"):
return False
sock = None
try:
sock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
sock.bind(("::1", 0))
except (socket.error, socket.gaierror):
return False
else:
return True
finally:
if sock is not None:
sock.close()

print supports_ipv6()


Reply to this email directly or view it on GitHub
#623 (comment).

@giampaolo
Copy link
Owner

@crusaderky can you please tell me what happens if you run this?

import socket

def supports_ipv6():
    """Return True if IPv6 is supported on this platform."""
    if not socket.has_ipv6 or not hasattr(socket, "AF_INET6"):
        return False
    sock = None
    try:
        sock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
        sock.bind(("::1", 0))
    except (socket.error, socket.gaierror):
        return False
    else:
        return True
    finally:
        if sock is not None:
            sock.close()

print supports_ipv6()

@crusaderky
Copy link
Contributor Author

Giampaolo, your second script looks identical to the first one...
On 14 Sep 2015 11:04, "giampaolo" notifications@github.com wrote:

@crusaderky https://github.com/crusaderky can you please tell me what
happens if you run this?

import socket

def supports_ipv6():
"""Return True if IPv6 is supported on this platform."""
if not socket.has_ipv6 or not hasattr(socket, "AF_INET6"):
return False
sock = None
try:
sock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
sock.bind(("::1", 0))
except (socket.error, socket.gaierror):
return False
else:
return True
finally:
if sock is not None:
sock.close()

print supports_ipv6()


Reply to this email directly or view it on GitHub
#623 (comment).

@giampaolo
Copy link
Owner

That's because I never received a response from you. For the third time: can you run that script and tell me what it returns?

@crusaderky
Copy link
Contributor Author

Resending the answer from before
On 4 Sep 2015 10:15, "CRV§ADER//KY" crusaderky@gmail.com wrote:

On my office environment (an ancient red hat 5) it returns false
On 30 Aug 2015 18:50, "giampaolo" notifications@github.com wrote:

@crusaderky https://github.com/crusaderky what happens if you run this?

import socket

def supports_ipv6():
"""Return True if IPv6 is supported on this platform."""
if not socket.has_ipv6 or not hasattr(socket, "AF_INET6"):
return False
sock = None
try:
sock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
sock.bind(("::1", 0))
except (socket.error, socket.gaierror):
return False
else:
return True
finally:
if sock is not None:
sock.close()

print supports_ipv6()


Reply to this email directly or view it on GitHub
#623 (comment).

@giampaolo
Copy link
Owner

I can't see that response in any previous comment. Weird.

@mrjefftang
Copy link
Collaborator

It's hidden in the folded quotation block '...'

On 4 Sep 2015 10:15, "CRV§ADER//KY" crusaderky@gmail.com wrote: > On my office environment (an ancient red hat 5) it returns false

@giampaolo
Copy link
Owner

Sorry, I missed the quoted part. I should have fixed this in 30901a6. @crusaderky can you confirm it works?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants