Skip to content

Commit

Permalink
socket_extra: added getaddrinfo, test_getaddrinfo & SocketProto
Browse files Browse the repository at this point in the history
  • Loading branch information
YoSTEALTH committed Mar 15, 2024
1 parent 5f09764 commit 5e46798
Show file tree
Hide file tree
Showing 6 changed files with 122 additions and 9 deletions.
1 change: 1 addition & 0 deletions src/liburing/socket.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ from .queue cimport io_uring_sqe

cdef class sockaddr:
cdef:
bint free
void* ptr
socklen_t sizeof
readonly sa_family_t family
Expand Down
53 changes: 46 additions & 7 deletions src/liburing/socket.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@ cdef class sockaddr:
>>> addr = sockaddr(AF_INET, b'0.0.0.0', 12345)
>>> addr = sockaddr(AF_INET6, b'::1', 12345)
>>> bind(sockfd, addr)
Note
- `sockaddr()` is low level setup, letting you serve/connect directly using path/ip.
If you need higher level features you can use `getaddrinfo()` this lets you connect
using domain names, ...
'''
def __cinit__(self, sa_family_t family=0, char* addr=b'', in_port_t port=0,
uint32_t scope_id=0):
Expand All @@ -24,6 +29,7 @@ cdef class sockaddr:
self.ptr = <void*>sockaddr_un(addr)
if self.ptr is NULL:
memory_error(self)
self.free = True
self.sizeof = sizeof(__sockaddr_un)
self.family = family

Expand All @@ -33,6 +39,7 @@ cdef class sockaddr:
self.ptr = <void*>sockaddr_in(addr, port)
if self.ptr is NULL:
raise ValueError('`sockaddr(AF_INET)` - `addr` did not receive IPv4 address!')
self.free = True
self.sizeof = sizeof(__sockaddr_in)
self.family = family

Expand All @@ -42,21 +49,22 @@ cdef class sockaddr:
self.ptr = <void*>sockaddr_in6(addr, port, scope_id)
if self.ptr is NULL:
raise ValueError('`sockaddr(AF_INET6)` - `addr` did not receive IPv6 address!')
self.free = True
self.sizeof = sizeof(__sockaddr_in6)
self.family = family

elif not family: # incoming
raise NotImplementedError
else: # error
elif family: # error
raise NotImplementedError

def __dealloc__(self):
if self.ptr is not NULL:
if self.free and self.ptr is not NULL:
PyMem_RawFree(self.ptr)
self.ptr = NULL
self.free = False

def __repr__(self):
return f'{self.__class__.__name__}({self._test})'
# TODO: return (host, port)
# def __repr__(self):
# return f'{self.__class__.__name__}({self._test})'

@property
def _test(self)-> dict:
Expand Down Expand Up @@ -504,7 +512,7 @@ cpdef enum io_uring_socket_op:
SOCKET_URING_OP_GETSOCKOPT = __SOCKET_URING_OP_GETSOCKOPT
SOCKET_URING_OP_SETSOCKOPT = __SOCKET_URING_OP_SETSOCKOPT

# defines
# setsockopt & getsockopt start >>>
SOL_SOCKET = __SOL_SOCKET
SO_DEBUG = __SO_DEBUG
SO_REUSEADDR = __SO_REUSEADDR
Expand Down Expand Up @@ -550,3 +558,34 @@ SO_TIMESTAMPNS = __SO_TIMESTAMPNS
SO_TIMESTAMPING = __SO_TIMESTAMPING
SO_RCVTIMEO = __SO_RCVTIMEO
SO_SNDTIMEO = __SO_SNDTIMEO
# setsockopt & getsockopt end <<<

cpdef enum SocketProto:
IPPROTO_IP = __IPPROTO_IP
IPPROTO_ICMP = __IPPROTO_ICMP
IPPROTO_IGMP = __IPPROTO_IGMP
IPPROTO_IPIP = __IPPROTO_IPIP
IPPROTO_TCP = __IPPROTO_TCP
IPPROTO_EGP = __IPPROTO_EGP
IPPROTO_PUP = __IPPROTO_PUP
IPPROTO_UDP = __IPPROTO_UDP
IPPROTO_IDP = __IPPROTO_IDP
IPPROTO_TP = __IPPROTO_TP
IPPROTO_DCCP = __IPPROTO_DCCP
IPPROTO_IPV6 = __IPPROTO_IPV6
IPPROTO_RSVP = __IPPROTO_RSVP
IPPROTO_GRE = __IPPROTO_GRE
IPPROTO_ESP = __IPPROTO_ESP
IPPROTO_AH = __IPPROTO_AH
IPPROTO_MTP = __IPPROTO_MTP
IPPROTO_BEETPH = __IPPROTO_BEETPH
IPPROTO_ENCAP = __IPPROTO_ENCAP
IPPROTO_PIM = __IPPROTO_PIM
IPPROTO_COMP = __IPPROTO_COMP
IPPROTO_L2TP = __IPPROTO_L2TP
IPPROTO_SCTP = __IPPROTO_SCTP
IPPROTO_UDPLITE = __IPPROTO_UDPLITE
IPPROTO_MPLS = __IPPROTO_MPLS
IPPROTO_ETHERNET = __IPPROTO_ETHERNET
IPPROTO_RAW = __IPPROTO_RAW
IPPROTO_MPTCP = __IPPROTO_MPTCP
4 changes: 4 additions & 0 deletions src/liburing/socket_extra.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ from .socket cimport *
from .error cimport trap_error


cdef class getaddrinfo:
cdef __addrinfo* ptr


cpdef int bind(int sockfd, sockaddr addr) nogil
cpdef int listen(int sockfd, int backlog) nogil
cpdef int getpeername(int sockfd, sockaddr addr) nogil
Expand Down
52 changes: 51 additions & 1 deletion src/liburing/socket_extra.pyx
Original file line number Diff line number Diff line change
@@ -1,12 +1,63 @@
from libc.string cimport memset


cdef class getaddrinfo:
def __cinit__(self, const char* host, char* port_service,
int family=0, int type=0, int proto=0, int flags=0):
'''
Example
>>> for af_, sock_, proto, canon, addr in getaddrinfo(b'127.0.0.1', b'12345'):
... ...
... io_uring_prep_socket(sqe, af_, sock_)
... ...
... io_uring_prep_connect(sqe, sockfd, addr)
... ...
... break # if connection successful break, else try next.
'''
# TODO: `port_service` should handle both `int` & `bytes` types.
cdef:
int no
__addrinfo hints

memset(&hints, 0, sizeof(__addrinfo))
hints.ai_flags = flags
hints.ai_family = family
hints.ai_socktype = type
hints.ai_protocol = proto
if no := __getaddrinfo(host, port_service, &hints, &self.ptr):
trap_error(no, __gai_strerror(no).decode())

def __dealloc__(self):
if self.ptr is not NULL:
__freeaddrinfo(self.ptr)
self.ptr = NULL

def __iter__(self):
cdef:
__addrinfo* p = self.ptr
sockaddr addr
while p.ai_next is not NULL:
addr = sockaddr()
addr.ptr = p.ai_addr
addr.family = p.ai_family
addr.sizeof = p.ai_addrlen
yield (
p.ai_family,
p.ai_socktype,
p.ai_protocol,
p.ai_canonname or b'',
addr
)
p = p.ai_next


cpdef int bind(int sockfd, sockaddr addr) nogil:
return trap_error(__bind(sockfd, <__sockaddr*>addr.ptr, addr.sizeof))

cpdef int listen(int sockfd, int backlog) nogil:
return trap_error(__listen(sockfd, backlog))

# TODO
cpdef int getpeername(int sockfd, sockaddr addr) nogil:
''' TODO '''
return trap_error(__getpeername(sockfd, <__sockaddr*>addr.ptr, &addr.sizeof))
Expand Down Expand Up @@ -50,7 +101,6 @@ cpdef tuple[bytes, uint16_t] getsockname(int sockfd, sockaddr addr):
raise TypeError('getsockname() - received `addr.family` type not supported!')
return (ip, port)


cpdef tuple getnameinfo(sockaddr addr, int flags=0):
'''
Example
Expand Down
19 changes: 19 additions & 0 deletions test/socket/getaddinfo_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import pytest
import liburing


