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