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