diff options
-rw-r--r-- | net/test/csocket.py | 32 | ||||
-rwxr-xr-x | net/test/csocket_test.py | 46 | ||||
-rwxr-xr-x | net/test/leak_test.py | 51 |
3 files changed, 128 insertions, 1 deletions
diff --git a/net/test/csocket.py b/net/test/csocket.py index 5dc495c..ad7fd9d 100644 --- a/net/test/csocket.py +++ b/net/test/csocket.py @@ -32,6 +32,7 @@ MsgHdr = cstruct.Struct("msghdr", "@LLLLLLi", SockaddrIn = cstruct.Struct("sockaddr_in", "=HH4sxxxxxxxx", "family port addr") SockaddrIn6 = cstruct.Struct("sockaddr_in6", "=HHI16sI", "family port flowinfo addr scope_id") +SockaddrStorage = cstruct.Struct("sockaddr_storage", "=H126s", "family data") # Constants. CMSG_ALIGNTO = struct.calcsize("@L") # The kernel defines this as sizeof(long). @@ -108,7 +109,7 @@ def _MakeMsgControl(optlist): def Bind(s, to): - """Python wrapper for connect.""" + """Python wrapper for bind.""" ret = libc.bind(s.fileno(), to.CPointer(), len(to)) MaybeRaiseSocketError(ret) return ret @@ -180,3 +181,32 @@ def Sendmsg(s, to, data, control, flags): MaybeRaiseSocketError(ret) return ret + + +def Recvfrom(s, size, flags=0): + """Python wrapper for recvfrom.""" + buf = ctypes.create_string_buffer(size) + addr = ctypes.create_string_buffer(len(SockaddrStorage)) + alen = ctypes.c_int(len(addr)) + + ret = libc.recvfrom(s.fileno(), buf, len(buf), flags, + addr, ctypes.byref(alen)) + MaybeRaiseSocketError(ret) + + data = buf[:ret] + alen = alen.value + addr = addr.raw[:alen] + + # Attempt to convert the address to something we understand. + if alen == 0: + addr = None + elif alen == len(SockaddrIn) and SockaddrIn(addr).family == socket.AF_INET: + addr = SockaddrIn(addr) + elif alen == len(SockaddrIn6) and SockaddrIn6(addr).family == socket.AF_INET6: + addr = SockaddrIn6(addr) + elif alen == len(SockaddrStorage): # Can this ever happen? + addr = SockaddrStorage(addr) + else: + pass # Unknown or malformed. Return the raw bytes. + + return data, addr diff --git a/net/test/csocket_test.py b/net/test/csocket_test.py new file mode 100755 index 0000000..7c47598 --- /dev/null +++ b/net/test/csocket_test.py @@ -0,0 +1,46 @@ +#!/usr/bin/python +# +# Copyright 2016 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Unit tests for csocket.""" + +import socket +import unittest + +import csocket + + +class CsocketTest(unittest.TestCase): + + def CheckRecvfrom(self, family, addr): + s = socket.socket(family, socket.SOCK_DGRAM, 0) + s.bind((addr, 0)) + + addr = s.getsockname() + sockaddr = csocket.Sockaddr(addr) + s.sendto("foo", addr) + data, addr = csocket.Recvfrom(s, 4096, 0) + self.assertEqual("foo", data) + self.assertEqual(sockaddr, addr) + + s.close() + + def testRecvfrom(self): + self.CheckRecvfrom(socket.AF_INET, "127.0.0.1") + self.CheckRecvfrom(socket.AF_INET6, "::1") + + +if __name__ == "__main__": + unittest.main() diff --git a/net/test/leak_test.py b/net/test/leak_test.py new file mode 100755 index 0000000..c748155 --- /dev/null +++ b/net/test/leak_test.py @@ -0,0 +1,51 @@ +#!/usr/bin/python +# +# Copyright 2016 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from errno import * # pylint: disable=wildcard-import +from socket import * # pylint: disable=wildcard-import +import threading +import time +import unittest + +import csocket +import net_test + + +class LeakTest(net_test.NetworkTest): + + def testRecvfromLeak(self): + s = socket(AF_INET6, SOCK_DGRAM, 0) + s.bind(("::1", 0)) + + # Call shutdown on another thread while a recvfrom is in progress. + net_test.SetSocketTimeout(s, 200) + def ShutdownSocket(): + time.sleep(0.1) + self.assertRaisesErrno(ENOTCONN, s.shutdown, SHUT_RDWR) + + t = threading.Thread(target=ShutdownSocket) + t.start() + + # This could have been written with just "s.recvfrom", but because we're + # testing for a bug where the kernel returns garbage, it's probably safer + # to call the syscall directly. + data, addr = csocket.Recvfrom(s, 4096) + self.assertEqual("", data) + self.assertEqual(None, addr) + + +if __name__ == "__main__": + unittest.main() |