Branch data Line data Source code
1 : : // Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
2 : : //
3 : : // Permission to use, copy, modify, and/or distribute this software for any
4 : : // purpose with or without fee is hereby granted, provided that the above
5 : : // copyright notice and this permission notice appear in all copies.
6 : : //
7 : : // THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
8 : : // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
9 : : // AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
10 : : // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
11 : : // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
12 : : // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
13 : : // PERFORMANCE OF THIS SOFTWARE.
14 : :
15 : : // Enable this if you use s# variants with PyArg_ParseTuple(), see
16 : : // http://docs.python.org/py3k/c-api/arg.html#strings-and-buffers
17 : : //#define PY_SSIZE_T_CLEAN
18 : :
19 : : // Python.h needs to be placed at the head of the program file, see:
20 : : // http://docs.python.org/py3k/extending/extending.html#a-simple-example
21 : : #include <Python.h>
22 : :
23 : : #include <sys/types.h>
24 : : #include <sys/socket.h>
25 : : #include <netdb.h>
26 : :
27 : : #include <string>
28 : : #include <stdexcept>
29 : :
30 : : #include <boost/lexical_cast.hpp>
31 : :
32 : : #include <exceptions/exceptions.h>
33 : :
34 : : #include <util/io/sockaddr_util.h>
35 : : #include <util/io/socketsession.h>
36 : : #include <util/python/pycppwrapper_util.h>
37 : :
38 : : #include "socketsession_python.h"
39 : : #include "socketsessionforwarder_python.h"
40 : :
41 : : using namespace std;
42 : : using namespace isc::util::python;
43 : : using namespace isc::util::io;
44 : : using namespace isc::util::io::internal;
45 : : using namespace isc::util::io::python;
46 : : using boost::lexical_cast;
47 : :
48 : : // Trivial constructor.
49 : 0 : s_SocketSessionForwarder::s_SocketSessionForwarder() : cppobj(NULL) {
50 : 0 : }
51 : :
52 : : // Import pydoc text
53 : : #include "socketsessionforwarder_inc.cc"
54 : :
55 : : // See python/isc/log/log.cc for the use of namespace
56 : : namespace clang_unnamed_namespace_workaround {
57 : : // Internal exception class thrown when address parsing fails
58 : 3 : class AddressParseError: public isc::Exception {
59 : : public:
60 : 3 : AddressParseError(const char *file, size_t line, const char *what):
61 : 3 : isc::Exception(file, line, what) {}
62 : : };
63 : : }
64 : : using namespace clang_unnamed_namespace_workaround;
65 : :
66 : : namespace {
67 : :
68 : : int
69 : 7 : SocketSessionForwarder_init(PyObject* po_self, PyObject* args, PyObject*) {
70 : : s_SocketSessionForwarder* self =
71 : 7 : static_cast<s_SocketSessionForwarder*>(po_self);
72 : : try {
73 : : const char* unix_file;
74 [ + - ][ + + ]: 7 : if (PyArg_ParseTuple(args, "s", &unix_file)) {
75 [ + - ][ + - ]: 7 : self->cppobj = new SocketSessionForwarder(unix_file);
[ + - ]
76 : : return (0);
77 : : }
78 : 0 : } catch (const exception& ex) {
79 : : const string ex_what =
80 : : "Failed to construct SocketSessionForwarder object: " +
81 [ # # ][ # # ]: 0 : string(ex.what());
82 [ # # ]: 0 : PyErr_SetString(po_SocketSessionError, ex_what.c_str());
83 : : return (-1);
84 [ # # ]: 0 : } catch (...) {
85 [ # # ]: 0 : PyErr_SetString(PyExc_SystemError, "Unexpected C++ exception");
86 : : return (-1);
87 : : }
88 : :
89 : : return (-1);
90 : : }
91 : :
92 : : void
93 : 7 : SocketSessionForwarder_destroy(PyObject* po_self) {
94 : : s_SocketSessionForwarder* self =
95 : 7 : static_cast<s_SocketSessionForwarder*>(po_self);
96 [ + + ]: 7 : delete self->cppobj;
97 : 7 : self->cppobj = NULL;
98 : 7 : Py_TYPE(self)->tp_free(self);
99 : 7 : }
100 : :
101 : : // Convert a Python socket address object to an addrinfo structure by
102 : : // getaddrinfo.
103 : : void
104 : 41 : parsePySocketAddress(PyObject* obj, int type, int protocol,
105 : : struct sockaddr_storage* ss)
106 : : {
107 : : struct addrinfo hints;
108 : : memset(&hints, 0, sizeof(hints));
109 : 41 : hints.ai_socktype = type;
110 : 41 : hints.ai_protocol = protocol;
111 : 41 : hints.ai_flags = AI_NUMERICHOST | AI_NUMERICSERV;
112 : :
113 : : const char* addr;
114 : : int port, flowinfo, scopeid;
115 : : struct addrinfo *res;
116 [ + + ]: 41 : if (PyArg_ParseTuple(obj, "si", &addr, &port)) {
117 : : // Possibly an IPv4 address.
118 : 28 : hints.ai_family = AF_INET;
119 : : const int error = getaddrinfo(addr,
120 : 28 : lexical_cast<string>(port).c_str(),
121 [ + - ]: 28 : &hints, &res);
122 [ + + ]: 28 : if (error == 0) {
123 [ - + ]: 26 : assert(res->ai_addrlen <= sizeof(*ss));
124 : 26 : memcpy(ss, res->ai_addr, res->ai_addrlen);
125 : : return;
126 : : }
127 [ + - ][ + - ]: 4 : isc_throw(AddressParseError, "Invalid or unsupported socket address: "
[ + - ]
128 : : << gai_strerror(error));
129 : : }
130 : 13 : PyErr_Clear();
131 [ + + ]: 13 : if (PyArg_ParseTuple(obj, "siii", &addr, &port, &flowinfo, &scopeid)) {
132 : : // Possibly an IPv6 address. We ignore flowinfo.
133 : 12 : hints.ai_family = AF_INET6;
134 : : const int error = getaddrinfo(addr,
135 : 12 : lexical_cast<string>(port).c_str(),
136 [ + - ]: 12 : &hints, &res);
137 [ + - ]: 12 : if (error == 0) {
138 [ - + ]: 12 : assert(res->ai_addrlen <= sizeof(*ss));
139 : 12 : memcpy(ss, res->ai_addr, res->ai_addrlen);
140 : 12 : void* p = ss;
141 : 12 : static_cast<struct sockaddr_in6*>(p)->sin6_scope_id = scopeid;
142 : 12 : return;
143 : : }
144 [ # # ][ # # ]: 0 : isc_throw(AddressParseError, "Invalid or unsupported socket address: "
[ # # ]
145 : : << gai_strerror(error));
146 : : }
147 : 1 : PyErr_Clear();
148 [ + - ][ + - ]: 40 : isc_throw(AddressParseError, "Invalid or unsupported socket address, must "
149 : : "be AF_INET or AF_INET6 socket address.");
150 : : }
151 : :
152 : : PyObject*
153 : 3 : SocketSessionForwarder_connectToReceiver(PyObject* po_self, PyObject*) {
154 : : s_SocketSessionForwarder* const self =
155 : 3 : static_cast<s_SocketSessionForwarder*>(po_self);
156 : :
157 : : try {
158 [ + - ]: 3 : self->cppobj->connectToReceiver();
159 : 3 : Py_RETURN_NONE;
160 : 0 : } catch (const isc::BadValue& ex) {
161 [ # # ]: 0 : PyErr_SetString(PyExc_TypeError, ex.what());
162 : : return (NULL);
163 : 0 : } catch (const SocketSessionError& ex) {
164 [ # # ]: 0 : PyErr_SetString(po_SocketSessionError, ex.what());
165 : : return (NULL);
166 : 0 : } catch (const exception& ex) {
167 : : const string ex_what =
168 : : "Unexpected failure in connecting to receiver: " +
169 [ # # ][ # # ]: 0 : string(ex.what());
170 [ # # ]: 0 : PyErr_SetString(PyExc_SystemError, ex_what.c_str());
171 : : return (NULL);
172 [ # # # # ]: 0 : } catch (...) {
173 [ # # ]: 0 : PyErr_SetString(PyExc_SystemError, "Unexpected C++ exception");
174 : : return (NULL);
175 : : }
176 : : }
177 : :
178 : : PyObject*
179 : 25 : SocketSessionForwarder_push(PyObject* po_self, PyObject* args) {
180 : : s_SocketSessionForwarder* const self =
181 : 25 : static_cast<s_SocketSessionForwarder*>(po_self);
182 : :
183 : : try {
184 : : int fd, family, type, protocol;
185 : : PyObject* po_local_end;
186 : : PyObject* po_remote_end;
187 : : Py_buffer py_buf;
188 : :
189 [ + + ]: 25 : if (!PyArg_ParseTuple(args, "iiiiOOy*", &fd, &family, &type, &protocol,
190 [ + - ]: 25 : &po_local_end, &po_remote_end, &py_buf)) {
191 : : return (NULL);
192 : : }
193 : : struct sockaddr_storage ss_local, ss_remote;
194 [ + + ]: 22 : parsePySocketAddress(po_local_end, type, protocol, &ss_local);
195 [ + - ]: 19 : parsePySocketAddress(po_remote_end, type, protocol, &ss_remote);
196 : : self->cppobj->push(fd, family, type, protocol,
197 : : *convertSockAddr(&ss_local),
198 : 19 : *convertSockAddr(&ss_remote),
199 [ + + ]: 19 : py_buf.buf, py_buf.len);
200 : 25 : Py_RETURN_NONE;
201 : 6 : } catch (const AddressParseError& ex) {
202 [ - + ]: 3 : PyErr_SetString(PyExc_TypeError, ex.what());
203 : : return (NULL);
204 : 10 : } catch (const isc::BadValue& ex) {
205 [ - + ]: 5 : PyErr_SetString(PyExc_TypeError, ex.what());
206 : : return (NULL);
207 : 4 : } catch (const SocketSessionError& ex) {
208 [ - + ]: 2 : PyErr_SetString(po_SocketSessionError, ex.what());
209 : : return (NULL);
210 [ + + + - ]: 10 : } catch (...) {
211 [ # # ]: 0 : PyErr_SetString(PyExc_SystemError, "Unexpected C++ exception");
212 : : return (NULL);
213 : : }
214 : : }
215 : :
216 : : // This list contains the actual set of functions we have in
217 : : // python. Each entry has
218 : : // 1. Python method name
219 : : // 2. Our static function here
220 : : // 3. Argument type
221 : : // 4. Documentation
222 : : PyMethodDef SocketSessionForwarder_methods[] = {
223 : : { "push", SocketSessionForwarder_push, METH_VARARGS,
224 : : SocketSessionForwarder_push_doc },
225 : : { "connect_to_receiver", SocketSessionForwarder_connectToReceiver,
226 : : METH_NOARGS, SocketSessionForwarder_connectToReceiver_doc },
227 : : { NULL, NULL, 0, NULL }
228 : : };
229 : : } // end of unnamed namespace
230 : :
231 : : namespace isc {
232 : : namespace util {
233 : : namespace io {
234 : : namespace python {
235 : : // This defines the complete type for reflection in python and
236 : : // parsing of PyObject* to s_SocketSessionForwarder
237 : : // Most of the functions are not actually implemented and NULL here.
238 : : PyTypeObject socketsessionforwarder_type = {
239 : : PyVarObject_HEAD_INIT(NULL, 0)
240 : : "isc.util.cio.SocketSessionForwarder",
241 : : sizeof(s_SocketSessionForwarder), // tp_basicsize
242 : : 0, // tp_itemsize
243 : : SocketSessionForwarder_destroy, // tp_dealloc
244 : : NULL, // tp_print
245 : : NULL, // tp_getattr
246 : : NULL, // tp_setattr
247 : : NULL, // tp_reserved
248 : : NULL, // tp_repr
249 : : NULL, // tp_as_number
250 : : NULL, // tp_as_sequence
251 : : NULL, // tp_as_mapping
252 : : NULL, // tp_hash
253 : : NULL, // tp_call
254 : : NULL, // tp_str
255 : : NULL, // tp_getattro
256 : : NULL, // tp_setattro
257 : : NULL, // tp_as_buffer
258 : : Py_TPFLAGS_DEFAULT, // tp_flags
259 : : SocketSessionForwarder_doc,
260 : : NULL, // tp_traverse
261 : : NULL, // tp_clear
262 : : NULL, // tp_richcompare
263 : : 0, // tp_weaklistoffset
264 : : NULL, // tp_iter
265 : : NULL, // tp_iternext
266 : : SocketSessionForwarder_methods, // tp_methods
267 : : NULL, // tp_members
268 : : NULL, // tp_getset
269 : : NULL, // tp_base
270 : : NULL, // tp_dict
271 : : NULL, // tp_descr_get
272 : : NULL, // tp_descr_set
273 : : 0, // tp_dictoffset
274 : : SocketSessionForwarder_init, // tp_init
275 : : NULL, // tp_alloc
276 : : PyType_GenericNew, // tp_new
277 : : NULL, // tp_free
278 : : NULL, // tp_is_gc
279 : : NULL, // tp_bases
280 : : NULL, // tp_mro
281 : : NULL, // tp_cache
282 : : NULL, // tp_subclasses
283 : : NULL, // tp_weaklist
284 : : NULL, // tp_del
285 : : 0 // tp_version_tag
286 : : };
287 : :
288 : : // Module Initialization, all statics are initialized here
289 : : bool
290 : 2 : initModulePart_SocketSessionForwarder(PyObject* mod) {
291 : : // We initialize the static description object with PyType_Ready(),
292 : : // then add it to the module. This is not just a check! (leaving
293 : : // this out results in segmentation faults)
294 [ + - ]: 2 : if (PyType_Ready(&socketsessionforwarder_type) < 0) {
295 : : return (false);
296 : : }
297 : 2 : void* p = &socketsessionforwarder_type;
298 [ + - ]: 2 : if (PyModule_AddObject(mod, "SocketSessionForwarder",
299 : 2 : static_cast<PyObject*>(p)) < 0) {
300 : : return (false);
301 : : }
302 : 2 : Py_INCREF(&socketsessionforwarder_type);
303 : :
304 : 2 : return (true);
305 : : }
306 : : } // namespace python
307 : : } // namespace io
308 : : } // namespace util
309 : 160 : } // namespace isc
|