Add select, getsockopt, setsockopt, ioctl and fcntl to Python wrapper
This commit is contained in:
@@ -1,4 +1,5 @@
|
|||||||
from .libzt import *
|
from .libzt import *
|
||||||
from .sockets import *
|
from .sockets import *
|
||||||
from .node import *
|
from .node import *
|
||||||
|
from .select import *
|
||||||
from .version import __version__
|
from .version import __version__
|
||||||
|
|||||||
@@ -15,56 +15,35 @@
|
|||||||
* @file
|
* @file
|
||||||
*
|
*
|
||||||
* ZeroTier Socket API (Python)
|
* ZeroTier Socket API (Python)
|
||||||
|
*
|
||||||
|
* This code derives from the Python standard library:
|
||||||
|
*
|
||||||
|
* Lib/socket.py
|
||||||
|
* Modules/socketmodule.c
|
||||||
|
* Modules/fcntlmodule.c
|
||||||
|
* Modules/clinic/fcntlmodule.c.h
|
||||||
|
* Modules/clinic/selectmodule.c.h
|
||||||
|
*
|
||||||
|
* Copyright and license text can be found in pypi packaging directory.
|
||||||
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "ZeroTierSockets.h"
|
#include "ZeroTierSockets.h"
|
||||||
#include "lwip/inet.h"
|
|
||||||
#include "lwip/sockets.h"
|
|
||||||
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#ifdef ZTS_ENABLE_PYTHON
|
#ifdef ZTS_ENABLE_PYTHON
|
||||||
|
|
||||||
int zts_py_setblocking(int fd, int block)
|
#include "Python.h"
|
||||||
|
#include "PythonSockets.h"
|
||||||
|
#include "lwip/inet.h"
|
||||||
|
#include "lwip/sockets.h"
|
||||||
|
#include "structmember.h" // PyMemberDef
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
|
||||||
|
PyObject* set_error(void)
|
||||||
{
|
{
|
||||||
int new_flags, cur_flags, err = 0;
|
return NULL; // PyErr_SetFromErrno(zts_errno);
|
||||||
|
|
||||||
Py_BEGIN_ALLOW_THREADS cur_flags = zts_bsd_fcntl(fd, F_GETFL, 0);
|
|
||||||
|
|
||||||
if (cur_flags < 0) {
|
|
||||||
err = ZTS_ERR_SOCKET;
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (! block) {
|
|
||||||
new_flags |= ZTS_O_NONBLOCK;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
new_flags &= ~ZTS_O_NONBLOCK;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (new_flags != cur_flags) {
|
|
||||||
err = zts_bsd_fcntl(fd, F_SETFL, new_flags);
|
|
||||||
}
|
|
||||||
|
|
||||||
done:
|
|
||||||
Py_END_ALLOW_THREADS
|
|
||||||
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
int zts_py_getblocking(int fd)
|
|
||||||
{
|
|
||||||
int flags;
|
|
||||||
|
|
||||||
Py_BEGIN_ALLOW_THREADS flags = zts_bsd_fcntl(fd, F_GETFL, 0);
|
|
||||||
Py_END_ALLOW_THREADS
|
|
||||||
|
|
||||||
if (flags < 0)
|
|
||||||
{
|
|
||||||
return ZTS_ERR_SOCKET;
|
|
||||||
}
|
|
||||||
return flags & ZTS_O_NONBLOCK;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int zts_py_tuple_to_sockaddr(int family, PyObject* addr_obj, struct zts_sockaddr* dst_addr, int* addrlen)
|
static int zts_py_tuple_to_sockaddr(int family, PyObject* addr_obj, struct zts_sockaddr* dst_addr, int* addrlen)
|
||||||
@@ -115,14 +94,6 @@ PyObject* zts_py_accept(int fd)
|
|||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
|
|
||||||
int zts_py_listen(int fd, int backlog)
|
|
||||||
{
|
|
||||||
if (backlog < 0) {
|
|
||||||
backlog = 128;
|
|
||||||
}
|
|
||||||
return zts_bsd_listen(fd, backlog);
|
|
||||||
}
|
|
||||||
|
|
||||||
int zts_py_bind(int fd, int family, int type, PyObject* addr_obj)
|
int zts_py_bind(int fd, int family, int type, PyObject* addr_obj)
|
||||||
{
|
{
|
||||||
struct zts_sockaddr_storage addrbuf;
|
struct zts_sockaddr_storage addrbuf;
|
||||||
@@ -211,4 +182,469 @@ PyObject* zts_py_addr_get_str(uint64_t net_id, int family)
|
|||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* list of Python objects and their file descriptor */
|
||||||
|
typedef struct {
|
||||||
|
PyObject* obj; /* owned reference */
|
||||||
|
int fd;
|
||||||
|
int sentinel; /* -1 == sentinel */
|
||||||
|
} pylist;
|
||||||
|
|
||||||
|
void reap_obj(pylist fd2obj[ZTS_FD_SETSIZE + 1])
|
||||||
|
{
|
||||||
|
unsigned int i;
|
||||||
|
for (i = 0; i < (unsigned int)ZTS_FD_SETSIZE + 1 && fd2obj[i].sentinel >= 0; i++) {
|
||||||
|
Py_CLEAR(fd2obj[i].obj);
|
||||||
|
}
|
||||||
|
fd2obj[0].sentinel = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* returns NULL and sets the Python exception if an error occurred */
|
||||||
|
PyObject* set2list(zts_fd_set* set, pylist fd2obj[ZTS_FD_SETSIZE + 1])
|
||||||
|
{
|
||||||
|
int i, j, count = 0;
|
||||||
|
PyObject *list, *o;
|
||||||
|
int fd;
|
||||||
|
|
||||||
|
for (j = 0; fd2obj[j].sentinel >= 0; j++) {
|
||||||
|
if (ZTS_FD_ISSET(fd2obj[j].fd, set)) {
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
list = PyList_New(count);
|
||||||
|
if (! list) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
i = 0;
|
||||||
|
for (j = 0; fd2obj[j].sentinel >= 0; j++) {
|
||||||
|
fd = fd2obj[j].fd;
|
||||||
|
if (ZTS_FD_ISSET(fd, set)) {
|
||||||
|
o = fd2obj[j].obj;
|
||||||
|
fd2obj[j].obj = NULL;
|
||||||
|
/* transfer ownership */
|
||||||
|
if (PyList_SetItem(list, i, o) < 0) {
|
||||||
|
goto finally;
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
finally:
|
||||||
|
Py_DECREF(list);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* returns -1 and sets the Python exception if an error occurred, otherwise
|
||||||
|
returns a number >= 0
|
||||||
|
*/
|
||||||
|
int seq2set(PyObject* seq, zts_fd_set* set, pylist fd2obj[FD_SETSIZE + 1])
|
||||||
|
{
|
||||||
|
int max = -1;
|
||||||
|
unsigned int index = 0;
|
||||||
|
Py_ssize_t i;
|
||||||
|
PyObject* fast_seq = NULL;
|
||||||
|
PyObject* o = NULL;
|
||||||
|
|
||||||
|
fd2obj[0].obj = (PyObject*)0; /* set list to zero size */
|
||||||
|
ZTS_FD_ZERO(set);
|
||||||
|
|
||||||
|
fast_seq = PySequence_Fast(seq, "arguments 1-3 must be sequences");
|
||||||
|
if (! fast_seq) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < PySequence_Fast_GET_SIZE(fast_seq); i++) {
|
||||||
|
int v;
|
||||||
|
|
||||||
|
/* any intervening fileno() calls could decr this refcnt */
|
||||||
|
if (! (o = PySequence_Fast_GET_ITEM(fast_seq, i))) {
|
||||||
|
goto finally;
|
||||||
|
}
|
||||||
|
|
||||||
|
Py_INCREF(o);
|
||||||
|
v = PyObject_AsFileDescriptor(o);
|
||||||
|
if (v == -1) {
|
||||||
|
goto finally;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
max = 0; /* not used for Win32 */
|
||||||
|
#else /* !_MSC_VER */
|
||||||
|
if (! _PyIsSelectable_fd(v)) {
|
||||||
|
PyErr_SetString(PyExc_ValueError, "filedescriptor out of range in select()");
|
||||||
|
goto finally;
|
||||||
|
}
|
||||||
|
if (v > max) {
|
||||||
|
max = v;
|
||||||
|
}
|
||||||
|
#endif /* _MSC_VER */
|
||||||
|
ZTS_FD_SET(v, set);
|
||||||
|
|
||||||
|
/* add object and its file descriptor to the list */
|
||||||
|
if (index >= (unsigned int)FD_SETSIZE) {
|
||||||
|
PyErr_SetString(PyExc_ValueError, "too many file descriptors in select()");
|
||||||
|
goto finally;
|
||||||
|
}
|
||||||
|
fd2obj[index].obj = o;
|
||||||
|
fd2obj[index].fd = v;
|
||||||
|
fd2obj[index].sentinel = 0;
|
||||||
|
fd2obj[++index].sentinel = -1;
|
||||||
|
}
|
||||||
|
Py_DECREF(fast_seq);
|
||||||
|
return max + 1;
|
||||||
|
|
||||||
|
finally:
|
||||||
|
Py_XDECREF(o);
|
||||||
|
Py_DECREF(fast_seq);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
PyObject* zts_py_select(PyObject* module, PyObject* rlist, PyObject* wlist, PyObject* xlist, PyObject* timeout_obj)
|
||||||
|
{
|
||||||
|
pylist rfd2obj[FD_SETSIZE + 1];
|
||||||
|
pylist wfd2obj[FD_SETSIZE + 1];
|
||||||
|
pylist efd2obj[FD_SETSIZE + 1];
|
||||||
|
PyObject* ret = NULL;
|
||||||
|
zts_fd_set ifdset, ofdset, efdset;
|
||||||
|
struct timeval tv, *tvp;
|
||||||
|
int imax, omax, emax, max;
|
||||||
|
int n;
|
||||||
|
_PyTime_t timeout, deadline = 0;
|
||||||
|
|
||||||
|
if (timeout_obj == Py_None) {
|
||||||
|
tvp = (struct timeval*)NULL;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (_PyTime_FromSecondsObject(&timeout, timeout_obj, _PyTime_ROUND_TIMEOUT) < 0) {
|
||||||
|
if (PyErr_ExceptionMatches(PyExc_TypeError)) {
|
||||||
|
PyErr_SetString(PyExc_TypeError, "timeout must be a float or None");
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (_PyTime_AsTimeval(timeout, &tv, _PyTime_ROUND_TIMEOUT) == -1) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (tv.tv_sec < 0) {
|
||||||
|
PyErr_SetString(PyExc_ValueError, "timeout must be non-negative");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
tvp = &tv;
|
||||||
|
}
|
||||||
|
/* Convert iterables to zts_fd_sets, and get maximum fd number
|
||||||
|
* propagates the Python exception set in seq2set()
|
||||||
|
*/
|
||||||
|
rfd2obj[0].sentinel = -1;
|
||||||
|
wfd2obj[0].sentinel = -1;
|
||||||
|
efd2obj[0].sentinel = -1;
|
||||||
|
if ((imax = seq2set(rlist, &ifdset, rfd2obj)) < 0) {
|
||||||
|
goto finally;
|
||||||
|
}
|
||||||
|
if ((omax = seq2set(wlist, &ofdset, wfd2obj)) < 0) {
|
||||||
|
goto finally;
|
||||||
|
}
|
||||||
|
if ((emax = seq2set(xlist, &efdset, efd2obj)) < 0) {
|
||||||
|
goto finally;
|
||||||
|
}
|
||||||
|
|
||||||
|
max = imax;
|
||||||
|
if (omax > max) {
|
||||||
|
max = omax;
|
||||||
|
}
|
||||||
|
if (emax > max) {
|
||||||
|
max = emax;
|
||||||
|
}
|
||||||
|
if (tvp) {
|
||||||
|
deadline = _PyTime_GetMonotonicClock() + timeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
do {
|
||||||
|
Py_BEGIN_ALLOW_THREADS errno = 0;
|
||||||
|
// struct zts_timeval zts_tvp;
|
||||||
|
// zts_tvp.tv_sec = tvp.tv_sec;
|
||||||
|
// zts_tvp.tv_sec = tvp.tv_sec;
|
||||||
|
|
||||||
|
n = zts_bsd_select(max, &ifdset, &ofdset, &efdset, (struct zts_timeval*)tvp);
|
||||||
|
Py_END_ALLOW_THREADS
|
||||||
|
|
||||||
|
if (errno != EINTR)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* select() was interrupted by a signal */
|
||||||
|
if (PyErr_CheckSignals()) {
|
||||||
|
goto finally;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tvp) {
|
||||||
|
timeout = deadline - _PyTime_GetMonotonicClock();
|
||||||
|
if (timeout < 0) {
|
||||||
|
/* bpo-35310: lists were unmodified -- clear them explicitly */
|
||||||
|
ZTS_FD_ZERO(&ifdset);
|
||||||
|
ZTS_FD_ZERO(&ofdset);
|
||||||
|
ZTS_FD_ZERO(&efdset);
|
||||||
|
n = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
_PyTime_AsTimeval_noraise(timeout, &tv, _PyTime_ROUND_CEILING);
|
||||||
|
/* retry select() with the recomputed timeout */
|
||||||
|
}
|
||||||
|
} while (1);
|
||||||
|
|
||||||
|
#ifdef MS_WINDOWS
|
||||||
|
if (n == SOCKET_ERROR) {
|
||||||
|
PyErr_SetExcFromWindowsErr(PyExc_OSError, WSAGetLastError());
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
if (n < 0) {
|
||||||
|
PyErr_SetFromErrno(PyExc_OSError);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
else {
|
||||||
|
/* any of these three calls can raise an exception. it's more
|
||||||
|
convenient to test for this after all three calls... but
|
||||||
|
is that acceptable?
|
||||||
|
*/
|
||||||
|
rlist = set2list(&ifdset, rfd2obj);
|
||||||
|
wlist = set2list(&ofdset, wfd2obj);
|
||||||
|
xlist = set2list(&efdset, efd2obj);
|
||||||
|
if (PyErr_Occurred()) {
|
||||||
|
ret = NULL;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ret = PyTuple_Pack(3, rlist, wlist, xlist);
|
||||||
|
}
|
||||||
|
Py_XDECREF(rlist);
|
||||||
|
Py_XDECREF(wlist);
|
||||||
|
Py_XDECREF(xlist);
|
||||||
|
}
|
||||||
|
|
||||||
|
finally:
|
||||||
|
reap_obj(rfd2obj);
|
||||||
|
reap_obj(wfd2obj);
|
||||||
|
reap_obj(efd2obj);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int zts_py_setsockopt(int fd, PyObject* args)
|
||||||
|
{
|
||||||
|
int level;
|
||||||
|
int optname;
|
||||||
|
int res;
|
||||||
|
Py_buffer optval;
|
||||||
|
int flag;
|
||||||
|
unsigned int optlen;
|
||||||
|
PyObject* none;
|
||||||
|
|
||||||
|
// setsockopt(level, opt, flag)
|
||||||
|
if (PyArg_ParseTuple(args, "iii:setsockopt", &level, &optname, &flag)) {
|
||||||
|
res = zts_bsd_setsockopt(fd, level, optname, (char*)&flag, sizeof flag);
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
PyErr_Clear();
|
||||||
|
// setsockopt(level, opt, None, flag)
|
||||||
|
if (PyArg_ParseTuple(args, "iiO!I:setsockopt", &level, &optname, Py_TYPE(Py_None), &none, &optlen)) {
|
||||||
|
assert(sizeof(socklen_t) >= sizeof(unsigned int));
|
||||||
|
res = zts_bsd_setsockopt(fd, level, optname, NULL, (socklen_t)optlen);
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
PyErr_Clear();
|
||||||
|
// setsockopt(level, opt, buffer)
|
||||||
|
if (! PyArg_ParseTuple(args, "iiy*:setsockopt", &level, &optname, &optval))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
#ifdef MS_WINDOWS
|
||||||
|
if (optval.len > INT_MAX) {
|
||||||
|
PyBuffer_Release(&optval);
|
||||||
|
PyErr_Format(PyExc_OverflowError, "socket option is larger than %i bytes", INT_MAX);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
res = zts_bsd_setsockopt(fd, level, optname, optval.buf, (int)optval.len);
|
||||||
|
#else
|
||||||
|
res = zts_bsd_setsockopt(fd, level, optname, optval.buf, optval.len);
|
||||||
|
#endif
|
||||||
|
PyBuffer_Release(&optval);
|
||||||
|
|
||||||
|
done:
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
PyObject* zts_py_getsockopt(int fd, PyObject* args)
|
||||||
|
{
|
||||||
|
int level;
|
||||||
|
int optname;
|
||||||
|
int res;
|
||||||
|
PyObject* buf;
|
||||||
|
socklen_t buflen = 0;
|
||||||
|
int flag = 0;
|
||||||
|
socklen_t flagsize;
|
||||||
|
|
||||||
|
if (! PyArg_ParseTuple(args, "ii|i:getsockopt", &level, &optname, &buflen)) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (buflen == 0) {
|
||||||
|
flagsize = sizeof flag;
|
||||||
|
res = zts_bsd_getsockopt(fd, level, optname, (void*)&flag, &flagsize);
|
||||||
|
if (res < 0) {
|
||||||
|
return set_error();
|
||||||
|
}
|
||||||
|
return PyLong_FromLong(flag);
|
||||||
|
}
|
||||||
|
if (buflen <= 0 || buflen > 1024) {
|
||||||
|
PyErr_SetString(PyExc_OSError, "getsockopt buflen out of range");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
buf = PyBytes_FromStringAndSize((char*)NULL, buflen);
|
||||||
|
if (buf == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
res = zts_bsd_getsockopt(fd, level, optname, (void*)PyBytes_AS_STRING(buf), &buflen);
|
||||||
|
if (res < 0) {
|
||||||
|
Py_DECREF(buf);
|
||||||
|
return set_error();
|
||||||
|
}
|
||||||
|
_PyBytes_Resize(&buf, buflen);
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
PyObject* zts_py_fcntl(int fd, int code, PyObject* arg)
|
||||||
|
{
|
||||||
|
unsigned int int_arg = 0;
|
||||||
|
int ret;
|
||||||
|
if (PySys_Audit("fcntl.fcntl", "iiO", fd, code, arg ? arg : Py_None) < 0) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (arg != NULL) {
|
||||||
|
int parse_result;
|
||||||
|
PyErr_Clear();
|
||||||
|
parse_result = PyArg_Parse(
|
||||||
|
arg,
|
||||||
|
"I;fcntl requires a file or file descriptor,"
|
||||||
|
" an integer and optionally a third integer",
|
||||||
|
&int_arg);
|
||||||
|
if (! parse_result) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
do {
|
||||||
|
Py_BEGIN_ALLOW_THREADS ret = zts_bsd_fcntl(fd, code, (int)int_arg);
|
||||||
|
Py_END_ALLOW_THREADS
|
||||||
|
} while (ret == -1 && zts_errno == ZTS_EINTR);
|
||||||
|
if (ret < 0) {
|
||||||
|
return set_error();
|
||||||
|
}
|
||||||
|
return PyLong_FromLong((long)ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
PyObject* zts_py_ioctl(int fd, unsigned int code, PyObject* ob_arg, int mutate_arg)
|
||||||
|
{
|
||||||
|
#define IOCTL_BUFSZ 1024
|
||||||
|
int arg = 0;
|
||||||
|
int ret;
|
||||||
|
Py_buffer pstr;
|
||||||
|
char* str;
|
||||||
|
Py_ssize_t len;
|
||||||
|
char buf[IOCTL_BUFSZ + 1]; /* argument plus NUL byte */
|
||||||
|
|
||||||
|
if (PySys_Audit("fcntl.ioctl", "iIO", fd, code, ob_arg ? ob_arg : Py_None) < 0) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ob_arg != NULL) {
|
||||||
|
if (PyArg_Parse(ob_arg, "w*:ioctl", &pstr)) {
|
||||||
|
char* arg;
|
||||||
|
str = (char*)pstr.buf;
|
||||||
|
len = pstr.len;
|
||||||
|
|
||||||
|
if (mutate_arg) {
|
||||||
|
if (len <= IOCTL_BUFSZ) {
|
||||||
|
memcpy(buf, str, len);
|
||||||
|
buf[len] = '\0';
|
||||||
|
arg = buf;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
arg = str;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (len > IOCTL_BUFSZ) {
|
||||||
|
PyBuffer_Release(&pstr);
|
||||||
|
PyErr_SetString(PyExc_ValueError, "ioctl string arg too long");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
memcpy(buf, str, len);
|
||||||
|
buf[len] = '\0';
|
||||||
|
arg = buf;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (buf == arg) {
|
||||||
|
Py_BEGIN_ALLOW_THREADS /* think array.resize() */
|
||||||
|
ret = zts_bsd_ioctl(fd, code, arg);
|
||||||
|
Py_END_ALLOW_THREADS
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ret = zts_bsd_ioctl(fd, code, arg);
|
||||||
|
}
|
||||||
|
if (mutate_arg && (len <= IOCTL_BUFSZ)) {
|
||||||
|
memcpy(str, buf, len);
|
||||||
|
}
|
||||||
|
PyBuffer_Release(&pstr); /* No further access to str below this point */
|
||||||
|
if (ret < 0) {
|
||||||
|
PyErr_SetFromErrno(PyExc_OSError);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (mutate_arg) {
|
||||||
|
return PyLong_FromLong(ret);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return PyBytes_FromStringAndSize(buf, len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PyErr_Clear();
|
||||||
|
if (PyArg_Parse(ob_arg, "s*:ioctl", &pstr)) {
|
||||||
|
str = (char*)pstr.buf;
|
||||||
|
len = pstr.len;
|
||||||
|
if (len > IOCTL_BUFSZ) {
|
||||||
|
PyBuffer_Release(&pstr);
|
||||||
|
PyErr_SetString(PyExc_ValueError, "ioctl string arg too long");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
memcpy(buf, str, len);
|
||||||
|
buf[len] = '\0';
|
||||||
|
Py_BEGIN_ALLOW_THREADS ret = zts_bsd_ioctl(fd, code, buf);
|
||||||
|
Py_END_ALLOW_THREADS if (ret < 0)
|
||||||
|
{
|
||||||
|
PyBuffer_Release(&pstr);
|
||||||
|
PyErr_SetFromErrno(PyExc_OSError);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
PyBuffer_Release(&pstr);
|
||||||
|
return PyBytes_FromStringAndSize(buf, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
PyErr_Clear();
|
||||||
|
if (! PyArg_Parse(
|
||||||
|
ob_arg,
|
||||||
|
"i;ioctl requires a file or file descriptor,"
|
||||||
|
" an integer and optionally an integer or buffer argument",
|
||||||
|
&arg)) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
// Fall-through to outside the 'if' statement.
|
||||||
|
}
|
||||||
|
// TODO: Double check that &arg is correct
|
||||||
|
Py_BEGIN_ALLOW_THREADS ret = zts_bsd_ioctl(fd, code, &arg);
|
||||||
|
Py_END_ALLOW_THREADS if (ret < 0)
|
||||||
|
{
|
||||||
|
PyErr_SetFromErrno(PyExc_OSError);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return PyLong_FromLong((long)ret);
|
||||||
|
#undef IOCTL_BUFSZ
|
||||||
|
}
|
||||||
|
|
||||||
#endif // ZTS_ENABLE_PYTHON
|
#endif // ZTS_ENABLE_PYTHON
|
||||||
|
|||||||
@@ -17,26 +17,28 @@
|
|||||||
#ifdef ZTS_ENABLE_PYTHON
|
#ifdef ZTS_ENABLE_PYTHON
|
||||||
#include "Python.h"
|
#include "Python.h"
|
||||||
|
|
||||||
|
typedef int SOCKET_T;
|
||||||
|
|
||||||
int zts_py_bind(int fd, int family, int type, PyObject* addro);
|
int zts_py_bind(int fd, int family, int type, PyObject* addro);
|
||||||
|
|
||||||
int zts_py_connect(int fd, int family, int type, PyObject* addro);
|
int zts_py_connect(int fd, int family, int type, PyObject* addro);
|
||||||
|
|
||||||
PyObject* zts_py_accept(int fd);
|
PyObject* zts_py_accept(int fd);
|
||||||
|
|
||||||
int zts_py_listen(int fd, int backlog);
|
|
||||||
|
|
||||||
PyObject* zts_py_recv(int fd, int len, int flags);
|
PyObject* zts_py_recv(int fd, int len, int flags);
|
||||||
|
|
||||||
int zts_py_send(int fd, PyObject* buf, int flags);
|
int zts_py_send(int fd, PyObject* buf, int flags);
|
||||||
|
|
||||||
int zts_py_close(int fd);
|
int zts_py_close(int fd);
|
||||||
|
|
||||||
int zts_py_setblocking(int fd, int flag);
|
|
||||||
|
|
||||||
int zts_py_getblocking(int fd);
|
|
||||||
|
|
||||||
PyObject* zts_py_addr_get_str(uint64_t net_id, int family);
|
PyObject* zts_py_addr_get_str(uint64_t net_id, int family);
|
||||||
|
|
||||||
|
PyObject* zts_py_select(PyObject* module, PyObject* rlist, PyObject* wlist, PyObject* xlist, PyObject* timeout_obj);
|
||||||
|
|
||||||
|
int zts_py_setsockopt(int fd, PyObject* args);
|
||||||
|
|
||||||
|
PyObject* zts_py_getsockopt(int fd, PyObject* args);
|
||||||
|
|
||||||
#endif // ZTS_ENABLE_PYTHON
|
#endif // ZTS_ENABLE_PYTHON
|
||||||
|
|
||||||
#endif // ZTS_PYTHON_SOCKETS_H
|
#endif // ZTS_PYTHON_SOCKETS_H
|
||||||
|
|||||||
@@ -76,6 +76,9 @@ class ZeroTierNode:
|
|||||||
def node_is_online(self):
|
def node_is_online(self):
|
||||||
return libzt.zts_node_is_online()
|
return libzt.zts_node_is_online()
|
||||||
|
|
||||||
|
def node_id(self):
|
||||||
|
return libzt.zts_node_get_id()
|
||||||
|
|
||||||
def net_transport_is_ready(self, net_id):
|
def net_transport_is_ready(self, net_id):
|
||||||
return libzt.zts_net_transport_is_ready(net_id)
|
return libzt.zts_net_transport_is_ready(net_id)
|
||||||
|
|
||||||
|
|||||||
7
src/bindings/python/select.py
Normal file
7
src/bindings/python/select.py
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import libzt
|
||||||
|
|
||||||
|
|
||||||
|
class select:
|
||||||
|
def select(self, r, w, x, timeout=None):
|
||||||
|
r, w, x = libzt.zts_py_select(self, r, w, x, timeout)
|
||||||
|
return r, w + x, []
|
||||||
@@ -104,13 +104,11 @@ class socket:
|
|||||||
|
|
||||||
def fromfd(self, fd, sock_family, sock_type, sock_proto=0):
|
def fromfd(self, fd, sock_family, sock_type, sock_proto=0):
|
||||||
"""libzt does not support this (yet)"""
|
"""libzt does not support this (yet)"""
|
||||||
raise NotImplementedError(
|
raise NotImplementedError("ZeroTier does not expose OS-level sockets")
|
||||||
"fromfd(): Not supported. OS File descriptors aren't used in libzt."
|
|
||||||
)
|
|
||||||
|
|
||||||
def fromshare(self, data):
|
def fromshare(self, data):
|
||||||
"""libzt does not support this (yet)"""
|
"""ZeroTier sockets cannot be shared"""
|
||||||
raise NotImplementedError("libzt does not support this (yet?)")
|
raise NotImplementedError("ZeroTier sockets cannot be shared")
|
||||||
|
|
||||||
def getaddrinfo(
|
def getaddrinfo(
|
||||||
self, host, port, sock_family=0, sock_type=0, sock_proto=0, flags=0
|
self, host, port, sock_family=0, sock_type=0, sock_proto=0, flags=0
|
||||||
@@ -246,21 +244,21 @@ class socket:
|
|||||||
if err < 0:
|
if err < 0:
|
||||||
handle_error(err)
|
handle_error(err)
|
||||||
|
|
||||||
def connect(self, remote_address):
|
def connect(self, address):
|
||||||
"""connect(address)
|
"""connect(address)
|
||||||
|
|
||||||
Connect the socket to a remote address"""
|
Connect the socket to a remote address"""
|
||||||
err = libzt.zts_py_connect(self._fd, self._family, self._type, remote_address)
|
err = libzt.zts_py_connect(self._fd, self._family, self._type, address)
|
||||||
if err < 0:
|
if err < 0:
|
||||||
handle_error(err)
|
handle_error(err)
|
||||||
|
|
||||||
def connect_ex(self, remote_address):
|
def connect_ex(self, address):
|
||||||
"""connect_ex(address) -> errno
|
"""connect_ex(address) -> errno
|
||||||
|
|
||||||
Connect to remote host but return low-level result code, and errno on failure
|
Connect to remote host but return low-level result code, and errno on failure
|
||||||
This uses a non-thread-safe implementation of errno
|
This uses a non-thread-safe implementation of errno
|
||||||
"""
|
"""
|
||||||
err = libzt.zts_py_connect(self._fd, self._family, self._type, remote_address)
|
err = libzt.zts_py_connect(self._fd, self._family, self._type, address)
|
||||||
if err < 0:
|
if err < 0:
|
||||||
return errno()
|
return errno()
|
||||||
return err
|
return err
|
||||||
@@ -268,7 +266,7 @@ class socket:
|
|||||||
def detach(self):
|
def detach(self):
|
||||||
"""libzt does not support this"""
|
"""libzt does not support this"""
|
||||||
raise NotImplementedError(
|
raise NotImplementedError(
|
||||||
"detach(): Not supported. OS File descriptors aren't used in libzt."
|
"detach(): Not supported. ZeroTier sockets are not OS-level sockets"
|
||||||
)
|
)
|
||||||
|
|
||||||
def dup(self):
|
def dup(self):
|
||||||
@@ -276,18 +274,19 @@ class socket:
|
|||||||
raise NotImplementedError("libzt does not support this (yet?)")
|
raise NotImplementedError("libzt does not support this (yet?)")
|
||||||
|
|
||||||
def fileno(self):
|
def fileno(self):
|
||||||
"""libzt does not support this"""
|
"""Return ZeroTier socket file descriptor. This is not OS-level. Can
|
||||||
raise NotImplementedError("libzt does not support this (yet?)")
|
only be used with ZeroTier's version of select"""
|
||||||
|
return self._fd
|
||||||
|
|
||||||
def get_inheritable(self):
|
def get_inheritable(self):
|
||||||
"""libzt does not support this (yet)"""
|
"""ZeroTier sockets cannot be shared. This always returns False"""
|
||||||
raise NotImplementedError("libzt does not support this (yet?)")
|
return False
|
||||||
|
|
||||||
def getblocking(self):
|
def getblocking(self):
|
||||||
"""getblocking()
|
"""getblocking()
|
||||||
|
|
||||||
Return True if the socket is in blocking mode, False if it is non-blocking"""
|
Return True if the socket is in blocking mode, False if it is non-blocking"""
|
||||||
return libzt.zts_py_getblocking(self._fd)
|
return libzt.zts_get_blocking(self._fd)
|
||||||
|
|
||||||
def getpeername(self):
|
def getpeername(self):
|
||||||
"""libzt does not support this (yet)"""
|
"""libzt does not support this (yet)"""
|
||||||
@@ -297,17 +296,17 @@ class socket:
|
|||||||
"""libzt does not support this (yet)"""
|
"""libzt does not support this (yet)"""
|
||||||
raise NotImplementedError("libzt does not support this (yet?)")
|
raise NotImplementedError("libzt does not support this (yet?)")
|
||||||
|
|
||||||
def getsockopt(self, level, optname, buflen):
|
def getsockopt(self, level, optname, buflen=None):
|
||||||
"""libzt does not support this (yet)"""
|
"""Get a socket option value"""
|
||||||
raise NotImplementedError("libzt does not support this (yet?)")
|
return libzt.zts_py_getsockopt(self._fd, (level, optname))
|
||||||
|
|
||||||
def gettimeout(self):
|
def gettimeout(self):
|
||||||
"""libzt does not support this (yet)"""
|
"""libzt does not support this (yet)"""
|
||||||
raise NotImplementedError("libzt does not support this (yet?)")
|
raise NotImplementedError("libzt does not support this (yet?)")
|
||||||
|
|
||||||
def ioctl(self, control, option):
|
def ioctl(self, request, arg=0, mutate_flag=True):
|
||||||
"""libzt does not support this (yet)"""
|
"""Perform I/O control operations"""
|
||||||
raise NotImplementedError("libzt does not support this (yet?)")
|
return libzt.zts_py_ioctl(self._fd, request, arg, mutate_flag)
|
||||||
|
|
||||||
def listen(self, backlog=None):
|
def listen(self, backlog=None):
|
||||||
"""listen([backlog])
|
"""listen([backlog])
|
||||||
@@ -322,7 +321,7 @@ class socket:
|
|||||||
if backlog is None:
|
if backlog is None:
|
||||||
backlog = -1 # Lower-level code picks default
|
backlog = -1 # Lower-level code picks default
|
||||||
|
|
||||||
err = libzt.zts_py_listen(self._fd, backlog)
|
err = libzt.zts_bsd_listen(self._fd, backlog)
|
||||||
if err < 0:
|
if err < 0:
|
||||||
handle_error(err)
|
handle_error(err)
|
||||||
|
|
||||||
@@ -412,25 +411,25 @@ class socket:
|
|||||||
raise NotImplementedError("libzt does not support this (yet?)")
|
raise NotImplementedError("libzt does not support this (yet?)")
|
||||||
|
|
||||||
def set_inheritable(self, inheritable):
|
def set_inheritable(self, inheritable):
|
||||||
"""libzt does not support this (yet)"""
|
"""ZeroTier sockets cannot be shared"""
|
||||||
raise NotImplementedError("libzt does not support this (yet?)")
|
raise NotImplementedError("ZeroTier sockets cannot be shared")
|
||||||
|
|
||||||
def setblocking(self, flag):
|
def setblocking(self, flag):
|
||||||
"""setblocking(flag)
|
"""setblocking(flag)
|
||||||
|
|
||||||
Sets the socket to blocking mode if flag=True, non-blocking if flag=False."""
|
Sets the socket to blocking mode if flag=True, non-blocking if flag=False."""
|
||||||
libzt.zts_py_setblocking(self._fd, flag)
|
libzt.zts_set_blocking(self._fd, flag)
|
||||||
|
|
||||||
def settimeout(self, value):
|
def settimeout(self, value):
|
||||||
"""libzt does not support this (yet)"""
|
"""libzt does not support this (yet)"""
|
||||||
raise NotImplementedError("libzt does not support this (yet?)")
|
raise NotImplementedError("libzt does not support this (yet?)")
|
||||||
|
|
||||||
# TODO: value: buffer
|
|
||||||
# TODO: value: int
|
|
||||||
# TODO: value: None -> optlen required
|
|
||||||
def setsockopt(self, level, optname, value):
|
def setsockopt(self, level, optname, value):
|
||||||
"""libzt does not support this (yet)"""
|
"""Set a socket option value"""
|
||||||
raise NotImplementedError("libzt does not support this (yet?)")
|
err = libzt.zts_py_setsockopt(self._fd, (level, optname, value))
|
||||||
|
if err < 0:
|
||||||
|
handle_error(err)
|
||||||
|
return err
|
||||||
|
|
||||||
def shutdown(self, how):
|
def shutdown(self, how):
|
||||||
"""shutdown(how)
|
"""shutdown(how)
|
||||||
|
|||||||
Reference in New Issue
Block a user