def test_getaddrinfo():
assert len(list(liburing.getaddrinfo(b'127.0.0.1', b'12345'))) == 2
assert len(list(liburing.getaddrinfo(b'python.org', b'80'))) > 12 < 33
for af_, sock_, proto, canon, addr in liburing.getaddrinfo(b'127.0.0.1', b'12345'):
assert af_ == liburing.AF_INET
assert sock_ == liburing.SOCK_STREAM
assert proto == liburing.IPPROTO_TCP
assert canon == b''
assert type(addr) is liburing.sockaddr
assert addr.family == liburing.AF_INET
break

msg = 'Servname not supported for ai_socktype'
with pytest.raises(OSError, match=msg):
liburing.getaddrinfo(b'127.0.0.1', b'123abc45')
2 changes: 1 addition & 1 deletion test/socket/getsockname_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,6 @@ def test_getsockname(ring, cqe):
liburing.io_uring_prep_close(sqe, sockfd)
sqe.user_data = i+1
assert liburing.io_uring_submit_and_wait_timeout(ring, cqe, 1, ts) == 1
sockfd = liburing.trap_error(cqe.res)
liburing.trap_error(cqe.res)
assert cqe.user_data == i+1
liburing.io_uring_cqe_seen(ring, cqe)

0 comments on commit 5e46798

Please sign in to comment.