Add a wrapper for sendmsg.

Change-Id: Ie1e376fdf9c067415bc682139ef7862e4b6dcf23
diff --git a/net/test/sendmsg.py b/net/test/sendmsg.py
new file mode 100644
index 0000000..bbc46ec
--- /dev/null
+++ b/net/test/sendmsg.py
@@ -0,0 +1,151 @@
+#!/usr/bin/python
+
+"""Python wrapper for sendmsg."""
+
+import ctypes
+import ctypes.util
+import os
+import socket
+import struct
+
+import cstruct
+
+
+# Data structures used by sendmsg.
+CMsgHdr = cstruct.Struct("cmsghdr", "@Lii", "len level type")
+Iovec = cstruct.Struct("iovec", "@LL", "base len")
+MsgHdr = cstruct.Struct("msghdr", "@LLLLLLi",
+                        "name namelen iov iovlen control msg_controllen flags")
+SockaddrIn = cstruct.Struct("sockaddr_in", "=HH4sxxxxxxxx", "family port addr")
+SockaddrIn6 = cstruct.Struct("sockaddr_in6", "=HHI16sI",
+                             "family port flowinfo addr scope_id")
+
+# Constants.
+CMSG_ALIGNTO = struct.calcsize("@L")  # The kernel defines this as sizeof(long).
+MSG_CONFIRM = 0X800
+
+# Find the C library.
+libc = ctypes.CDLL(ctypes.util.find_library("c"), use_errno=True)
+
+
+def PaddedLength(length):
+  return CMSG_ALIGNTO * ((length / CMSG_ALIGNTO) + (length % CMSG_ALIGNTO != 0))
+
+
+def Sockaddr(addr):
+  if ":" in addr[0]:
+    family = socket.AF_INET6
+    if len(addr) == 4:
+      addr, port, flowinfo, scope_id = addr
+    else:
+      (addr, port), flowinfo, scope_id = addr, 0, 0
+    addr = socket.inet_pton(family, addr)
+    return SockaddrIn6((family, socket.ntohs(port), socket.ntohl(flowinfo),
+                        addr, scope_id))
+  else:
+    family = socket.AF_INET
+    addr, port = addr
+    addr = socket.inet_pton(family, addr)
+    return SockaddrIn((family, socket.ntohs(port), addr))
+
+
+def _MakeMsgControl(optlist):
+  """Creates a msg_control blob from a list of cmsg attributes.
+
+  Takes a list of cmsg attributes. Each attribute is a tuple of:
+   - level: An integer, e.g., SOL_IPV6.
+   - type: An integer, the option identifier, e.g., IPV6_HOPLIMIT.
+   - data: The option data. This is either a string or an integer. If it's an
+     integer it will be written as an unsigned integer in host byte order. If
+     it's a string, it's used as is.
+
+  Data is padded to an integer multiple of CMSG_ALIGNTO.
+
+  Args:
+    optlist: A list of tuples describing cmsg options.
+
+  Returns:
+    A string, a binary blob usable as the control data for a sendmsg call.
+
+  Raises:
+    TypeError: Option data is neither an integer nor a string.
+  """
+  msg_control = ""
+
+  for i, opt in enumerate(optlist):
+    msg_level, msg_type, data = opt
+    if isinstance(data, int):
+      data = struct.pack("=I", data)
+    elif not isinstance(data, str):
+      raise TypeError("unknown data type for opt %i: %s" % (i, type(data)))
+
+    datalen = len(data)
+    msg_len = len(CMsgHdr) + datalen
+    padding = "\x00" * (PaddedLength(datalen) - datalen)
+    msg_control += CMsgHdr((msg_len, msg_level, msg_type)).Pack()
+    msg_control += data + padding
+
+  return msg_control
+
+
+def Sendmsg(s, to, data, control, flags):
+  """Python wrapper for sendmsg.
+
+  Args:
+    s: A Python socket object. Becomes sockfd.
+    to: A Python socket address tuple. Becomes msg->msg_name.
+    data: A string, the data to write. Goes into msg->msg_iov.
+    control: A list of cmsg options. Becomes msg->msg_control.
+    flags: An integer. Becomes msg->msg_flags.
+
+  Returns:
+    If sendmsg succeeds, returns the number of bytes written as an integer.
+
+  Raises:
+    socket.error: If sendmsg fails.
+  """
+  # Create ctypes buffers and pointers from our structures. We need to hang on
+  # to the underlying Python objects, because we don't want them to be garbage
+  # collected and freed while we have C pointers to them.
+
+  # Convert the destination address into a struct sockaddr.
+  if to:
+    name = Sockaddr(to)
+    msg_name = name.CPointer()
+    msg_namelen = len(name)
+  else:
+    msg_name = 0
+    msg_namelen = 0
+
+  # Convert the data to a data buffer and a struct iovec pointing at it.
+  if data:
+    databuf = ctypes.create_string_buffer(data)
+    iov = Iovec((ctypes.addressof(databuf), len(data)))
+    msg_iov = iov.CPointer()
+    msg_iovlen = 1
+  else:
+    msg_iov = 0
+    msg_iovlen = 0
+
+  # Marshal the cmsg options.
+  if control:
+    control = _MakeMsgControl(control)
+    controlbuf = ctypes.create_string_buffer(control)
+    msg_control = ctypes.addressof(controlbuf)
+    msg_controllen = len(control)
+  else:
+    msg_control = 0
+    msg_controllen = 0
+
+  # Assemble the struct msghdr.
+  msghdr = MsgHdr((msg_name, msg_namelen, msg_iov, msg_iovlen,
+                   msg_control, msg_controllen, flags)).Pack()
+
+  # Call sendmsg.
+  ret = libc.sendmsg(s.fileno(), msghdr, 0)
+
+  if ret < 0:
+    errno = ctypes.get_errno()
+    raise socket.error(errno, os.strerror(errno))
+
+  return ret