blob: 00cb0e30db6939d7ab1a10802bf8beab19755321 [file] [log] [blame]
Thierry FOURNIER00a02252018-02-25 20:59:57 +01001/* spoa-server: processing Python
2 *
3 * Copyright 2018 OZON / Thierry Fournier <thierry.fournier@ozon.io>
Gilchrist Dadaglo3e235d32020-05-06 12:25:31 +00004 * Copyright (C) 2020 Gilchrist Dadaglo <gilchrist@dadaglo.com>
Thierry FOURNIER00a02252018-02-25 20:59:57 +01005 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version
9 * 2 of the License, or (at your option) any later version.
10 *
Gilchrist Dadaglo3e235d32020-05-06 12:25:31 +000011 * This program is provided in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 */
17
18/*
19 * Define PY_SSIZE_T_CLEAN before including Python.h
20 * as per https://docs.python.org/3/c-api/arg.html and https://docs.python.org/2/c-api/arg.html
Thierry FOURNIER00a02252018-02-25 20:59:57 +010021 */
Gilchrist Dadaglo3e235d32020-05-06 12:25:31 +000022#define PY_SSIZE_T_CLEAN
Thierry FOURNIER00a02252018-02-25 20:59:57 +010023
24#include <Python.h>
25
26#include <arpa/inet.h>
27
28#include <errno.h>
29#include <string.h>
Gilchrist Dadaglo3e235d32020-05-06 12:25:31 +000030#include <limits.h>
Thierry FOURNIER00a02252018-02-25 20:59:57 +010031
32#include "spoa.h"
Gilchrist Dadaglo3e235d32020-05-06 12:25:31 +000033#include "ps_python.h"
Thierry FOURNIER00a02252018-02-25 20:59:57 +010034
35/* Embedding python documentation:
36 *
37 * https://docs.python.org/2/extending/embedding.html
38 * https://docs.python.org/2/extending/extending.html#extending-python-with-c-or-c
39 * https://docs.python.org/2/extending/extending.html#calling-python-functions-from-c
40 */
41
42static PyObject *module_ipaddress;
43static PyObject *ipv4_address;
44static PyObject *ipv6_address;
45static PyObject *spoa_error;
46static PyObject *empty_array;
47static struct worker *worker;
48
49static int ps_python_start_worker(struct worker *w);
50static int ps_python_load_file(struct worker *w, const char *file);
51static int ps_python_exec_message(struct worker *w, void *ref, int nargs, struct spoe_kv *args);
52
53static struct ps ps_python_bindings = {
54 .init_worker = ps_python_start_worker,
55 .load_file = ps_python_load_file,
56 .exec_message = ps_python_exec_message,
57 .ext = ".py",
58};
59
Gilchrist Dadaglo3e235d32020-05-06 12:25:31 +000060static int ps_python_check_overflow(Py_ssize_t len)
61{
62 /* There might be an overflow when converting from Py_ssize_t to int.
63 * This function will catch those cases.
64 * Also, spoa "struct chunk" is limited to int size.
65 * We should not send data bigger than it can handle.
66 */
67 if (len >= (Py_ssize_t)INT_MAX) {
68 PyErr_Format(spoa_error,
69 "%d is over 2GB. Please split in smaller pieces.", \
70 len);
71 return -1;
72 } else {
73 return Py_SAFE_DOWNCAST(len, Py_ssize_t, int);
74 }
75}
76
77#if IS_PYTHON_3K
78static PyObject *module_spoa;
79static PyObject *PyInit_spoa_module(void);
80#endif /* IS_PYTHON_3K */
81
Thierry FOURNIER00a02252018-02-25 20:59:57 +010082static PyObject *ps_python_register_message(PyObject *self, PyObject *args)
83{
84 const char *name;
85 PyObject *ref;
86
87 if (!PyArg_ParseTuple(args, "sO!", &name, &PyFunction_Type, &ref))
88 return NULL;
Ilya Shipitsince7b00f2020-03-23 22:28:40 +050089 Py_XINCREF(ref); /* because the function is internally referenced */
Thierry FOURNIER00a02252018-02-25 20:59:57 +010090
91 ps_register_message(&ps_python_bindings, name, (void *)ref);
92
93 return Py_None;
94}
95
96static PyObject *ps_python_set_var_null(PyObject *self, PyObject *args)
97{
98 const char *name;
Gilchrist Dadaglo3e235d32020-05-06 12:25:31 +000099 Py_ssize_t name_len;
100 int name_len_i;
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100101 int scope;
102
103 if (!PyArg_ParseTuple(args, "s#i", &name, &name_len, &scope))
104 return NULL;
Gilchrist Dadaglo3e235d32020-05-06 12:25:31 +0000105 name_len_i = ps_python_check_overflow(name_len);
106 if (name_len_i == -1)
107 return NULL;
108 if (!set_var_null(worker, name, name_len_i, scope)) {
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100109 PyErr_SetString(spoa_error, "No space left available");
110 return NULL;
111 }
112 return Py_None;
113}
114
115static PyObject *ps_python_set_var_boolean(PyObject *self, PyObject *args)
116{
117 const char *name;
Gilchrist Dadaglo3e235d32020-05-06 12:25:31 +0000118 Py_ssize_t name_len;
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100119 int scope;
120 int value;
Gilchrist Dadaglo3e235d32020-05-06 12:25:31 +0000121 int name_len_i;
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100122
123 if (!PyArg_ParseTuple(args, "s#ii", &name, &name_len, &scope, &value))
124 return NULL;
Gilchrist Dadaglo3e235d32020-05-06 12:25:31 +0000125 name_len_i = ps_python_check_overflow(name_len);
126 if (name_len_i == -1)
127 return NULL;
128 if (!set_var_bool(worker, name, name_len_i, scope, value)) {
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100129 PyErr_SetString(spoa_error, "No space left available");
130 return NULL;
131 }
132 return Py_None;
133}
134
135static PyObject *ps_python_set_var_int32(PyObject *self, PyObject *args)
136{
137 const char *name;
Gilchrist Dadaglo3e235d32020-05-06 12:25:31 +0000138 Py_ssize_t name_len;
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100139 int scope;
140 int32_t value;
Gilchrist Dadaglo3e235d32020-05-06 12:25:31 +0000141 int name_len_i;
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100142
143 if (!PyArg_ParseTuple(args, "s#ii", &name, &name_len, &scope, &value))
144 return NULL;
Gilchrist Dadaglo3e235d32020-05-06 12:25:31 +0000145 name_len_i = ps_python_check_overflow(name_len);
146 if (name_len_i == -1)
147 return NULL;
148 if (!set_var_int32(worker, name, name_len_i, scope, value)) {
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100149 PyErr_SetString(spoa_error, "No space left available");
150 return NULL;
151 }
152 return Py_None;
153}
154
155static PyObject *ps_python_set_var_uint32(PyObject *self, PyObject *args)
156{
157 const char *name;
Gilchrist Dadaglo3e235d32020-05-06 12:25:31 +0000158 Py_ssize_t name_len;
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100159 int scope;
160 uint32_t value;
Gilchrist Dadaglo3e235d32020-05-06 12:25:31 +0000161 int name_len_i;
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100162
163 if (!PyArg_ParseTuple(args, "s#iI", &name, &name_len, &scope, &value))
164 return NULL;
Gilchrist Dadaglo3e235d32020-05-06 12:25:31 +0000165 name_len_i = ps_python_check_overflow(name_len);
166 if (name_len_i == -1)
167 return NULL;
168 if (!set_var_uint32(worker, name, name_len_i, scope, value)) {
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100169 PyErr_SetString(spoa_error, "No space left available");
170 return NULL;
171 }
172 return Py_None;
173}
174
175static PyObject *ps_python_set_var_int64(PyObject *self, PyObject *args)
176{
177 const char *name;
Gilchrist Dadaglo3e235d32020-05-06 12:25:31 +0000178 Py_ssize_t name_len;
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100179 int scope;
180 int64_t value;
Gilchrist Dadaglo3e235d32020-05-06 12:25:31 +0000181 int name_len_i;
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100182
183 if (!PyArg_ParseTuple(args, "s#il", &name, &name_len, &scope, &value))
184 return NULL;
Gilchrist Dadaglo3e235d32020-05-06 12:25:31 +0000185 name_len_i = ps_python_check_overflow(name_len);
186 if (name_len_i == -1)
187 return NULL;
188 if (!set_var_int64(worker, name, name_len_i, scope, value)) {
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100189 PyErr_SetString(spoa_error, "No space left available");
190 return NULL;
191 }
192 return Py_None;
193}
194
195static PyObject *ps_python_set_var_uint64(PyObject *self, PyObject *args)
196{
197 const char *name;
Gilchrist Dadaglo3e235d32020-05-06 12:25:31 +0000198 Py_ssize_t name_len;
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100199 int scope;
200 uint64_t value;
Gilchrist Dadaglo3e235d32020-05-06 12:25:31 +0000201 int name_len_i;
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100202
203 if (!PyArg_ParseTuple(args, "s#ik", &name, &name_len, &scope, &value))
204 return NULL;
Gilchrist Dadaglo3e235d32020-05-06 12:25:31 +0000205 name_len_i = ps_python_check_overflow(name_len);
206 if (name_len_i == -1)
207 return NULL;
208 if (!set_var_uint64(worker, name, name_len_i, scope, value)) {
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100209 PyErr_SetString(spoa_error, "No space left available");
210 return NULL;
211 }
212 return Py_None;
213}
214
215static PyObject *ps_python_set_var_ipv4(PyObject *self, PyObject *args)
216{
217 const char *name;
Gilchrist Dadaglo3e235d32020-05-06 12:25:31 +0000218 Py_ssize_t name_len;
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100219 int scope;
220 PyObject *ipv4;
221 PyObject *value;
222 struct in_addr ip;
Gilchrist Dadaglo3e235d32020-05-06 12:25:31 +0000223 int name_len_i;
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100224
225 if (!PyArg_ParseTuple(args, "s#iO", &name, &name_len, &scope, &ipv4))
226 return NULL;
Gilchrist Dadaglo3e235d32020-05-06 12:25:31 +0000227 name_len_i = ps_python_check_overflow(name_len);
228 if (name_len_i == -1)
229 return NULL;
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100230 if (!PyObject_IsInstance(ipv4, ipv4_address)) {
231 PyErr_Format(spoa_error, "must be 'IPv4Address', not '%s'", ipv4->ob_type->tp_name);
232 return NULL;
233 }
234 /* Execute packed ... I think .. */
235 value = PyObject_GetAttrString(ipv4, "packed");
236 if (value == NULL)
237 return NULL;
Gilchrist Dadaglo3e235d32020-05-06 12:25:31 +0000238 if (PY_STRING_GET_SIZE(value) != sizeof(ip)) {
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100239 PyErr_Format(spoa_error, "UPv6 manipulation internal error");
240 return NULL;
241 }
Gilchrist Dadaglo3e235d32020-05-06 12:25:31 +0000242 memcpy(&ip, PY_STRING_AS_STRING(value), PY_STRING_GET_SIZE(value));
243 if (!set_var_ipv4(worker, name, name_len_i, scope, &ip)) {
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100244 PyErr_SetString(spoa_error, "No space left available");
245 return NULL;
246 }
247 return Py_None;
248}
249
250static PyObject *ps_python_set_var_ipv6(PyObject *self, PyObject *args)
251{
252 const char *name;
Gilchrist Dadaglo3e235d32020-05-06 12:25:31 +0000253 Py_ssize_t name_len;
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100254 int scope;
255 PyObject *ipv6;
256 PyObject *value;
257 struct in6_addr ip;
Gilchrist Dadaglo3e235d32020-05-06 12:25:31 +0000258 int name_len_i;
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100259
260 if (!PyArg_ParseTuple(args, "s#iO", &name, &name_len, &scope, &ipv6))
261 return NULL;
Gilchrist Dadaglo3e235d32020-05-06 12:25:31 +0000262 name_len_i = ps_python_check_overflow(name_len);
263 if (name_len_i == -1)
264 return NULL;
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100265 if (!PyObject_IsInstance(ipv6, ipv6_address)) {
266 PyErr_Format(spoa_error, "must be 'IPv6Address', not '%s'", ipv6->ob_type->tp_name);
267 return NULL;
268 }
269 /* Execute packed ... I think .. */
270 value = PyObject_GetAttrString(ipv6, "packed");
271 if (value == NULL)
272 return NULL;
Gilchrist Dadaglo3e235d32020-05-06 12:25:31 +0000273 if (PY_STRING_GET_SIZE(value) != sizeof(ip)) {
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100274 PyErr_Format(spoa_error, "UPv6 manipulation internal error");
275 return NULL;
276 }
Gilchrist Dadaglo3e235d32020-05-06 12:25:31 +0000277 memcpy(&ip, PY_STRING_AS_STRING(value), PY_STRING_GET_SIZE(value));
278 if (!set_var_ipv6(worker, name, name_len_i, scope, &ip)) {
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100279 PyErr_SetString(spoa_error, "No space left available");
280 return NULL;
281 }
282 return Py_None;
283}
284
285static PyObject *ps_python_set_var_str(PyObject *self, PyObject *args)
286{
287 const char *name;
Gilchrist Dadaglo3e235d32020-05-06 12:25:31 +0000288 Py_ssize_t name_len;
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100289 int scope;
290 const char *value;
Gilchrist Dadaglo3e235d32020-05-06 12:25:31 +0000291 Py_ssize_t value_len;
292 int name_len_i;
293 int value_len_i;
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100294
295 if (!PyArg_ParseTuple(args, "s#is#", &name, &name_len, &scope, &value, &value_len))
296 return NULL;
Gilchrist Dadaglo3e235d32020-05-06 12:25:31 +0000297 name_len_i = ps_python_check_overflow(name_len);
298 value_len_i = ps_python_check_overflow(value_len);
299 if (name_len_i == -1 || value_len_i == -1)
300 return NULL;
301 if (!set_var_string(worker, name, name_len_i, scope, value, value_len_i)) {
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100302 PyErr_SetString(spoa_error, "No space left available");
303 return NULL;
304 }
305 return Py_None;
306}
307
308static PyObject *ps_python_set_var_bin(PyObject *self, PyObject *args)
309{
310 const char *name;
Gilchrist Dadaglo3e235d32020-05-06 12:25:31 +0000311 Py_ssize_t name_len;
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100312 int scope;
313 const char *value;
Gilchrist Dadaglo3e235d32020-05-06 12:25:31 +0000314 Py_ssize_t value_len;
315 int name_len_i;
316 int value_len_i;
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100317
318 if (!PyArg_ParseTuple(args, "s#is#", &name, &name_len, &scope, &value, &value_len))
319 return NULL;
Gilchrist Dadaglo3e235d32020-05-06 12:25:31 +0000320 name_len_i = ps_python_check_overflow(name_len);
321 value_len_i = ps_python_check_overflow(value_len);
322 if (name_len_i == -1 || value_len_i == -1)
323 return NULL;
324 if (!set_var_bin(worker, name, name_len_i, scope, value, value_len_i)) {
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100325 PyErr_SetString(spoa_error, "No space left available");
326 return NULL;
327 }
328 return Py_None;
329}
330
331
332static PyMethodDef spoa_methods[] = {
333 {"register_message", ps_python_register_message, METH_VARARGS,
334 "Register binding for SPOA message."},
335 {"set_var_null", ps_python_set_var_null, METH_VARARGS,
336 "Set SPOA NULL variable"},
337 {"set_var_boolean", ps_python_set_var_boolean, METH_VARARGS,
338 "Set SPOA boolean variable"},
339 {"set_var_int32", ps_python_set_var_int32, METH_VARARGS,
340 "Set SPOA int32 variable"},
341 {"set_var_uint32", ps_python_set_var_uint32, METH_VARARGS,
342 "Set SPOA uint32 variable"},
343 {"set_var_int64", ps_python_set_var_int64, METH_VARARGS,
344 "Set SPOA int64 variable"},
345 {"set_var_uint64", ps_python_set_var_uint64, METH_VARARGS,
346 "Set SPOA uint64 variable"},
347 {"set_var_ipv4", ps_python_set_var_ipv4, METH_VARARGS,
348 "Set SPOA ipv4 variable"},
349 {"set_var_ipv6", ps_python_set_var_ipv6, METH_VARARGS,
350 "Set SPOA ipv6 variable"},
351 {"set_var_str", ps_python_set_var_str, METH_VARARGS,
352 "Set SPOA str variable"},
353 {"set_var_bin", ps_python_set_var_bin, METH_VARARGS,
354 "Set SPOA bin variable"},
355 { /* end */ }
356};
357
Gilchrist Dadaglo3e235d32020-05-06 12:25:31 +0000358#if IS_PYTHON_3K
359static struct PyModuleDef spoa_module_definition = {
360 PyModuleDef_HEAD_INIT, /* m_base */
361 "spoa", /* m_name */
362 "HAProxy SPOA module for python", /* m_doc */
363 -1, /* m_size */
364 spoa_methods, /* m_methods */
365 NULL, /* m_slots */
366 NULL, /* m_traverse */
367 NULL, /* m_clear */
368 NULL /* m_free */
369};
370
371static PyObject *PyInit_spoa_module(void)
372{
373 return module_spoa;
374}
375#endif /* IS_PYTHON_3K */
376
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100377static int ps_python_start_worker(struct worker *w)
378{
379 PyObject *m;
380 PyObject *module_name;
381 PyObject *value;
382 int ret;
383
Gilchrist Dadaglo3e235d32020-05-06 12:25:31 +0000384#if IS_PYTHON_27
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100385 Py_SetProgramName("spoa-server");
Gilchrist Dadaglo3e235d32020-05-06 12:25:31 +0000386#endif /* IS_PYTHON_27 */
387#if IS_PYTHON_3K
388 Py_SetProgramName(Py_DecodeLocale("spoa-server", NULL));
389 PyImport_AppendInittab("spoa", &PyInit_spoa_module);
390#endif /* IS_PYTHON_3K */
391
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100392 Py_Initialize();
393
Gilchrist Dadaglo3e235d32020-05-06 12:25:31 +0000394 module_name = PY_STRING_FROM_STRING("ipaddress");
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100395 if (module_name == NULL) {
396 PyErr_Print();
397 return 0;
398 }
399
400 module_ipaddress = PyImport_Import(module_name);
401 Py_DECREF(module_name);
402 if (module_ipaddress == NULL) {
403 PyErr_Print();
404 return 0;
405 }
406
407 ipv4_address = PyObject_GetAttrString(module_ipaddress, "IPv4Address");
408 if (ipv4_address == NULL) {
409 PyErr_Print();
410 return 0;
411 }
412
413 ipv6_address = PyObject_GetAttrString(module_ipaddress, "IPv6Address");
414 if (ipv4_address == NULL) {
415 PyErr_Print();
416 return 0;
417 }
418
Gilchrist Dadaglo3e235d32020-05-06 12:25:31 +0000419 PY_INIT_MODULE(m, "spoa", spoa_methods, &spoa_module_definition);
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100420 if (m == NULL) {
421 PyErr_Print();
422 return 0;
423 }
424
425 spoa_error = PyErr_NewException("spoa.error", NULL, NULL);
426 Py_INCREF(spoa_error);
427 PyModule_AddObject(m, "error", spoa_error);
428
429
430 value = PyLong_FromLong(SPOE_SCOPE_PROC);
431 if (value == NULL) {
432 PyErr_Print();
433 return 0;
434 }
435
436 ret = PyModule_AddObject(m, "scope_proc", value);
437 if (ret == -1) {
438 PyErr_Print();
439 return 0;
440 }
441
442 value = PyLong_FromLong(SPOE_SCOPE_SESS);
443 if (value == NULL) {
444 PyErr_Print();
445 return 0;
446 }
447
448 ret = PyModule_AddObject(m, "scope_sess", value);
449 if (ret == -1) {
450 PyErr_Print();
451 return 0;
452 }
453
454 value = PyLong_FromLong(SPOE_SCOPE_TXN);
455 if (value == NULL) {
456 PyErr_Print();
457 return 0;
458 }
459
460 ret = PyModule_AddObject(m, "scope_txn", value);
461 if (ret == -1) {
462 PyErr_Print();
463 return 0;
464 }
465
466 value = PyLong_FromLong(SPOE_SCOPE_REQ);
467 if (value == NULL) {
468 PyErr_Print();
469 return 0;
470 }
471
472 ret = PyModule_AddObject(m, "scope_req", value);
473 if (ret == -1) {
474 PyErr_Print();
475 return 0;
476 }
477
478 value = PyLong_FromLong(SPOE_SCOPE_RES);
479 if (value == NULL) {
480 PyErr_Print();
481 return 0;
482 }
483
484 ret = PyModule_AddObject(m, "scope_res", value);
485 if (ret == -1) {
486 PyErr_Print();
487 return 0;
488 }
489
490 empty_array = PyDict_New();
491 if (empty_array == NULL) {
492 PyErr_Print();
493 return 0;
494 }
495
Gilchrist Dadaglo3e235d32020-05-06 12:25:31 +0000496#if IS_PYTHON_3K
497 module_spoa = m;
498#endif /* IS_PYTHON_3K */
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100499 worker = w;
500 return 1;
501}
502
503static int ps_python_load_file(struct worker *w, const char *file)
504{
505 FILE *fp;
506 int ret;
507
508 fp = fopen(file, "r");
509 if (fp == NULL) {
510 LOG("python: Cannot read file \"%s\": %s", file, strerror(errno));
511 return 0;
512 }
513
514 ret = PyRun_SimpleFile(fp, file);
515 fclose(fp);
516 if (ret != 0) {
517 PyErr_Print();
518 return 0;
519 }
520
521 return 1;
522}
523
524static int ps_python_exec_message(struct worker *w, void *ref, int nargs, struct spoe_kv *args)
525{
526 int i;
527 PyObject *python_ref = ref;
528 PyObject *fkw;
529 PyObject *kw_args;
530 PyObject *result;
531 PyObject *ent;
532 PyObject *key;
533 PyObject *value;
534 PyObject *func;
535 int ret;
536 char ipbuf[64];
537 const char *p;
538 PyObject *ip_dict;
539 PyObject *ip_name;
540 PyObject *ip_value;
541
542 /* Dict containing arguments */
543
544 kw_args = PyList_New(0);
545 if (kw_args == NULL) {
546 PyErr_Print();
547 return 0;
548 }
549
550 for (i = 0; i < nargs; i++) {
551
552 /* New dict containing one argument */
553
554 ent = PyDict_New();
555 if (ent == NULL) {
556 Py_DECREF(kw_args);
557 Py_DECREF(ent);
558 PyErr_Print();
559 return 0;
560 }
561
562 /* Create the name entry */
563
Gilchrist Dadaglo3e235d32020-05-06 12:25:31 +0000564 key = PY_STRING_FROM_STRING("name");
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100565 if (key == NULL) {
566 Py_DECREF(kw_args);
567 PyErr_Print();
568 return 0;
569 }
570
Gilchrist Dadaglo3e235d32020-05-06 12:25:31 +0000571 value = PY_STRING_FROM_STRING_AND_SIZE(args[i].name.str, args[i].name.len);
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100572 if (value == NULL) {
573 Py_DECREF(kw_args);
574 Py_DECREF(ent);
575 Py_DECREF(key);
576 PyErr_Print();
577 return 0;
578 }
579
580 ret = PyDict_SetItem(ent, key, value);
581 Py_DECREF(key);
582 Py_DECREF(value);
583 if (ret == -1) {
584 Py_DECREF(kw_args);
585 Py_DECREF(ent);
586 PyErr_Print();
587 return 0;
588 }
589
590 /* Create th value entry */
591
Gilchrist Dadaglo3e235d32020-05-06 12:25:31 +0000592 key = PY_STRING_FROM_STRING("value");
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100593 if (key == NULL) {
594 Py_DECREF(kw_args);
595 Py_DECREF(ent);
596 PyErr_Print();
597 return 0;
598 }
599
600 switch (args[i].value.type) {
601 case SPOE_DATA_T_NULL:
602 value = Py_None;
603 break;
604 case SPOE_DATA_T_BOOL:
605 value = PyBool_FromLong(args[i].value.u.boolean);
606 break;
607 case SPOE_DATA_T_INT32:
608 value = PyLong_FromLong(args[i].value.u.sint32);
609 break;
610 case SPOE_DATA_T_UINT32:
611 value = PyLong_FromLong(args[i].value.u.uint32);
612 break;
613 case SPOE_DATA_T_INT64:
614 value = PyLong_FromLong(args[i].value.u.sint64);
615 break;
616 case SPOE_DATA_T_UINT64:
617 value = PyLong_FromUnsignedLong(args[i].value.u.uint64);
618 break;
619 case SPOE_DATA_T_IPV4:
620 case SPOE_DATA_T_IPV6:
621 if (args[i].value.type == SPOE_DATA_T_IPV4)
622 p = inet_ntop(AF_INET, &args[i].value.u.ipv4, ipbuf, 64);
623 else
624 p = inet_ntop(AF_INET6, &args[i].value.u.ipv6, ipbuf, 64);
625 if (!p)
626 strcpy(ipbuf, "0.0.0.0");
627
628 func = PyObject_GetAttrString(module_ipaddress, "ip_address");
629 if (func == NULL) {
630 Py_DECREF(kw_args);
631 Py_DECREF(ent);
632 PyErr_Print();
633 return 0;
634 }
635 ip_dict = PyDict_New();
636 if (ip_dict == NULL) {
637 Py_DECREF(kw_args);
638 Py_DECREF(ent);
639 Py_DECREF(func);
640 PyErr_Print();
641 return 0;
642 }
Gilchrist Dadaglo3e235d32020-05-06 12:25:31 +0000643 ip_name = PY_STRING_FROM_STRING("address");
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100644 if (ip_name == NULL) {
645 Py_DECREF(kw_args);
646 Py_DECREF(ent);
647 Py_DECREF(func);
648 Py_DECREF(ip_dict);
649 PyErr_Print();
650 return 0;
651 }
652 ip_value = PyUnicode_FromString(ipbuf);
653 if (ip_value == NULL) {
654 Py_DECREF(kw_args);
655 Py_DECREF(ent);
656 Py_DECREF(func);
657 Py_DECREF(ip_dict);
658 Py_DECREF(ip_name);
659 PyErr_Print();
660 return 0;
661 }
662 ret = PyDict_SetItem(ip_dict, ip_name, ip_value);
663 Py_DECREF(ip_name);
664 Py_DECREF(ip_value);
665 if (ret == -1) {
666 Py_DECREF(ip_dict);
667 PyErr_Print();
668 return 0;
669 }
670 value = PyObject_Call(func, empty_array, ip_dict);
671 Py_DECREF(func);
672 Py_DECREF(ip_dict);
673 break;
674
675 case SPOE_DATA_T_STR:
Gilchrist Dadaglo3e235d32020-05-06 12:25:31 +0000676 value = PY_STRING_FROM_STRING_AND_SIZE(args[i].value.u.buffer.str, args[i].value.u.buffer.len);
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100677 break;
678 case SPOE_DATA_T_BIN:
Gilchrist Dadaglo3e235d32020-05-06 12:25:31 +0000679 value = PY_BYTES_FROM_STRING_AND_SIZE(args[i].value.u.buffer.str, args[i].value.u.buffer.len);
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100680 break;
681 default:
682 value = Py_None;
683 break;
684 }
685 if (value == NULL) {
686 Py_DECREF(kw_args);
687 Py_DECREF(ent);
688 Py_DECREF(key);
689 PyErr_Print();
690 return 0;
691 }
692
693 ret = PyDict_SetItem(ent, key, value);
694 Py_DECREF(key);
695 Py_DECREF(value);
696 if (ret == -1) {
697 Py_DECREF(kw_args);
698 Py_DECREF(ent);
699 PyErr_Print();
700 return 0;
701 }
702
703 /* Add dict to the list */
704
705 ret = PyList_Append(kw_args, ent);
706 Py_DECREF(ent);
707 if (ret == -1) {
708 Py_DECREF(kw_args);
709 PyErr_Print();
710 return 0;
711 }
712 }
713
Ilya Shipitsince7b00f2020-03-23 22:28:40 +0500714 /* Dictionary { args = <list-of-args> } for the function */
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100715
716 fkw = PyDict_New();
717 if (fkw == NULL) {
718 Py_DECREF(kw_args);
719 PyErr_Print();
720 return 0;
721 }
722
Gilchrist Dadaglo3e235d32020-05-06 12:25:31 +0000723 key = PY_STRING_FROM_STRING("args");
Thierry FOURNIER00a02252018-02-25 20:59:57 +0100724 if (key == NULL) {
725 Py_DECREF(kw_args);
726 Py_DECREF(fkw);
727 PyErr_Print();
728 return 0;
729 }
730
731 ret = PyDict_SetItem(fkw, key, kw_args);
732 Py_DECREF(kw_args);
733 Py_DECREF(key);
734 if (ret == -1) {
735 Py_DECREF(fkw);
736 PyErr_Print();
737 return 0;
738 }
739
740 result = PyObject_Call(python_ref, empty_array, fkw);
741 if (result == NULL) {
742 PyErr_Print();
743 return 0;
744 }
745
746 return 1;
747}
748
749__attribute__((constructor))
750static void __ps_python_init(void)
751{
752 ps_register(&ps_python_bindings);
753}