Update Python bindings to 1.4.0 API (WIP)
This commit is contained in:
@@ -1,218 +0,0 @@
|
||||
/*
|
||||
* Copyright (c)2013-2021 ZeroTier, Inc.
|
||||
*
|
||||
* Use of this software is governed by the Business Source License included
|
||||
* in the LICENSE.TXT file in the project's root directory.
|
||||
*
|
||||
* Change Date: 2025-01-01
|
||||
*
|
||||
* On the date above, in accordance with the Business Source License, use
|
||||
* of this software will be governed by version 2.0 of the Apache License.
|
||||
*/
|
||||
/****/
|
||||
|
||||
/**
|
||||
* @file
|
||||
*
|
||||
* ZeroTier Socket API (Python)
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "lwip/sockets.h"
|
||||
#include "lwip/inet.h"
|
||||
|
||||
#include "ZeroTierSockets.h"
|
||||
|
||||
#ifdef ZTS_ENABLE_PYTHON
|
||||
|
||||
int zts_py_setblocking(int fd, int block)
|
||||
{
|
||||
int new_flags, cur_flags, err = 0;
|
||||
|
||||
Py_BEGIN_ALLOW_THREADS
|
||||
cur_flags = zts_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_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_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)
|
||||
{
|
||||
if (family == AF_INET) {
|
||||
struct zts_sockaddr_in* addr;
|
||||
char *host_str;
|
||||
int result, port;
|
||||
if (!PyTuple_Check(addr_obj)) {
|
||||
return ZTS_ERR_ARG;
|
||||
}
|
||||
if (!PyArg_ParseTuple(addr_obj,
|
||||
"eti:zts_py_tuple_to_sockaddr", "idna", &host_str, &port)) {
|
||||
return ZTS_ERR_ARG;
|
||||
}
|
||||
addr = (struct zts_sockaddr_in*)dst_addr;
|
||||
zts_inet_pton(ZTS_AF_INET, host_str, &(addr->sin_addr.s_addr));
|
||||
PyMem_Free(host_str);
|
||||
if (port < 0 || port > 0xFFFF) {
|
||||
return ZTS_ERR_ARG;
|
||||
}
|
||||
if (result < 0) {
|
||||
return ZTS_ERR_ARG;
|
||||
}
|
||||
addr->sin_family = AF_INET;
|
||||
addr->sin_port = lwip_htons((short)port);
|
||||
*addrlen = sizeof *addr;
|
||||
return ZTS_ERR_OK;
|
||||
}
|
||||
if (family == AF_INET6) {
|
||||
// TODO
|
||||
}
|
||||
return ZTS_ERR_ARG;
|
||||
}
|
||||
|
||||
PyObject * zts_py_accept(int fd)
|
||||
{
|
||||
struct zts_sockaddr_in addrbuf;
|
||||
socklen_t addrlen = sizeof(addrbuf);
|
||||
memset(&addrbuf, 0, addrlen);
|
||||
int err = zts_accept(fd, (struct zts_sockaddr*)&addrbuf, &addrlen);
|
||||
char ipstr[ZTS_INET_ADDRSTRLEN];
|
||||
memset(ipstr, 0, sizeof(ipstr));
|
||||
zts_inet_ntop(ZTS_AF_INET, &(addrbuf.sin_addr), ipstr, ZTS_INET_ADDRSTRLEN);
|
||||
PyObject *t;
|
||||
t = PyTuple_New(3);
|
||||
PyTuple_SetItem(t, 0, PyLong_FromLong(err)); // New file descriptor
|
||||
PyTuple_SetItem(t, 1, PyUnicode_FromString(ipstr));
|
||||
PyTuple_SetItem(t, 2, PyLong_FromLong(lwip_ntohs(addrbuf.sin_port)));
|
||||
Py_INCREF(t);
|
||||
return t;
|
||||
}
|
||||
|
||||
int zts_py_listen(int fd, int backlog)
|
||||
{
|
||||
if (backlog < 0) {
|
||||
backlog = 128;
|
||||
}
|
||||
return zts_listen(fd, backlog);
|
||||
}
|
||||
|
||||
int zts_py_bind(int fd, int family, int type, PyObject *addr_obj)
|
||||
{
|
||||
struct zts_sockaddr_storage addrbuf;
|
||||
int addrlen;
|
||||
int err;
|
||||
if (zts_py_tuple_to_sockaddr(family, addr_obj,
|
||||
(struct zts_sockaddr *)&addrbuf, &addrlen) != ZTS_ERR_OK)
|
||||
{
|
||||
return ZTS_ERR_ARG;
|
||||
}
|
||||
Py_BEGIN_ALLOW_THREADS
|
||||
err = zts_bind(fd, (struct zts_sockaddr *)&addrbuf, addrlen);
|
||||
Py_END_ALLOW_THREADS
|
||||
return err;
|
||||
}
|
||||
|
||||
int zts_py_connect(int fd, int family, int type, PyObject *addr_obj)
|
||||
{
|
||||
struct zts_sockaddr_storage addrbuf;
|
||||
int addrlen;
|
||||
int err;
|
||||
if (zts_py_tuple_to_sockaddr(family, addr_obj,
|
||||
(struct zts_sockaddr *)&addrbuf, &addrlen) != ZTS_ERR_OK)
|
||||
{
|
||||
return ZTS_ERR_ARG;
|
||||
}
|
||||
Py_BEGIN_ALLOW_THREADS
|
||||
err = zts_connect(fd, (struct zts_sockaddr *)&addrbuf, addrlen);
|
||||
Py_END_ALLOW_THREADS
|
||||
return err;
|
||||
}
|
||||
|
||||
PyObject * zts_py_recv(int fd, int len, int flags)
|
||||
{
|
||||
PyObject *t, *buf;
|
||||
int bytes_read;
|
||||
|
||||
buf = PyBytes_FromStringAndSize((char *) 0, len);
|
||||
if (buf == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bytes_read = zts_recv(fd, PyBytes_AS_STRING(buf), len, flags);
|
||||
t = PyTuple_New(2);
|
||||
PyTuple_SetItem(t, 0, PyLong_FromLong(bytes_read));
|
||||
|
||||
if (bytes_read < 0) {
|
||||
Py_DECREF(buf);
|
||||
Py_INCREF(Py_None);
|
||||
PyTuple_SetItem(t, 1, Py_None);
|
||||
Py_INCREF(t);
|
||||
return t;
|
||||
}
|
||||
|
||||
if (bytes_read != len) {
|
||||
_PyBytes_Resize(&buf, bytes_read);
|
||||
}
|
||||
|
||||
PyTuple_SetItem(t, 1, buf);
|
||||
Py_INCREF(t);
|
||||
return t;
|
||||
}
|
||||
|
||||
int zts_py_send(int fd, PyObject *buf, int flags)
|
||||
{
|
||||
Py_buffer output;
|
||||
int bytes_sent;
|
||||
|
||||
if (PyObject_GetBuffer(buf, &output, PyBUF_SIMPLE) != 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
bytes_sent = zts_send(fd, output.buf, output.len, flags);
|
||||
PyBuffer_Release(&output);
|
||||
|
||||
return bytes_sent;
|
||||
}
|
||||
|
||||
int zts_py_close(int fd)
|
||||
{
|
||||
int err;
|
||||
Py_BEGIN_ALLOW_THREADS
|
||||
err = zts_close(fd);
|
||||
Py_END_ALLOW_THREADS
|
||||
return err;
|
||||
}
|
||||
|
||||
#endif // ZTS_ENABLE_PYTHON
|
||||
203
src/bindings/python/PythonSockets.cxx
Normal file
203
src/bindings/python/PythonSockets.cxx
Normal file
@@ -0,0 +1,203 @@
|
||||
/*
|
||||
* Copyright (c)2013-2021 ZeroTier, Inc.
|
||||
*
|
||||
* Use of this software is governed by the Business Source License included
|
||||
* in the LICENSE.TXT file in the project's root directory.
|
||||
*
|
||||
* Change Date: 2026-01-01
|
||||
*
|
||||
* On the date above, in accordance with the Business Source License, use
|
||||
* of this software will be governed by version 2.0 of the Apache License.
|
||||
*/
|
||||
/****/
|
||||
|
||||
/**
|
||||
* @file
|
||||
*
|
||||
* ZeroTier Socket API (Python)
|
||||
*/
|
||||
|
||||
#include "ZeroTierSockets.h"
|
||||
#include "lwip/inet.h"
|
||||
#include "lwip/sockets.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#ifdef ZTS_ENABLE_PYTHON
|
||||
|
||||
int zts_py_setblocking(int fd, int block)
|
||||
{
|
||||
int new_flags, cur_flags, err = 0;
|
||||
|
||||
Py_BEGIN_ALLOW_THREADS cur_flags = zts_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_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_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)
|
||||
{
|
||||
if (family == AF_INET) {
|
||||
struct zts_sockaddr_in* addr;
|
||||
char* host_str;
|
||||
int result, port;
|
||||
if (! PyTuple_Check(addr_obj)) {
|
||||
return ZTS_ERR_ARG;
|
||||
}
|
||||
if (! PyArg_ParseTuple(addr_obj, "eti:zts_py_tuple_to_sockaddr", "idna", &host_str, &port)) {
|
||||
return ZTS_ERR_ARG;
|
||||
}
|
||||
addr = (struct zts_sockaddr_in*)dst_addr;
|
||||
zts_inet_pton(ZTS_AF_INET, host_str, &(addr->sin_addr.s_addr));
|
||||
PyMem_Free(host_str);
|
||||
if (port < 0 || port > 0xFFFF) {
|
||||
return ZTS_ERR_ARG;
|
||||
}
|
||||
if (result < 0) {
|
||||
return ZTS_ERR_ARG;
|
||||
}
|
||||
addr->sin_family = AF_INET;
|
||||
addr->sin_port = lwip_htons((short)port);
|
||||
*addrlen = sizeof *addr;
|
||||
return ZTS_ERR_OK;
|
||||
}
|
||||
if (family == AF_INET6) {
|
||||
// TODO
|
||||
}
|
||||
return ZTS_ERR_ARG;
|
||||
}
|
||||
|
||||
PyObject* zts_py_accept(int fd)
|
||||
{
|
||||
struct zts_sockaddr_in addrbuf = { 0 };
|
||||
socklen_t addrlen = sizeof(addrbuf);
|
||||
int err = zts_accept(fd, (struct zts_sockaddr*)&addrbuf, &addrlen);
|
||||
char ipstr[ZTS_INET_ADDRSTRLEN] = { 0 };
|
||||
zts_inet_ntop(ZTS_AF_INET, &(addrbuf.sin_addr), ipstr, ZTS_INET_ADDRSTRLEN);
|
||||
PyObject* t;
|
||||
t = PyTuple_New(3);
|
||||
PyTuple_SetItem(t, 0, PyLong_FromLong(err)); // New file descriptor
|
||||
PyTuple_SetItem(t, 1, PyUnicode_FromString(ipstr));
|
||||
PyTuple_SetItem(t, 2, PyLong_FromLong(lwip_ntohs(addrbuf.sin_port)));
|
||||
Py_INCREF(t);
|
||||
return t;
|
||||
}
|
||||
|
||||
int zts_py_listen(int fd, int backlog)
|
||||
{
|
||||
if (backlog < 0) {
|
||||
backlog = 128;
|
||||
}
|
||||
return zts_listen(fd, backlog);
|
||||
}
|
||||
|
||||
int zts_py_bind(int fd, int family, int type, PyObject* addr_obj)
|
||||
{
|
||||
struct zts_sockaddr_storage addrbuf;
|
||||
int addrlen;
|
||||
int err;
|
||||
if (zts_py_tuple_to_sockaddr(family, addr_obj, (struct zts_sockaddr*)&addrbuf, &addrlen) != ZTS_ERR_OK) {
|
||||
return ZTS_ERR_ARG;
|
||||
}
|
||||
Py_BEGIN_ALLOW_THREADS err = zts_bind(fd, (struct zts_sockaddr*)&addrbuf, addrlen);
|
||||
Py_END_ALLOW_THREADS return err;
|
||||
}
|
||||
|
||||
int zts_py_connect(int fd, int family, int type, PyObject* addr_obj)
|
||||
{
|
||||
struct zts_sockaddr_storage addrbuf;
|
||||
int addrlen;
|
||||
int err;
|
||||
if (zts_py_tuple_to_sockaddr(family, addr_obj, (struct zts_sockaddr*)&addrbuf, &addrlen) != ZTS_ERR_OK) {
|
||||
return ZTS_ERR_ARG;
|
||||
}
|
||||
Py_BEGIN_ALLOW_THREADS err = zts_connect(fd, (struct zts_sockaddr*)&addrbuf, addrlen);
|
||||
Py_END_ALLOW_THREADS return err;
|
||||
}
|
||||
|
||||
PyObject* zts_py_recv(int fd, int len, int flags)
|
||||
{
|
||||
PyObject *t, *buf;
|
||||
int bytes_read;
|
||||
|
||||
buf = PyBytes_FromStringAndSize((char*)0, len);
|
||||
if (buf == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bytes_read = zts_recv(fd, PyBytes_AS_STRING(buf), len, flags);
|
||||
t = PyTuple_New(2);
|
||||
PyTuple_SetItem(t, 0, PyLong_FromLong(bytes_read));
|
||||
|
||||
if (bytes_read < 0) {
|
||||
Py_DECREF(buf);
|
||||
Py_INCREF(Py_None);
|
||||
PyTuple_SetItem(t, 1, Py_None);
|
||||
Py_INCREF(t);
|
||||
return t;
|
||||
}
|
||||
|
||||
if (bytes_read != len) {
|
||||
_PyBytes_Resize(&buf, bytes_read);
|
||||
}
|
||||
|
||||
PyTuple_SetItem(t, 1, buf);
|
||||
Py_INCREF(t);
|
||||
return t;
|
||||
}
|
||||
|
||||
int zts_py_send(int fd, PyObject* buf, int flags)
|
||||
{
|
||||
Py_buffer output;
|
||||
int bytes_sent;
|
||||
|
||||
if (PyObject_GetBuffer(buf, &output, PyBUF_SIMPLE) != 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
bytes_sent = zts_send(fd, output.buf, output.len, flags);
|
||||
PyBuffer_Release(&output);
|
||||
|
||||
return bytes_sent;
|
||||
}
|
||||
|
||||
int zts_py_close(int fd)
|
||||
{
|
||||
int err;
|
||||
Py_BEGIN_ALLOW_THREADS err = zts_close(fd);
|
||||
Py_END_ALLOW_THREADS return err;
|
||||
}
|
||||
|
||||
#endif // ZTS_ENABLE_PYTHON
|
||||
79
src/bindings/python/node.py
Normal file
79
src/bindings/python/node.py
Normal file
@@ -0,0 +1,79 @@
|
||||
import libzt
|
||||
|
||||
_user_specified_event_handler = None
|
||||
|
||||
|
||||
class _EventCallbackClass(libzt.PythonDirectorCallbackClass):
|
||||
"""ZeroTier event callback class"""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class MyEventCallbackClass(_EventCallbackClass):
|
||||
def on_zerotier_event(self, msg):
|
||||
id = 0
|
||||
if msg.event_code == libzt.ZTS_EVENT_NODE_ONLINE:
|
||||
id = msg.node.node_id
|
||||
if msg.event_code == libzt.ZTS_EVENT_NODE_OFFLINE:
|
||||
id = msg.node.node_id
|
||||
if msg.event_code == libzt.ZTS_EVENT_NETWORK_READY_IP4:
|
||||
id = msg.network.net_id
|
||||
if msg.event_code == libzt.ZTS_EVENT_NETWORK_READY_IP6:
|
||||
id = msg.network.net_id
|
||||
if msg.event_code == libzt.ZTS_EVENT_PEER_DIRECT:
|
||||
id = msg.peer.peer_id
|
||||
if msg.event_code == libzt.ZTS_EVENT_PEER_RELAY:
|
||||
id = msg.peer.peer_id
|
||||
# Now that we've adjusted internal state, notify user
|
||||
global _user_specified_event_handler
|
||||
_user_specified_event_handler(msg.event_code, id)
|
||||
|
||||
|
||||
class ZeroTierNode:
|
||||
native_event_handler = None
|
||||
|
||||
def __init__(self):
|
||||
self.native_event_handler = MyEventCallbackClass()
|
||||
libzt.zts_init_set_event_handler(self.native_event_handler)
|
||||
|
||||
"""ZeroTier Node"""
|
||||
|
||||
def init_from_storage(self, storage_path):
|
||||
"""Initialize the node from storage (or tell it to write to that location)"""
|
||||
return libzt.zts_init_from_storage(storage_path)
|
||||
|
||||
"""Set the node's event handler"""
|
||||
|
||||
def init_set_event_handler(self, event_handler):
|
||||
global _user_specified_event_handler
|
||||
_user_specified_event_handler = event_handler
|
||||
|
||||
def init_set_port(self, port):
|
||||
"""Set the node's primary port"""
|
||||
return libzt.zts_init_set_port(port)
|
||||
|
||||
def node_start(self):
|
||||
"""Start the ZeroTier service"""
|
||||
return libzt.zts_node_start()
|
||||
|
||||
def node_stop(self):
|
||||
"""Stop the ZeroTier service"""
|
||||
return libzt.zts_node_stop()
|
||||
|
||||
def node_free(self):
|
||||
"""Permanently shut down the network stack"""
|
||||
return libzt.zts_node_free()
|
||||
|
||||
def net_join(self, net_id):
|
||||
"""Join a ZeroTier network"""
|
||||
return libzt.zts_net_join(net_id)
|
||||
|
||||
def net_leave(self, net_id):
|
||||
"""Leave a ZeroTier network"""
|
||||
return libzt.zts_net_leave(net_id)
|
||||
|
||||
def node_is_online(self):
|
||||
return libzt.zts_node_is_online()
|
||||
|
||||
def net_transport_is_ready(self, net_id):
|
||||
return libzt.zts_net_transport_is_ready(net_id)
|
||||
46
src/bindings/python/sockets.py
Normal file → Executable file
46
src/bindings/python/sockets.py
Normal file → Executable file
@@ -2,9 +2,6 @@
|
||||
|
||||
import libzt
|
||||
|
||||
class EventCallbackClass(libzt.PythonDirectorCallbackClass):
|
||||
"""ZeroTier event callback class"""
|
||||
pass
|
||||
|
||||
def handle_error(err):
|
||||
"""Convert libzt error code to exception"""
|
||||
@@ -20,6 +17,7 @@ def handle_error(err):
|
||||
if err == libzt.ZTS_ERR_GENERAL:
|
||||
raise Exception("ZTS_ERR_GENERAL (" + str(err) + ")")
|
||||
|
||||
|
||||
# This implementation of errno is NOT thread safe
|
||||
# That is, this value is shared among all lower-level socket calls
|
||||
# and may change for any reason at any time if you have multiple
|
||||
@@ -28,41 +26,10 @@ def errno():
|
||||
"""Return errno value of low-level socket layer"""
|
||||
return libzt.cvar.zts_errno
|
||||
|
||||
def start(path, callback, port):
|
||||
"""Start the ZeroTier service"""
|
||||
libzt.zts_start(path, callback, port)
|
||||
|
||||
def stop():
|
||||
"""Stop the ZeroTier service"""
|
||||
libzt.zts_stop()
|
||||
|
||||
def restart():
|
||||
"""[debug] Restarts the ZeroTier service and network stack"""
|
||||
libzt.zts_restart()
|
||||
|
||||
def free():
|
||||
"""Permenantly shuts down the network stack"""
|
||||
libzt.zts_free()
|
||||
|
||||
def join(network_id):
|
||||
"""Join a ZeroTier network"""
|
||||
libzt.zts_join(network_id)
|
||||
|
||||
def leave(network_id):
|
||||
"""Leave a ZeroTier network"""
|
||||
libzt.zts_leave(network_id)
|
||||
|
||||
def zts_orbit(moon_world_id, moon_seed):
|
||||
"""Orbit a moon"""
|
||||
return libzt.zts_orbit(moon_world_id, moon_seed)
|
||||
|
||||
def zts_deorbit(moon_world_id):
|
||||
"""De-orbit a moon"""
|
||||
return libzt.zts_deorbit(moon_world_id)
|
||||
|
||||
|
||||
class socket:
|
||||
"""Pythonic class that wraps low-level sockets"""
|
||||
|
||||
_fd = -1 # native layer file descriptor
|
||||
_family = -1
|
||||
_type = -1
|
||||
@@ -131,7 +98,9 @@ class socket:
|
||||
"""libzt does not support this (yet)"""
|
||||
raise NotImplementedError("libzt does not support this (yet?)")
|
||||
|
||||
def getaddrinfo(self, host, port, sock_family=0, sock_type=0, sock_proto=0, flags=0):
|
||||
def getaddrinfo(
|
||||
self, host, port, sock_family=0, sock_type=0, sock_proto=0, flags=0
|
||||
):
|
||||
"""libzt does not support this (yet)"""
|
||||
raise NotImplementedError("libzt does not support this (yet?)")
|
||||
|
||||
@@ -285,7 +254,8 @@ class socket:
|
||||
def detach(self):
|
||||
"""libzt does not support this"""
|
||||
raise NotImplementedError(
|
||||
"detach(): Not supported. OS File descriptors aren't used in libzt.")
|
||||
"detach(): Not supported. OS File descriptors aren't used in libzt."
|
||||
)
|
||||
|
||||
def dup(self):
|
||||
"""libzt does not support this"""
|
||||
@@ -336,7 +306,7 @@ class socket:
|
||||
if backlog is not None and backlog < 0:
|
||||
backlog = 0
|
||||
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)
|
||||
if err < 0:
|
||||
|
||||
Reference in New Issue
Block a user