blob: 020c2adc31bc2423295c7304ab787b45579f952d [file] [log] [blame]
Scott James Remnant96927a42013-07-17 18:27:57 -07001/* Copyright (c) 2013 The Chromium Authors. All rights reserved.
2 * Use of this source code is governed by a BSD-style license that can be
3 * found in the LICENSE file.
4 */
5
6#include <Python.h>
7
8#include <sys/types.h>
9#include <sys/socket.h>
10#include <poll.h>
11
12#include "bluetooth.h"
13#include "hci.h"
14
15static PyObject *_btsocket_error;
16static PyObject *_btsocket_timeout;
17
18static int _get_fileno(PyObject *socket) {
19 PyObject *fileno_object;
20 long fileno;
21
22 fileno_object = PyObject_CallMethod(socket, "fileno", "", NULL);
23 if (!fileno_object)
24 return -1;
25
26 if (!PyInt_Check(fileno_object))
27 return -1;
28
29 /* PyInts are longer than your average int, truncate back to an int */
30 fileno = PyInt_AS_LONG(fileno_object);
31 Py_DECREF(fileno_object);
32 return (int)fileno;
33}
34
35static int _get_timeout(PyObject *socket, double *timeout) {
36 PyObject *timeout_object;
37
38 timeout_object = PyObject_CallMethod(socket, "gettimeout", "", NULL);
39 if (!timeout_object)
40 return 0;
41
42 if (!PyFloat_Check(timeout_object))
43 return 0;
44
45 *timeout = PyFloat_AS_DOUBLE(timeout_object);
46 Py_DECREF(timeout_object);
47 return 1;
48}
49
50static PyObject *_btsocket_bind(PyObject *self, PyObject *args) {
51 PyObject *socket_object;
52 struct sockaddr_hci addr;
53 int fd, result;
54
55 addr.hci_family = AF_BLUETOOTH;
56
57 if (!PyArg_ParseTuple(args, "O(HH):_btsocket.bind",
58 &socket_object, &addr.hci_dev, &addr.hci_channel))
59 return NULL;
60
61 fd = _get_fileno(socket_object);
62 if (fd < 0)
63 return NULL;
64
65 Py_BEGIN_ALLOW_THREADS;
66 result = bind(fd, (struct sockaddr *)&addr, sizeof addr);
67 Py_END_ALLOW_THREADS;
68
69 if (result < 0)
70 return PyErr_SetFromErrno(_btsocket_error);
71
72 Py_INCREF(Py_None);
73 return Py_None;
74}
75
76static PyObject *_btsocket_recvmsg(PyObject *self, PyObject *args) {
77 PyObject *socket_object, *buffers, *iterator, *cmsg_list = NULL, *addrval;
78 PyObject *retval = NULL;
79 Py_ssize_t controllen = 0, nbuffers, buf_index = 0;
80 int flags = 0, fd, i, timeout = 0, did_timeout = 0;
81 double timeout_secs;
82 struct iovec *iovs = NULL;
83 Py_buffer *bufs = NULL;
84 void *controlbuf = NULL;
85 struct sockaddr_hci addr;
86 struct msghdr msg = {0};
87 ssize_t len;
88 struct cmsghdr *cmsgh;
89
90 /* Parse arguments, allocating an iovec array matching the incoming buffers
91 list and a matching PyBuffer for each one that we can fetch the incoming
92 buffer into for receiving. */
93 if (!PyArg_ParseTuple(args, "OO|ni:_btsocket.recvmsg_into",
94 &socket_object, &buffers, &controllen, &flags))
95 return NULL;
96
97 fd = _get_fileno(socket_object);
98 if (fd < 0)
99 return NULL;
100
101 timeout = _get_timeout(socket_object, &timeout_secs);
102
103 iterator = PySequence_Fast(buffers, ("recvmsg_into() argument 1 must be an "
104 "iterable"));
105 if (!iterator)
106 return NULL;
107
108 nbuffers = PySequence_Fast_GET_SIZE(iterator);
109 if (nbuffers > INT_MAX) {
110 PyErr_SetString(_btsocket_error, "recvmsg_into() argument 1 is too long");
111 goto finally;
112 }
113
114 if (nbuffers > 0) {
115 iovs = PyMem_New(struct iovec, nbuffers);
116 bufs = PyMem_New(Py_buffer, nbuffers);
117
118 if (!iovs || !bufs) {
119 PyErr_NoMemory();
120 goto finally;
121 }
122 }
123
124 for (buf_index = 0; buf_index < nbuffers; ++buf_index) {
125 if (!PyArg_Parse(PySequence_Fast_GET_ITEM(iterator, buf_index),
126 ("w*;recvmsg_into() argument 1 must be an iterable "
127 "of single-segment read-write buffers"),
128 &bufs[buf_index]))
129 goto finally;
130
131 iovs[buf_index].iov_base = bufs[buf_index].buf;
132 iovs[buf_index].iov_len = bufs[buf_index].len;
133 }
134
135 /* Allocate a control buffer large enough to receive ancillary data. */
136 if (controllen < 0 || controllen > INT_MAX) {
137 PyErr_SetString(_btsocket_error, "recvmsg_into() argument 2 invalid");
138 goto finally;
139 }
140
141 if (controllen > 0) {
142 controlbuf = PyMem_Malloc(controllen);
143 if (!controlbuf) {
144 PyErr_NoMemory();
145 goto finally;
146 }
147 }
148
149 /* Receive data on the socket. */
150 Py_BEGIN_ALLOW_THREADS;
151 msg.msg_name = (struct sockaddr *)&addr;
152 msg.msg_namelen = sizeof addr;
153 msg.msg_iov = iovs;
154 msg.msg_iovlen = nbuffers;
155 msg.msg_control = controlbuf;
156 msg.msg_controllen = controllen;
157
158 if (timeout) {
159 struct pollfd pollfd;
160 int timeout_ms;
161 int n;
162
163 pollfd.fd = fd;
164 pollfd.events = POLLIN;
165
166 /* timeout limits are set by Python */
167 timeout_ms = (int)(timeout_secs * 1000);
168 n = poll(&pollfd, 1, timeout_ms);
169 if (n <= 0)
170 did_timeout = 1;
171 }
172
173 if (!did_timeout)
174 len = recvmsg(fd, &msg, flags);
175 Py_END_ALLOW_THREADS;
176
177 if (did_timeout) {
178 PyErr_SetString(_btsocket_timeout, "timed out");
179 goto finally;
180 }
181 if (len < 0) {
182 PyErr_SetFromErrno(_btsocket_error);
183 goto finally;
184 }
185
186 /* Parse control message data into a list we pass in the return value. */
187 cmsg_list = PyList_New(0);
188 if (!cmsg_list)
189 goto finally;
190
191 for (cmsgh = CMSG_FIRSTHDR(&msg); cmsgh != NULL;
192 cmsgh = CMSG_NXTHDR(&msg, cmsgh)) {
193 size_t cmsgdatalen;
194 PyObject *bytes, *tuple;
195 int tmp;
196
197 /* cmsg_len includes the length of the 'struct cmsghdr' and any padding
198 the kernel sees fit to add. CMSG_LEN(0) gives the value that cmsg_len
199 would have if there were 0 bytes of additional data. Thus by doing
200 cmsg_len - CMSG_LEN(0) we get the actual length of data.
201
202 Never let kernel engineers design APIs. */
203 cmsgdatalen = cmsgh->cmsg_len - CMSG_LEN(0);
204 /* bytes is refcounted, if NULL the Py_BuildValue will fail and tuple will
205 be NULL too - which we catch. If it succeeds, the refcount will be 0
206 until Py_BuildValue succeeds. So there's no need to explicitly free or
207 decref bytes. */
208 bytes = PyBytes_FromStringAndSize((char *)CMSG_DATA(cmsgh), cmsgdatalen);
209 tuple = Py_BuildValue("iiN",
210 (int)cmsgh->cmsg_level, (int)cmsgh->cmsg_type, bytes);
211 if (tuple == NULL)
212 goto finally;
213
214 tmp = PyList_Append(cmsg_list, tuple);
215 Py_DECREF(tuple);
216 if (tmp != 0)
217 goto finally;
218 }
219
220 /* Build the rest of the return value. */
221 addrval = Py_BuildValue("ii", addr.hci_dev, addr.hci_channel);
222 if (!addrval)
223 goto finally;
224
225 retval = Py_BuildValue("NOiN",
226 PyLong_FromSsize_t(len),
227 cmsg_list,
228 (int)msg.msg_flags,
229 addrval);
230
231 /* Clean up in success cases as well as error. */
232finally:
233 Py_XDECREF(cmsg_list);
234 PyMem_Free(controlbuf);
235 /* We can abort out of the allocation loop only, so buffers will only be
236 allocated up to buf_index not nbuffers. */
237 for (i = 0; i < buf_index; ++i)
238 PyBuffer_Release(&bufs[i]);
239 PyMem_Free(bufs);
240 PyMem_Free(iovs);
241 Py_DECREF(iterator);
242 return retval;
243}
244
245
246static PyMethodDef _btsocket_methods[] = {
247 { "bind", _btsocket_bind, METH_VARARGS,
248 "Bind a Bluetooth socket to a device and channel" },
249 { "recvmsg", _btsocket_recvmsg, METH_VARARGS,
250 "Receive normal and ancillary data from a Bluetooth socket" },
251
252 { NULL, NULL, 0, NULL }
253};
254
255PyMODINIT_FUNC
256init_btsocket(void)
257{
258 PyObject *m;
259
260 m = Py_InitModule("_btsocket", _btsocket_methods);
261 if (!m)
262 return;
263
264 _btsocket_error = PyErr_NewException("btsocket.error",
265 PyExc_OSError, NULL);
266 Py_INCREF(_btsocket_error);
267 PyModule_AddObject(m, "error", _btsocket_error);
268
269 _btsocket_timeout = PyErr_NewException("btsocket.timeout",
270 PyExc_OSError, NULL);
271 Py_INCREF(_btsocket_timeout);
272 PyModule_AddObject(m, "timeout", _btsocket_timeout);
273}