6
|
1 |
"""
|
|
2 |
Socket addresses at various levels:
|
|
3 |
|
|
4 |
sockaddr - specific network-level address for socket operations
|
|
5 |
addrinfo - information on a specific sockaddr including its socket parameters
|
|
6 |
endpoint - human-readable network address, corresponding to multiple sockaddr's
|
|
7 |
"""
|
|
8 |
|
|
9 |
cimport libc
|
|
10 |
|
10
|
11 |
cimport qmsk.net.socket.platform as platform
|
|
12 |
|
6
|
13 |
cdef class sockaddr :
|
|
14 |
"""
|
|
15 |
A network-level socket address
|
|
16 |
|
|
17 |
XXX: rename to 'address'
|
|
18 |
|
|
19 |
>>> sockaddr().family
|
|
20 |
0
|
|
21 |
>>> sockaddr().port
|
|
22 |
Traceback (most recent call last):
|
|
23 |
...
|
|
24 |
NotImplementedError
|
|
25 |
>>> sockaddr().getnameinfo()
|
|
26 |
Traceback (most recent call last):
|
|
27 |
...
|
|
28 |
NotImplementedError
|
|
29 |
"""
|
|
30 |
|
|
31 |
# address family
|
|
32 |
# XXX: this should be a class constant! It's part of our type safety!
|
10
|
33 |
cdef readonly platform.sa_family_t family
|
6
|
34 |
|
10
|
35 |
cdef void _init_family (self, platform.sa_family_t family = ?)
|
6
|
36 |
|
|
37 |
# get the sockaddr/socklen
|
|
38 |
# each of these can be NULL to ignore it
|
10
|
39 |
cdef int _get_sockaddr (self, platform.sockaddr **sa_ptr, platform.socklen_t *sa_len) except -1
|
6
|
40 |
|
10
|
41 |
cdef platform.sockaddr* _get_sockaddr_ptr (self) except NULL
|
|
42 |
cdef platform.socklen_t _get_sockaddr_len (self) except -1
|
6
|
43 |
|
|
44 |
# set the sockaddr, socklen must match
|
10
|
45 |
cdef int _set_sockaddr (self, platform.sockaddr *sa, size_t sa_len) except -1
|
6
|
46 |
|
|
47 |
# build a sockaddr from the given sockaddr struct, based on sa_family
|
10
|
48 |
cdef sockaddr build_sockaddr (platform.sockaddr *sa, size_t sa_len)
|
6
|
49 |
|
|
50 |
cdef class addrinfo :
|
|
51 |
"""
|
|
52 |
A socket-level endpoint address, which contains the full socket parameters and an bind/connect address
|
|
53 |
"""
|
|
54 |
|
|
55 |
# cdef readonly int flags
|
|
56 |
cdef readonly int family, socktype, protocol
|
|
57 |
cdef readonly sockaddr addr
|
|
58 |
cdef readonly object canonname
|
|
59 |
|
10
|
60 |
cdef _init_addrinfo (self, platform.addrinfo *c_ai)
|
6
|
61 |
|
|
62 |
# build and return a new addrinfo instance
|
10
|
63 |
cdef addrinfo build_addrinfo (platform.addrinfo *c_ai)
|
6
|
64 |
|
|
65 |
cdef class endpoint :
|
|
66 |
"""
|
|
67 |
A network-level socket endpoint. This is the level that humans mostly work with, but the tricky bit is that
|
|
68 |
an endpoint can map to more than one sockaddr...
|
|
69 |
|
|
70 |
Hence, endpoints are stored as human-readable hostname/service strings, which are then translated to sockaddrs
|
|
71 |
using getaddrinfo.
|
|
72 |
|
8
|
73 |
>>> from __future__ import absolute_import; import socket as _socket
|
6
|
74 |
>>> e = endpoint('127.0.0.1', 80)
|
|
75 |
>>> str(e)
|
|
76 |
'hostname=127.0.0.1, service=80'
|
8
|
77 |
>>> res = e.getaddrinfo(_socket.AF_UNSPEC, _socket.SOCK_STREAM)
|
6
|
78 |
>>> len(res)
|
|
79 |
1
|
|
80 |
>>> str(res[0])
|
|
81 |
'family=2, socktype=1, protocol=6, addr=127.0.0.1:80, canonname=None'
|
|
82 |
>>> e = endpoint('2001::5', 80)
|
|
83 |
>>> str(e)
|
|
84 |
'hostname=2001::5, service=80'
|
8
|
85 |
>>> res = e.getaddrinfo(_socket.AF_UNSPEC, _socket.SOCK_STREAM)
|
6
|
86 |
>>> len(res)
|
|
87 |
1
|
|
88 |
>>> str(res[0])
|
|
89 |
'family=10, socktype=1, protocol=6, addr=[2001::5]:80, canonname=None'
|
|
90 |
|
|
91 |
"""
|
|
92 |
|
|
93 |
# our defining attributes, set via __init__
|
|
94 |
cdef object hostname, service
|
|
95 |
|
|
96 |
cpdef getaddrinfo (self, int family, int socktype, int protocol = ?, int flags = ?)
|
|
97 |
|