2021-03-01 21:10:39 -08:00
|
|
|
/*
|
|
|
|
|
* 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)
|
|
|
|
|
*/
|
|
|
|
|
|
2021-03-16 23:30:14 -07:00
|
|
|
#include <string.h>
|
|
|
|
|
|
2021-03-01 21:10:39 -08:00
|
|
|
#include "lwip/sockets.h"
|
2021-03-15 01:59:18 -07:00
|
|
|
#include "lwip/inet.h"
|
|
|
|
|
|
2021-03-01 21:10:39 -08:00
|
|
|
#include "ZeroTierSockets.h"
|
|
|
|
|
|
|
|
|
|
#ifdef ZTS_ENABLE_PYTHON
|
|
|
|
|
|
2021-03-05 00:18:11 -08:00
|
|
|
int zts_py_setblocking(int fd, int flag)
|
|
|
|
|
{
|
|
|
|
|
int flags = ZTS_ERR_OK;
|
|
|
|
|
if ((flags = zts_fcntl(fd, F_GETFL, 0)) < 0) {
|
|
|
|
|
return ZTS_ERR_SOCKET;
|
|
|
|
|
}
|
|
|
|
|
return zts_fcntl(fd, F_SETFL, flags | ZTS_O_NONBLOCK);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int zts_py_getblocking(int fd)
|
|
|
|
|
{
|
|
|
|
|
int flags = ZTS_ERR_OK;
|
|
|
|
|
if ((flags = zts_fcntl(fd, F_GETFL, 0)) < 0) {
|
|
|
|
|
return ZTS_ERR_SOCKET;
|
|
|
|
|
}
|
|
|
|
|
return flags & ZTS_O_NONBLOCK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int zts_py_tuple_to_sockaddr(int family,
|
2021-03-01 21:10:39 -08:00
|
|
|
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,
|
2021-03-05 00:18:11 -08:00
|
|
|
"eti:zts_py_tuple_to_sockaddr", "idna", &host_str, &port)) {
|
2021-03-01 21:10:39 -08:00
|
|
|
return ZTS_ERR_ARG;
|
|
|
|
|
}
|
|
|
|
|
addr = (struct zts_sockaddr_in*)dst_addr;
|
2021-03-15 01:59:18 -07:00
|
|
|
zts_inet_pton(ZTS_AF_INET, host_str, &(addr->sin_addr.s_addr));
|
2021-03-01 21:10:39 -08:00
|
|
|
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;
|
2021-03-15 01:59:18 -07:00
|
|
|
addr->sin_port = lwip_htons((short)port);
|
2021-03-01 21:10:39 -08:00
|
|
|
*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));
|
2021-03-15 01:59:18 -07:00
|
|
|
PyTuple_SetItem(t, 2, PyLong_FromLong(lwip_ntohs(addrbuf.sin_port)));
|
2021-03-01 21:10:39 -08:00
|
|
|
Py_INCREF(t);
|
|
|
|
|
return t;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int zts_py_listen(int fd, int backlog)
|
|
|
|
|
{
|
|
|
|
|
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;
|
2021-03-05 00:18:11 -08:00
|
|
|
if (zts_py_tuple_to_sockaddr(family, addr_obj,
|
2021-03-01 21:10:39 -08:00
|
|
|
(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
|
|
|
|
|
Py_INCREF(Py_None);
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int zts_py_connect(int fd, int family, int type, PyObject *addr_obj)
|
|
|
|
|
{
|
|
|
|
|
struct zts_sockaddr_storage addrbuf;
|
|
|
|
|
int addrlen;
|
|
|
|
|
int err;
|
2021-03-05 00:18:11 -08:00
|
|
|
if (zts_py_tuple_to_sockaddr(family, addr_obj,
|
2021-03-01 21:10:39 -08:00
|
|
|
(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
|
|
|
|
|
Py_INCREF(Py_None);
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PyObject * zts_py_recv(int fd, int len, int flags)
|
|
|
|
|
{
|
Attempt to fix zts_recv for Python.
There were a few I'm attempting to fix in zts_py_recv():
Was allocating a static buffer of 4096, but taking whatever
length the user passed in. This change allocates a PyBytes
object of the size the user requests.
Was converting to a string without giving a size. Which probably
led to the UnicodeDecodeError I was seeing below, trying to
decode a character beyond the received bytes. Would also lead
to problems reading binary data that included embedded NULs.
Used the number of bytes received as the size of the returned data.
Variable "err" was being used, changed that to "bytes_read" as
that's what lwip_recv() returns, with <0 bytes read indicating
error.
Read data was being returned as a Unicode string, leading to this
response when I tried talking to my SSH server:
UnicodeDecodeError: 'utf-8' codec can't decode
byte 0xa1 in position 42: invalid start byte
My ssh banner is 40 bytes long, so I think position 42 was outside
the received buffer. Changed it to a PyBytes response as is
normal for network data. This code was cribbed from the Python
socketmodule
This was producing this error, as it was still returning the tuple:
SystemError: <built-in function zts_py_recv> returned a
result with an error set
This indicates that the UnicodeDecodeError had set an exception,
but a tuple was being returned instead of NULL.
In the case of a lwip_recv() error, the error response was not
being sent back to the wrapper code. Instead, a "return NULL;"
was done. I changed this case to return the tuple (err, None)
to the wrapper.
NOTE: this code built using "./build.sh host-python release", but
there was some sort of build problem I didn't understand which
produced a _libzt.so that I couldn't import, due to:
ImportError: dynamic module does not define module export
function (PyInit__libzt)
So I don't have a way to test these changes.
2021-03-16 15:52:55 -06:00
|
|
|
PyObject *t, *buf;
|
|
|
|
|
int bytes_read;
|
|
|
|
|
|
|
|
|
|
buf = PyBytes_FromStringAndSize((char *) 0, len);
|
2021-03-16 23:30:14 -07:00
|
|
|
if (buf == NULL) {
|
2021-03-01 21:10:39 -08:00
|
|
|
return NULL;
|
2021-03-16 23:30:14 -07:00
|
|
|
}
|
Attempt to fix zts_recv for Python.
There were a few I'm attempting to fix in zts_py_recv():
Was allocating a static buffer of 4096, but taking whatever
length the user passed in. This change allocates a PyBytes
object of the size the user requests.
Was converting to a string without giving a size. Which probably
led to the UnicodeDecodeError I was seeing below, trying to
decode a character beyond the received bytes. Would also lead
to problems reading binary data that included embedded NULs.
Used the number of bytes received as the size of the returned data.
Variable "err" was being used, changed that to "bytes_read" as
that's what lwip_recv() returns, with <0 bytes read indicating
error.
Read data was being returned as a Unicode string, leading to this
response when I tried talking to my SSH server:
UnicodeDecodeError: 'utf-8' codec can't decode
byte 0xa1 in position 42: invalid start byte
My ssh banner is 40 bytes long, so I think position 42 was outside
the received buffer. Changed it to a PyBytes response as is
normal for network data. This code was cribbed from the Python
socketmodule
This was producing this error, as it was still returning the tuple:
SystemError: <built-in function zts_py_recv> returned a
result with an error set
This indicates that the UnicodeDecodeError had set an exception,
but a tuple was being returned instead of NULL.
In the case of a lwip_recv() error, the error response was not
being sent back to the wrapper code. Instead, a "return NULL;"
was done. I changed this case to return the tuple (err, None)
to the wrapper.
NOTE: this code built using "./build.sh host-python release", but
there was some sort of build problem I didn't understand which
produced a _libzt.so that I couldn't import, due to:
ImportError: dynamic module does not define module export
function (PyInit__libzt)
So I don't have a way to test these changes.
2021-03-16 15:52:55 -06:00
|
|
|
|
|
|
|
|
bytes_read = zts_recv(fd, PyBytes_AS_STRING(buf), len, flags);
|
2021-03-01 21:10:39 -08:00
|
|
|
t = PyTuple_New(2);
|
Attempt to fix zts_recv for Python.
There were a few I'm attempting to fix in zts_py_recv():
Was allocating a static buffer of 4096, but taking whatever
length the user passed in. This change allocates a PyBytes
object of the size the user requests.
Was converting to a string without giving a size. Which probably
led to the UnicodeDecodeError I was seeing below, trying to
decode a character beyond the received bytes. Would also lead
to problems reading binary data that included embedded NULs.
Used the number of bytes received as the size of the returned data.
Variable "err" was being used, changed that to "bytes_read" as
that's what lwip_recv() returns, with <0 bytes read indicating
error.
Read data was being returned as a Unicode string, leading to this
response when I tried talking to my SSH server:
UnicodeDecodeError: 'utf-8' codec can't decode
byte 0xa1 in position 42: invalid start byte
My ssh banner is 40 bytes long, so I think position 42 was outside
the received buffer. Changed it to a PyBytes response as is
normal for network data. This code was cribbed from the Python
socketmodule
This was producing this error, as it was still returning the tuple:
SystemError: <built-in function zts_py_recv> returned a
result with an error set
This indicates that the UnicodeDecodeError had set an exception,
but a tuple was being returned instead of NULL.
In the case of a lwip_recv() error, the error response was not
being sent back to the wrapper code. Instead, a "return NULL;"
was done. I changed this case to return the tuple (err, None)
to the wrapper.
NOTE: this code built using "./build.sh host-python release", but
there was some sort of build problem I didn't understand which
produced a _libzt.so that I couldn't import, due to:
ImportError: dynamic module does not define module export
function (PyInit__libzt)
So I don't have a way to test these changes.
2021-03-16 15:52:55 -06:00
|
|
|
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);
|
2021-03-01 21:10:39 -08:00
|
|
|
Py_INCREF(t);
|
|
|
|
|
return t;
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-16 23:30:14 -07:00
|
|
|
int zts_py_send(int fd, PyObject *buf, int flags)
|
2021-03-01 21:10:39 -08:00
|
|
|
{
|
2021-03-16 23:30:14 -07:00
|
|
|
int bytes_sent = ZTS_ERR_OK;
|
|
|
|
|
char *bytes = NULL;
|
|
|
|
|
PyObject *encodedStr = NULL;
|
|
|
|
|
|
|
|
|
|
// Check for various encodings, or lack thereof
|
|
|
|
|
|
|
|
|
|
if (PyByteArray_Check(buf)) {
|
|
|
|
|
bytes = PyByteArray_AsString(buf);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (PyUnicode_Check(buf)) {
|
|
|
|
|
encodedStr = PyUnicode_AsEncodedString(buf, "UTF-8", "strict");
|
|
|
|
|
if (!encodedStr) {
|
|
|
|
|
return ZTS_ERR_ARG;
|
|
|
|
|
}
|
|
|
|
|
bytes = PyBytes_AsString(encodedStr);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!bytes) {
|
|
|
|
|
// No encoding detected
|
|
|
|
|
bytes = PyBytes_AsString(buf);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If we still don't have a valid pointer to a C-string, fail
|
|
|
|
|
if (!bytes) {
|
|
|
|
|
bytes_sent = ZTS_ERR_ARG;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
bytes_sent = zts_send(fd, bytes, strlen(bytes), flags);
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-01 21:10:39 -08:00
|
|
|
if (encodedStr) {
|
2021-03-16 23:30:14 -07:00
|
|
|
Py_DECREF(encodedStr);
|
2021-03-01 21:10:39 -08:00
|
|
|
}
|
2021-03-16 23:30:14 -07:00
|
|
|
return bytes_sent;
|
2021-03-01 21:10:39 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int zts_py_close(int fd)
|
|
|
|
|
{
|
|
|
|
|
return zts_close(fd);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endif // ZTS_ENABLE_PYTHON
|