1 # Copyright 2014 The Android Open Source Project
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
7 # http://www.apache.org/licenses/LICENSE-2.0
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
15 """Python wrapper for C socket calls and data structures."""
27 CMsgHdr = cstruct.Struct("cmsghdr", "@Lii", "len level type")
28 Iovec = cstruct.Struct("iovec", "@LL", "base len")
29 MsgHdr = cstruct.Struct("msghdr", "@LLLLLLi",
30 "name namelen iov iovlen control msg_controllen flags")
31 SockaddrIn = cstruct.Struct("sockaddr_in", "=HH4sxxxxxxxx", "family port addr")
32 SockaddrIn6 = cstruct.Struct("sockaddr_in6", "=HHI16sI",
33 "family port flowinfo addr scope_id")
36 CMSG_ALIGNTO = struct.calcsize("@L") # The kernel defines this as sizeof(long).
40 libc = ctypes.CDLL(ctypes.util.find_library("c"), use_errno=True)
43 def PaddedLength(length):
44 return CMSG_ALIGNTO * ((length / CMSG_ALIGNTO) + (length % CMSG_ALIGNTO != 0))
47 def MaybeRaiseSocketError(ret):
49 errno = ctypes.get_errno()
50 raise socket.error(errno, os.strerror(errno))
55 family = socket.AF_INET6
57 addr, port, flowinfo, scope_id = addr
59 (addr, port), flowinfo, scope_id = addr, 0, 0
60 addr = socket.inet_pton(family, addr)
61 return SockaddrIn6((family, socket.ntohs(port), socket.ntohl(flowinfo),
64 family = socket.AF_INET
66 addr = socket.inet_pton(family, addr)
67 return SockaddrIn((family, socket.ntohs(port), addr))
70 def _MakeMsgControl(optlist):
71 """Creates a msg_control blob from a list of cmsg attributes.
73 Takes a list of cmsg attributes. Each attribute is a tuple of:
74 - level: An integer, e.g., SOL_IPV6.
75 - type: An integer, the option identifier, e.g., IPV6_HOPLIMIT.
76 - data: The option data. This is either a string or an integer. If it's an
77 integer it will be written as an unsigned integer in host byte order. If
78 it's a string, it's used as is.
80 Data is padded to an integer multiple of CMSG_ALIGNTO.
83 optlist: A list of tuples describing cmsg options.
86 A string, a binary blob usable as the control data for a sendmsg call.
89 TypeError: Option data is neither an integer nor a string.
93 for i, opt in enumerate(optlist):
94 msg_level, msg_type, data = opt
95 if isinstance(data, int):
96 data = struct.pack("=I", data)
97 elif not isinstance(data, str):
98 raise TypeError("unknown data type for opt %i: %s" % (i, type(data)))
101 msg_len = len(CMsgHdr) + datalen
102 padding = "\x00" * (PaddedLength(datalen) - datalen)
103 msg_control += CMsgHdr((msg_len, msg_level, msg_type)).Pack()
104 msg_control += data + padding
110 """Python wrapper for connect."""
111 ret = libc.bind(s.fileno(), to.CPointer(), len(to))
112 MaybeRaiseSocketError(ret)
116 """Python wrapper for connect."""
117 ret = libc.connect(s.fileno(), to.CPointer(), len(to))
118 MaybeRaiseSocketError(ret)
122 def Sendmsg(s, to, data, control, flags):
123 """Python wrapper for sendmsg.
126 s: A Python socket object. Becomes sockfd.
127 to: An address tuple, or a SockaddrIn[6] struct. Becomes msg->msg_name.
128 data: A string, the data to write. Goes into msg->msg_iov.
129 control: A list of cmsg options. Becomes msg->msg_control.
130 flags: An integer. Becomes msg->msg_flags.
133 If sendmsg succeeds, returns the number of bytes written as an integer.
136 socket.error: If sendmsg fails.
138 # Create ctypes buffers and pointers from our structures. We need to hang on
139 # to the underlying Python objects, because we don't want them to be garbage
140 # collected and freed while we have C pointers to them.
142 # Convert the destination address into a struct sockaddr.
144 if isinstance(to, tuple):
146 msg_name = to.CPointer()
147 msg_namelen = len(to)
152 # Convert the data to a data buffer and a struct iovec pointing at it.
154 databuf = ctypes.create_string_buffer(data)
155 iov = Iovec((ctypes.addressof(databuf), len(data)))
156 msg_iov = iov.CPointer()
162 # Marshal the cmsg options.
164 control = _MakeMsgControl(control)
165 controlbuf = ctypes.create_string_buffer(control)
166 msg_control = ctypes.addressof(controlbuf)
167 msg_controllen = len(control)
172 # Assemble the struct msghdr.
173 msghdr = MsgHdr((msg_name, msg_namelen, msg_iov, msg_iovlen,
174 msg_control, msg_controllen, flags)).Pack()
177 ret = libc.sendmsg(s.fileno(), msghdr, 0)
178 MaybeRaiseSocketError(ret)