Skip to content

Commit

Permalink
Merge pull request #33 from openziti/correct-getaddrinfo
Browse files Browse the repository at this point in the history
make ziti_getaddrinfo() conform to standard getaddrinfo
  • Loading branch information
ekoby authored Oct 18, 2022
2 parents 183ba2b + 9430658 commit 9290e16
Show file tree
Hide file tree
Showing 4 changed files with 137 additions and 7 deletions.
2 changes: 1 addition & 1 deletion setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,4 @@ tag_prefix = v
parentdir_prefix = openziti-

[openziti]
ziti_sdk_version = 0.30.0
ziti_sdk_version = 0.30.1
116 changes: 116 additions & 0 deletions src/openziti/zitilib.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import ctypes
import os
import platform
import socket
from typing import Tuple

_mod_path = os.path.dirname(__file__)
Expand All @@ -38,6 +39,78 @@ class _Ver(ctypes.Structure):
def __repr__(self):
return f'({self.version}, {self.revision})'

class SockAddrIn(ctypes.Structure):
"""
maps struct sockaddr_in
NOTE: on Linux/Win32 the first two bytes are address family as short
on Darwin the first two bytes
"""
_fields_ = [
('_family', ctypes.c_uint8 * 2),
('_port', ctypes.c_int16),
('_addr', ctypes.c_uint8 * 4)
]

def ip(self):
return '.'.join(str(o) for o in self._addr)

def af(self):
fam = self._family[0]
if osname == 'darwin':
fam = self._family[1]
return socket.AddressFamily(fam)

@property
def port(self):
return socket.ntohs(self._port)
def __repr__(self):
return f'{self.af().name}:{self.ip()}:{self.port}'


class _AddrInfo(ctypes.Structure):
"""
int ai_flags; /* Input flags. */
int ai_family; /* Protocol family for socket. */
int ai_socktype; /* Socket type. */
int ai_protocol; /* Protocol for socket. */
socklen_t ai_addrlen; /* Length of socket address. */
struct sockaddr *ai_addr; /* Socket address for socket. */
char *ai_canonname; /* Canonical name for service location. */
struct addrinfo *ai_next; /* Pointer to next in list. */
NOTE: the order of ai_addr and ai_canonname is switched on Dorwin and Windows compared to Linux
"""
_fields_ = [
('ai_flags', ctypes.c_int),
('ai_family', ctypes.c_int),
('ai_socktype', ctypes.c_int),
('ai_protocol', ctypes.c_int),
('ai_addrlen', ctypes.c_int32),
('ai_p1', ctypes.c_void_p),
('ai_p2', ctypes.c_void_p),
('ai_next', ctypes.c_void_p)
]

def get_addr(self):
addr_p = self.ai_p2
if osname == 'linux':
addr_p = self.ai_p1
if addr_p is None:
return None
return SockAddrIn.from_address(addr_p)

def get_canonname(self):
p = self.ai_p1
if osname == 'linux':
p = self.ai_p2
if p is None:
return None
return str(ctypes.cast(p, ctypes.c_char_p))

def __repr__(self):
return '|'.join((x[0] + '=' + str(getattr(self,x[0]))) for x in _AddrInfo._fields_)


_ziti_version = ziti.ziti_get_version
_ziti_version.restype = ctypes.POINTER(_Ver)
Expand Down Expand Up @@ -90,6 +163,14 @@ def __repr__(self):
]
_ziti_enroll.restype = ctypes.c_int

_ziti_resolve = ziti.Ziti_resolve
_ziti_resolve.argtypes = [
ctypes.c_char_p,
ctypes.c_char_p,
ctypes.POINTER(_AddrInfo),
ctypes.c_void_p
]

def free_win32(arg):
pass

Expand Down Expand Up @@ -190,3 +271,38 @@ def enroll(jwt, key=None, cert=None):
return id_json.value.decode()
finally:
_free(id_json)


def getaddrinfo(host, port, family=0, type=0, proto=0, flags=0):
if not isinstance(host, bytes):
host = bytes(str(host), 'utf-8')
if not isinstance(port, bytes):
port = bytes(str(port), 'utf-8')

hints = _AddrInfo(ai_family=family, ai_socktype=type, ai_protocol=proto, ai_flags=flags)
addr_p = ctypes.c_void_p()
rc = _ziti_resolve(host, port, hints, ctypes.byref(addr_p))

if rc != 0:
return None

addr_p = addr_p.value
result = []
while addr_p:
addr = _AddrInfo.from_address(addr_p)
addr_in = addr.get_addr()
try:
af = socket.AddressFamily(addr.ai_family)
except:
af = addr.ai_family
try:
t = socket.SocketKind(addr.ai_socktype)
except:
t = addr.ai_socktype

a = (af, t, addr.ai_protocol, addr.get_canonname(), (addr_in.ip(), addr_in.port))
result.append(a)
addr_p = addr.ai_next


return result
11 changes: 5 additions & 6 deletions src/openziti/zitisock.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
import socket
from socket import getaddrinfo as PyGetaddrinfo
from socket import socket as PySocket
from typing import Tuple

Expand Down Expand Up @@ -97,10 +98,8 @@ def create_ziti_connection(address, **_):
sock.connect(address)
return sock


def ziti_getaddrinfo(host, port, family=0, type=0, proto=0, flags=0):
# pylint: disable=unused-argument,redefined-builtin
# pylint: disable=protected-access,no-member
return [(socket._intenum_converter(socket.AF_INET, socket.AddressFamily),
socket._intenum_converter(type, socket.SocketKind),
proto, '', (host, port))]
addrs = zitilib.getaddrinfo(host, port, family, type, proto, flags)
if addrs is None:
addrs = PyGetaddrinfo(host, port, family, type, proto, flags)
return addrs
15 changes: 15 additions & 0 deletions tests/ziti_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,18 @@ def test_monkeypatch(self):
with self.assertRaises(ConnectionError):
get_httpbin('http://httpbin.ziti/json')

def test_resolve(selfs):
with openziti.monkeypatch():
import socket
addrlist = socket.getaddrinfo(host='httpbin.ziti', port=80, type=socket.SOCK_STREAM)
assert len(addrlist) == 1
af, socktype, proto, name, addr = addrlist[0]
assert af == socket.AF_INET
assert socktype == socket.SOCK_STREAM
assert proto == socket.IPPROTO_TCP
assert isinstance(addr, tuple)
assert isinstance(addr[0], str)
assert isinstance(addr[1], int)
assert addr[1] == 80
assert addr[0].startswith('100.64.0.')

0 comments on commit 9290e16

Please sign in to comment.