qmsk/net/socket/address.pyx
changeset 20 0e4933d5862e
parent 17 5f4077a530b0
child 22 f6e8d5e37998
equal deleted inserted replaced
19:e6b670dbfe3b 20:0e4933d5862e
       
     1 cimport qmsk.net.libc as libc
       
     2 
       
     3 from qmsk.net.socket.address cimport *
       
     4 
       
     5 cimport qmsk.net.socket.platform as platform
       
     6 
       
     7 cdef class sockaddr :
       
     8     cdef void _init_family (self, platform.sa_family_t family=platform.AF_UNSPEC) :
       
     9         self.family = family
       
    10 
       
    11     # XXX:use size_t
       
    12     cdef int _get_sockaddr (self, platform.sockaddr **sa_ptr, platform.socklen_t *sa_len) except -1 :
       
    13         """
       
    14             Get the sockaddr pointer and sockaddr length for this address
       
    15         """ 
       
    16 
       
    17         raise NotImplementedError()
       
    18 
       
    19     cdef platform.sockaddr* _get_sockaddr_ptr (self) except NULL :
       
    20         """
       
    21             Get the sockaddr pointer
       
    22         """
       
    23 
       
    24         cdef platform.sockaddr *sa
       
    25         cdef platform.socklen_t sa_len
       
    26 
       
    27         self._get_sockaddr(&sa, &sa_len)
       
    28 
       
    29         return sa
       
    30     
       
    31     cdef platform.socklen_t _get_sockaddr_len (self) except -1 :
       
    32         """
       
    33             Get the sockaddr len
       
    34         """
       
    35 
       
    36         cdef platform.sockaddr *sa
       
    37         cdef platform.socklen_t sa_len
       
    38 
       
    39         self._get_sockaddr(&sa, &sa_len)
       
    40 
       
    41         return sa_len
       
    42 
       
    43     cdef int _set_sockaddr (self, platform.sockaddr *sa, size_t sa_len) except -1 :
       
    44         """
       
    45             Set the sockaddr value for this address; sa_len must match!
       
    46         """
       
    47 
       
    48         raise NotImplementedError()
       
    49 
       
    50     def getnameinfo (self) :
       
    51         """
       
    52             Returns a (host, serv) tuple for this address à la getnameinfo
       
    53         """
       
    54 
       
    55         cdef platform.sockaddr *sa
       
    56         cdef platform.socklen_t sa_len
       
    57 
       
    58         # XXX: take as args?
       
    59         cdef int flags = platform.NI_NUMERICHOST | platform.NI_NUMERICSERV
       
    60         
       
    61         # get our abstract sockaddr
       
    62         self._get_sockaddr(&sa, &sa_len)
       
    63 
       
    64         # get nice text format
       
    65         return platform.getnameinfo(sa, sa_len, flags)
       
    66 
       
    67     property addr :
       
    68         """
       
    69             The ASCII literal network address
       
    70         """
       
    71 
       
    72         def __get__ (self) :
       
    73             """
       
    74                 Default implmentation using getnameinfo()
       
    75             """
       
    76 
       
    77             addr, port = self.getnameinfo()
       
    78 
       
    79             return addr
       
    80 
       
    81     property port :
       
    82         """
       
    83             The integer port number
       
    84         """
       
    85 
       
    86         def __get__ (self) :
       
    87             """
       
    88                 Default implementation using getnameinfo() and int()
       
    89             """
       
    90 
       
    91             addr, port = self.getnameinfo()
       
    92 
       
    93             return int(port)
       
    94     
       
    95     def __repr__ (self) :
       
    96         return "sockaddr(%d, %s, %d)" % (self.family, self.addr, self.port)
       
    97 
       
    98 cdef class sockaddr_in (sockaddr) :
       
    99     """
       
   100         AF_INET struct sockaddr_in
       
   101 
       
   102         >>> sa = sockaddr_in("127.0.0.1", 80)
       
   103         >>> sa.addr
       
   104         '127.0.0.1'
       
   105         >>> sa.port
       
   106         80
       
   107         >>> str(sa)
       
   108         '127.0.0.1:80'
       
   109         >>> str(sockaddr_in())
       
   110         '0.0.0.0:0'
       
   111     """
       
   112 
       
   113     # the struct sockaddr_in
       
   114     cdef platform.sockaddr_in sockaddr
       
   115 
       
   116     def __init__ (self, object addr=None, platform.in_port_t port=0) :
       
   117         """
       
   118             Construct using given literal IPv4 address and TCP/UDP port
       
   119 
       
   120                 addr        - IPv4 address, defaults to INADDR_ANY (0.0.0.0)
       
   121                 port        - TCP/UDP port, defaults to 0 (ephemeral)
       
   122         """
       
   123 
       
   124         # store our family
       
   125         # XXX: this should be a class attribute...
       
   126         self._init_family(platform.AF_INET)
       
   127 
       
   128         # constant af
       
   129         self.sockaddr.sin_family = self.family
       
   130         
       
   131         # set the sin_port
       
   132         self.sockaddr.sin_port = platform.htons(port)
       
   133         
       
   134         if addr :
       
   135             # set the sin_addr
       
   136             # this automatically converts the addr from str -> char *
       
   137             platform.inet_pton(self.family, addr, &self.sockaddr.sin_addr)
       
   138 
       
   139         else :
       
   140             # set as INADDR_ANY
       
   141             self.sockaddr.sin_addr.s_addr = platform.INADDR_ANY
       
   142     
       
   143     cdef int _get_sockaddr (self, platform.sockaddr **sa_ptr, platform.socklen_t *sa_len) except -1 :
       
   144         if sa_ptr :
       
   145             sa_ptr[0] = <platform.sockaddr *> &self.sockaddr
       
   146 
       
   147         if sa_len :
       
   148             sa_len[0] = sizeof(self.sockaddr)
       
   149 
       
   150         return 0
       
   151 
       
   152     cdef int _set_sockaddr (self, platform.sockaddr *sa, size_t sa_len) except -1 :
       
   153         assert sa_len == sizeof(self.sockaddr)
       
   154 
       
   155         libc.memcpy(&self.sockaddr, sa, sa_len)
       
   156 
       
   157     property port :
       
   158         """
       
   159             The integer port number
       
   160         """
       
   161 
       
   162         def __get__ (self) :
       
   163             return platform.ntohs(self.sockaddr.sin_port)
       
   164 
       
   165     def __str__ (self) :
       
   166         """
       
   167             Return the literal ASCII representation for this sockaddr as an '<addr>:<port> string
       
   168         """
       
   169         
       
   170         # format
       
   171         return "%s:%s" % self.getnameinfo()
       
   172 
       
   173 cdef class sockaddr_in6 (sockaddr) :
       
   174     """
       
   175         AF_INET6 struct sockaddr_in6
       
   176 
       
   177         >>> sa6 = sockaddr_in6("::1", 80)
       
   178         >>> sa6.addr
       
   179         '::1'
       
   180         >>> sa6.port
       
   181         80
       
   182         >>> str(sa6)
       
   183         '[::1]:80'
       
   184         >>> str(sockaddr_in6())
       
   185         '[::]:0'
       
   186     """
       
   187 
       
   188     cdef platform.sockaddr_in6 sockaddr
       
   189 
       
   190     def __init__ (self, object addr=None, platform.in_port_t port=0) :
       
   191         """
       
   192             Construct using given literal IPv6 address and TCP/UDP port
       
   193 
       
   194                 addr        - IPv6 address, defaults to platform.in6addr_any (::)
       
   195                 port        - TCP/UDP port, defaults to 0 (ephemeral)
       
   196         """
       
   197 
       
   198         # store our family
       
   199         # XXX: this should be a class attribute...
       
   200         self._init_family(platform.AF_INET6)
       
   201 
       
   202         # constant af
       
   203         self.sockaddr.sin6_family = self.family
       
   204         
       
   205         # set the sin_port
       
   206         self.sockaddr.sin6_port = platform.htons(port)
       
   207         
       
   208         if addr :
       
   209             # set the sin_addr
       
   210             # this automatically converts the addr from str -> char *
       
   211             platform.inet_pton(self.family, addr, &self.sockaddr.sin6_addr)
       
   212 
       
   213         else :
       
   214             # set as INADDR_ANY
       
   215             self.sockaddr.sin6_addr = platform.in6addr_any
       
   216 
       
   217     cdef int _get_sockaddr (self, platform.sockaddr **sa_ptr, platform.socklen_t *sa_len) except -1 :
       
   218         if sa_ptr :
       
   219             sa_ptr[0] = <platform.sockaddr *> &self.sockaddr
       
   220 
       
   221         if sa_len :
       
   222             sa_len[0] = sizeof(self.sockaddr)
       
   223 
       
   224         return 0
       
   225 
       
   226     cdef int _set_sockaddr (self, platform.sockaddr *sa, size_t sa_len) except -1 :
       
   227         assert sa_len == sizeof(self.sockaddr)
       
   228 
       
   229         libc.memcpy(&self.sockaddr, sa, sa_len)
       
   230 
       
   231     property port :
       
   232         """
       
   233             The integer port number
       
   234         """
       
   235 
       
   236         def __get__ (self) :
       
   237             return platform.ntohs(self.sockaddr.sin6_port)
       
   238 
       
   239 
       
   240     def __str__ (self) :
       
   241         """
       
   242             Return the literal ASCII representation for this sockaddr as a '[<addr>]:<port> string
       
   243         """
       
   244         
       
   245         # format
       
   246         return "[%s]:%s" % self.getnameinfo()
       
   247 
       
   248 # mapping of AF -> sockaddr, user-modifyable
       
   249 SOCKADDR_BY_FAMILY = {
       
   250     platform.AF_INET:   sockaddr_in,
       
   251     platform.AF_INET6:  sockaddr_in6,
       
   252 }
       
   253 
       
   254 # build a sockaddr from the given sockaddr struct, based on sa_family
       
   255 cdef sockaddr build_sockaddr (platform.sockaddr *sa, size_t sa_len) :
       
   256     # lookup correct class to use
       
   257     addr_type = SOCKADDR_BY_FAMILY[sa.sa_family]
       
   258     
       
   259     # construct with defaults
       
   260     cdef sockaddr addr = addr_type()
       
   261 
       
   262     # store
       
   263     addr._set_sockaddr(sa, sa_len)
       
   264 
       
   265     return addr
       
   266 
       
   267 cdef class addrinfo :
       
   268     
       
   269     cdef _init_addrinfo (self, platform.addrinfo *ai) :
       
   270         #ai.flags = c_ai.ai_flags
       
   271         self.family = ai.ai_family
       
   272         self.socktype = ai.ai_socktype
       
   273         self.protocol = ai.ai_protocol
       
   274         self.addr = build_sockaddr(ai.ai_addr, ai.ai_addrlen)
       
   275         self.canonname = ai.ai_canonname if ai.ai_canonname else None
       
   276 
       
   277     def __str__ (self) :
       
   278         return "family=%d, socktype=%d, protocol=%d, addr=%s, canonname=%s" % (self.family, self.socktype, self.protocol, self.addr, self.canonname)
       
   279 
       
   280 cdef addrinfo build_addrinfo (platform.addrinfo *c_ai) :
       
   281     cdef addrinfo ai = addrinfo()
       
   282     
       
   283     ai._init_addrinfo(c_ai)
       
   284 
       
   285     return ai
       
   286 
       
   287 cdef class endpoint :
       
   288 
       
   289     def __init__ (self, hostname=None, service=None) :
       
   290         """
       
   291             Construct with the given hostname/service, either of which may be None.
       
   292 
       
   293             A hostname of None implies all valid local addresses (with AI_PASSIVE), and a service of None implies an
       
   294             ephemeral port.
       
   295 
       
   296                 hostname        - the literal address or DNS hostname or anything else that GAI supports
       
   297                 service         - the numeric port or service name
       
   298         """
       
   299 
       
   300         self.hostname = str(hostname)
       
   301         self.service = str(service)
       
   302 
       
   303     cpdef getaddrinfo (self, int family, int socktype, int protocol = 0, int flags = platform.AI_PASSIVE) :
       
   304         """
       
   305             Look up our hostname/service using the given socket parameters, and return a sequence of addrinfo objects.
       
   306         """
       
   307         
       
   308         # XXX: Cython doesn't support proper compound value literals...
       
   309         cdef platform.addrinfo hints
       
   310         
       
   311         libc.memset(&hints, 0, sizeof(hints))
       
   312         hints.ai_flags          = flags
       
   313         hints.ai_family         = family
       
   314         hints.ai_socktype       = socktype
       
   315         hints.ai_protocol       = protocol
       
   316 
       
   317         cdef platform.addrinfo *res, *r
       
   318         cdef int err
       
   319         cdef object ret = []
       
   320 
       
   321         cdef char *hostname = NULL
       
   322         cdef char *service = NULL
       
   323 
       
   324         if self.hostname is not None :
       
   325             hostname = self.hostname
       
   326         
       
   327         if self.service is not None :
       
   328             service = self.service
       
   329 
       
   330         # operate!
       
   331         err = platform.c_getaddrinfo(hostname, service, &hints, &res)
       
   332 
       
   333         try :
       
   334             if err :
       
   335                 # XXX: raise a GAIError
       
   336                 raise Exception(platform.gai_strerror(err))
       
   337             
       
   338             # gather results
       
   339             r = res
       
   340 
       
   341             while r :
       
   342                 ret.append(build_addrinfo(r))
       
   343 
       
   344                 r = r.ai_next
       
   345             
       
   346             # ok
       
   347             return ret
       
   348 
       
   349         finally :
       
   350             platform.c_freeaddrinfo(res)
       
   351 
       
   352     def __str__ (self) :
       
   353         return "hostname=%s, service=%s" % (self.hostname, self.service)
       
   354