Ticket #1212: ipv6.3.patch

File ipv6.3.patch, 19.4 KB (added by guest, 7 years ago)
Line 
1
2New patches:
3
4[Add IPv6 support.
5Bryan O'Sullivan <bos@serpentine.com>**20070331183923
6 
7 The only public API changes are to Network.Socket, which has the following
8 exported names added (no existing names have been removed):
9 
10     -- IPv6 address components
11     HostAddress6
12     FlowInfo
13     ScopeID
14 
15     -- Name -> address lookup
16     getAddrInfo
17     AddrInfo
18     AddrInfoFlags
19     aI_PASSIVE
20     aI_CANONNAME
21     aI_NUMERICHOST
22     aI_ADDRCONFIG
23     defaultHints
24 
25     -- Address -> name lookup
26     getNameInfo
27     NameInfoFlags
28     nI_NOFQDN
29     nI_NUMERICHOST
30     nI_NAMEREQD
31     nI_NUMERICSERV
32     nI_DGRAM
33 
34 The SockAddr type acquires a new branch, SockAddr6.  (This could cause
35 new "non-exhaustive matches" warnings when compiling pre-existing client
36 code that pattern-matches on SockAddr values.  However, it will not
37 cause runtime pattern failure errors in clients using the pre-existing
38 IPv4 entry points, as they will never see IPv6 addresses.)
39 
40 This change moves a few type names from Network.BSD to Network.Socket:
41 
42     HostName
43     ServiceName
44 
45 These names are still re-exported from Network.BSD, so pre-existing code
46 should not be affected.
47] {
48hunk ./Network/BSD.hsc 117
49-type HostName     = String
50hunk ./Network/BSD.hsc 118
51-type ServiceName  = String
52hunk ./Network/BSD.hsc 299
53--- | This is the default protocol for the given service.
54-defaultProtocol :: ProtocolNumber
55-defaultProtocol = 0
56-
57hunk ./Network/Socket.hsc 54
58+#if defined(IPV6_SOCKET_SUPPORT)
59+    HostAddress6,
60+    FlowInfo,
61+    ScopeID,
62+#endif
63hunk ./Network/Socket.hsc 65
64+    -- * Address operations
65+
66+#if defined(IPV6_SOCKET_SUPPORT)
67+    AddrInfo(..),
68+
69+    AddrInfoFlags,
70+    aI_PASSIVE,
71+    aI_CANONNAME,
72+    aI_NUMERICHOST,
73+    aI_ADDRCONFIG,
74+
75+    HostName,
76+    ServiceName,
77+
78+    defaultHints,           -- :: AddrInfo
79+    defaultProtocol,        -- :: ProtocolNumber
80+
81+    getAddrInfo,            -- :: Maybe AddrInfo -> Maybe HostName -> Maybe ServiceName -> IO [AddrInfo]
82+
83+    NameInfoFlags,
84+    nI_NOFQDN,
85+    nI_NUMERICHOST,
86+    nI_NAMEREQD,
87+    nI_NUMERICSERV,
88+    nI_DGRAM,
89+
90+    getNameInfo,            -- :: NameInfoFlags -> Bool -> Bool -> SockAddr -> IO (Maybe HostName, Maybe ServiceName)
91+#endif
92+
93hunk ./Network/Socket.hsc 157
94+#if defined(IPV6_SOCKET_SUPPORT)
95+    iN6ADDR_ANY,       -- :: HostAddress6
96+#endif
97hunk ./Network/Socket.hsc 204
98-import Foreign.Ptr ( Ptr, castPtr, plusPtr )
99+import Foreign.Ptr ( Ptr, castPtr, nullPtr, plusPtr )
100hunk ./Network/Socket.hsc 207
101-import Foreign.C.String ( withCString, peekCString, peekCStringLen, castCharToCChar )
102+import Foreign.C.String ( CString, withCString, peekCString, peekCStringLen, castCharToCChar )
103hunk ./Network/Socket.hsc 211
104-import Foreign.Marshal.Utils ( with )
105+import Foreign.Marshal.Utils ( maybeWith, with )
106hunk ./Network/Socket.hsc 234
107+type HostName       = String
108+type ServiceName    = String
109+
110hunk ./Network/Socket.hsc 298
111+-- | This is the default protocol for a given service.
112+defaultProtocol :: ProtocolNumber
113+defaultProtocol = 0
114+
115hunk ./Network/Socket.hsc 307
116+#if defined(IPV6_SOCKET_SUPPORT)
117+type HostAddress6 = (Word32, Word32, Word32, Word32)
118+#endif
119+
120hunk ./Network/Socket.hsc 381
121--- families. Currently only Unix domain sockets and the Internet family
122--- are supported.
123+-- families. Currently only Unix domain sockets and the Internet
124+-- families are supported.
125+
126+#if defined(IPV6_SOCKET_SUPPORT)
127+type FlowInfo = Word32
128+type ScopeID = Word32
129+#endif
130hunk ./Network/Socket.hsc 393
131+#if defined(IPV6_SOCKET_SUPPORT)
132+  | SockAddrInet6
133+        PortNumber      -- sin6_port (network byte order)
134+        FlowInfo        -- sin6_flowinfo (ditto)
135+        HostAddress6    -- sin6_addr (ditto)
136+        ScopeID         -- sin6_scope_id (ditto)
137+#endif
138hunk ./Network/Socket.hsc 424
139+#if defined(IPV6_SOCKET_SUPPORT)
140+  showsPrec _ addr@(SockAddrInet6 port _ _ _)
141+   = showChar '['
142+   . showString (unsafePerformIO $
143+                 fst `liftM` getNameInfo nI_NUMERICHOST True False addr >>=
144+                 maybe (fail "showsPrec: impossible internal error") return)
145+   . showString "]:"
146+   . shows port
147+
148+instance Storable HostAddress6 where
149+    sizeOf _    = (#const sizeof(struct in6_addr))
150+    alignment _ = alignment (undefined :: CInt)
151+
152+    peek p = do
153+        a <- (#peek struct in6_addr, s6_addr32[0]) p
154+        b <- (#peek struct in6_addr, s6_addr32[1]) p
155+        c <- (#peek struct in6_addr, s6_addr32[2]) p
156+        d <- (#peek struct in6_addr, s6_addr32[3]) p
157+        return (a, b, c, d)
158+
159+    poke p (a, b, c, d) = do
160+        (#poke struct in6_addr, s6_addr32[0]) p a
161+        (#poke struct in6_addr, s6_addr32[1]) p b
162+        (#poke struct in6_addr, s6_addr32[2]) p c
163+        (#poke struct in6_addr, s6_addr32[3]) p d
164+#endif
165hunk ./Network/Socket.hsc 471
166+#if defined(IPV6_SOCKET_SUPPORT)
167+pokeSockAddr p (SockAddrInet6 (PortNum port) flow addr scope) = do
168+#if defined(darwin_TARGET_OS)
169+       zeroMemory p (#const sizeof(struct sockaddr_in6))
170+#endif
171+       (#poke struct sockaddr_in6, sin6_family) p ((#const AF_INET6) :: CSaFamily)
172+       (#poke struct sockaddr_in6, sin6_port) p port
173+       (#poke struct sockaddr_in6, sin6_flowinfo) p flow
174+       (#poke struct sockaddr_in6, sin6_addr) p addr   
175+       (#poke struct sockaddr_in6, sin6_scope_id) p scope
176+#endif
177+
178+peekSockAddr :: Ptr SockAddr -> IO SockAddr
179hunk ./Network/Socket.hsc 497
180+#if defined(IPV6_SOCKET_SUPPORT)
181+       (#const AF_INET6) -> do
182+               port <- (#peek struct sockaddr_in6, sin6_port) p
183+               flow <- (#peek struct sockaddr_in6, sin6_flowinfo) p
184+               addr <- (#peek struct sockaddr_in6, sin6_addr) p
185+               scope <- (#peek struct sockaddr_in6, sin6_scope_id) p
186+               return (SockAddrInet6 (PortNum port) flow addr scope)
187+#endif
188hunk ./Network/Socket.hsc 522
189+#if defined(IPV6_SOCKET_SUPPORT)
190+sizeOfSockAddr (SockAddrInet6 _ _ _ _) = #const sizeof(struct sockaddr_in6)
191+#endif
192hunk ./Network/Socket.hsc 908
193+#if defined(IPV6_SOCKET_SUPPORT)
194+socketPort sock@(MkSocket _ AF_INET6 _ _ _) = do
195+    (SockAddrInet6 port _ _ _) <- getSocketName sock
196+    return port
197+#endif
198hunk ./Network/Socket.hsc 1250
199+unpackSocketType:: CInt -> SocketType
200hunk ./Network/Socket.hsc 1893
201+unpackSocketType t = case t of
202+       0 -> NoSocketType
203+#ifdef SOCK_STREAM
204+       (#const SOCK_STREAM) -> Stream
205+#endif
206+#ifdef SOCK_DGRAM
207+       (#const SOCK_DGRAM) -> Datagram
208+#endif
209+#ifdef SOCK_RAW
210+       (#const SOCK_RAW) -> Raw
211+#endif
212+#ifdef SOCK_RDM
213+       (#const SOCK_RDM) -> RDM
214+#endif
215+#ifdef SOCK_SEQPACKET
216+       (#const SOCK_SEQPACKET) -> SeqPacket
217+#endif
218+
219hunk ./Network/Socket.hsc 1920
220+#if defined(IPV6_SOCKET_SUPPORT)
221+iN6ADDR_ANY :: HostAddress6
222+iN6ADDR_ANY = (0, 0, 0, 0)
223+#endif
224+
225hunk ./Network/Socket.hsc 2050
226+
227+
228+-----------------------------------------------------------------------------
229+-- Address and service lookups
230+
231+#if defined(IPV6_SOCKET_SUPPORT)
232+
233+-- | Flags that control the querying behaviour of 'getAddrInfo'.
234+type AddrInfoFlags = CInt
235+
236+-- Portable flags.
237+
238+aI_PASSIVE :: AddrInfoFlags
239+aI_PASSIVE                  = (#const AI_PASSIVE)
240+aI_CANONNAME :: AddrInfoFlags
241+aI_CANONNAME                = (#const AI_CANONNAME)
242+aI_NUMERICHOST :: AddrInfoFlags
243+aI_NUMERICHOST              = (#const AI_NUMERICHOST)
244+aI_ADDRCONFIG :: AddrInfoFlags
245+aI_ADDRCONFIG               = (#const AI_ADDRCONFIG)
246+
247+data AddrInfo =
248+    AddrInfo {
249+        addrFlags :: AddrInfoFlags,
250+        addrFamily :: Family,
251+        addrSocketType :: SocketType,
252+        addrProtocol :: ProtocolNumber,
253+        addrAddress :: SockAddr,
254+        addrCanonName :: Maybe String
255+        }
256+    deriving (Show)
257+
258+INSTANCE_TYPEABLE0(AddrInfo,addrInfoTc,"AddrInfo")
259+
260+instance Storable AddrInfo where
261+    sizeOf    _ = #const sizeof(struct addrinfo)
262+    alignment _ = alignment (undefined :: CInt)
263+
264+    peek p = do
265+       ai_flags <- (#peek struct addrinfo, ai_flags) p
266+       ai_family <- (#peek struct addrinfo, ai_family) p
267+       ai_socktype <- (#peek struct addrinfo, ai_socktype) p
268+       ai_protocol <- (#peek struct addrinfo, ai_protocol) p
269+        ai_addr <- (#peek struct addrinfo, ai_addr) p >>= peekSockAddr
270+       ai_canonname_ptr <- (#peek struct addrinfo, ai_canonname) p
271+
272+        ai_canonname <- if ai_canonname_ptr == nullPtr
273+                        then return Nothing
274+                        else liftM Just $ peekCString ai_canonname_ptr
275+                             
276+        return (AddrInfo
277+                {
278+                 addrFlags = ai_flags,
279+                 addrFamily = unpackFamily ai_family,
280+                 addrSocketType = unpackSocketType ai_socktype,
281+                 addrProtocol = ai_protocol,
282+                 addrAddress = ai_addr,
283+                 addrCanonName = ai_canonname
284+                })
285+
286+    poke p (AddrInfo flags family socketType protocol _ _) = do
287+        (#poke struct addrinfo, ai_flags) p flags
288+        (#poke struct addrinfo, ai_family) p (packFamily family)
289+        (#poke struct addrinfo, ai_socktype) p (packSocketType socketType)
290+        (#poke struct addrinfo, ai_protocol) p protocol
291+
292+        -- stuff below is probably not needed, but let's zero it for safety
293+
294+        (#poke struct addrinfo, ai_addrlen) p (0::CSize)
295+        (#poke struct addrinfo, ai_addr) p nullPtr
296+        (#poke struct addrinfo, ai_canonname) p nullPtr
297+        (#poke struct addrinfo, ai_next) p nullPtr
298+
299+type NameInfoFlags = CInt
300+
301+nI_NOFQDN :: NameInfoFlags
302+nI_NOFQDN = (#const NI_NOFQDN)
303+nI_NUMERICHOST :: NameInfoFlags
304+nI_NUMERICHOST = (#const NI_NUMERICHOST)
305+nI_NAMEREQD :: NameInfoFlags
306+nI_NAMEREQD = (#const NI_NAMEREQD)
307+nI_NUMERICSERV :: NameInfoFlags
308+nI_NUMERICSERV = (#const NI_NUMERICSERV)
309+nI_DGRAM :: NameInfoFlags
310+nI_DGRAM = (#const NI_DGRAM)
311+
312+-- | Default hints for address lookup with 'getAddrInfo'.  The values
313+-- of the 'addrAddress' and 'addrCanonName' fields are 'undefined',
314+-- and are never inspected by 'getAddrInfo'.
315+
316+defaultHints :: AddrInfo
317+
318+defaultHints = AddrInfo {
319+                         addrFlags = 0,
320+                         addrFamily = AF_UNSPEC,
321+                         addrSocketType = NoSocketType,
322+                         addrProtocol = defaultProtocol,
323+                         addrAddress = undefined,
324+                         addrCanonName = undefined
325+                        }
326+
327+-- | Resolve a host or service name query to one or more addresses.
328+-- The 'AddrInfo' values that this function returns contain 'SockAddr'
329+-- values that you can pass directly to 'connect' or
330+-- 'bindSocket'.
331+--
332+-- The 'AddrInfo' argument specifies the preferred query behaviour,
333+-- socket options, or protocol.  You can override these conveniently
334+-- using Haskell's record update syntax on 'defaultHints', for example
335+-- as follows:
336+--
337+-- @
338+--   myHints = defaultHints { addrFlags = aI_ADDRCONFIG + aI_CANONNAME }
339+-- @
340+--
341+-- Values for 'addrFlags' can be /or/ed together, and control query
342+-- behaviour.  The portable values are as follows:
343+--
344+--   [@aI_PASSIVE@] If no 'HostName' value is provided, the network
345+--     address in each 'SockAddr'
346+--     will be left as a "wild card", i.e. as either 'iNADDR_ANY'
347+--     or 'iN6ADDR_ANY'.  This is useful for server applications that
348+--     will accept connections from any client.
349+--
350+--   [@aI_CANONNAME@] The 'addrCanonName' field of the first returned
351+--     'AddrInfo' will contain the "canonical name" of the host.
352+--
353+--   [@aI_NUMERICHOST@] The 'HostName' argument /must/ be a numeric
354+--     address in string form, and network name lookups will not be
355+--      attempted.
356+--
357+--   [@aI_ADDRCONFIG@] The list of returned 'AddrInfo' values will only
358+--     contain IPv4 addresses if the local system has at least one
359+--     IPv4 interface configured, and likewise for IPv6.
360+--
361+-- A number of non-portable values for 'addrFlags' may also be
362+-- available, depending on your platform.
363+--
364+-- You must provide a real value for at least one of the 'HostName' or
365+-- 'ServiceName' arguments.  'HostName' can be either a numeric
366+-- network address (dotted quad for IPv4, hex for IPv6) or a hostname.
367+-- In the latter case, its addresses will be looked up unless
368+-- 'aI_NUMERICHOST' is specified as a hint.  If you do not provide a
369+-- 'HostName' value /and/ do not set 'aI_PASSIVE' as a hint, network
370+-- addresses in the result will contain the address of the loopback
371+-- interface.
372+--
373+-- If the query fails, this function throws an IO exception instead of
374+-- returning an empty list.  Otherwise, it returns a non-empty list
375+-- of 'AddrInfo' values.
376+--
377+-- There are several reasons why a query might result in several
378+-- values.  For example, the queried-for host could be multihomed, or
379+-- the service might be available via several protocols.
380+--
381+-- Note: the order of arguments is slightly different to that defined
382+-- for @getaddrinfo@ in RFC 2553.  The 'AddrInfo' parameter comes first
383+-- to make partial application easier.
384+--
385+-- Example:
386+-- @
387+--   let hints = defaultHints { addrFlags = aI_ADDRCONFIG + aI_CANONNAME }
388+--   addrs <- getAddrInfo (Just hints) (Just "www.haskell.org") (Just "http")
389+--   let addr = head addrs
390+--   sock <- socket (addrFamily addr) (addrSocketType addr) (addrProtocol addr)
391+--   connect sock (addrAddress addr)
392+-- @
393+
394+getAddrInfo :: Maybe AddrInfo -- ^ preferred socket type or protocol
395+            -> Maybe HostName -- ^ host name to look up
396+            -> Maybe ServiceName -- ^ service name to look up
397+            -> IO [AddrInfo] -- ^ resolved addresses, with "best" first
398+
399+getAddrInfo hints node service =
400+  maybeWith withCString node $ \c_node ->
401+    maybeWith withCString service $ \c_service ->
402+      maybeWith with hints $ \c_hints ->
403+        alloca $ \ptr_ptr_addrs -> do
404+          ret <- c_getaddrinfo c_node c_service c_hints ptr_ptr_addrs
405+          case ret of
406+            0 -> do ptr_addrs <- peek ptr_ptr_addrs
407+                    ais <- followAddrInfo ptr_addrs
408+                    c_freeaddrinfo ptr_addrs
409+                    return ais
410+            _ -> do err <- gai_strerror ret
411+                    ioError (IOError Nothing NoSuchThing "getAddrInfo" err
412+                             Nothing)
413+
414+followAddrInfo :: Ptr AddrInfo -> IO [AddrInfo]
415+
416+followAddrInfo ptr_ai | ptr_ai == nullPtr = return []
417+                      | otherwise = do
418+    a <- peek ptr_ai
419+    as <- (#peek struct addrinfo, ai_next) ptr_ai >>= followAddrInfo
420+    return (a:as)
421+
422+foreign import ccall safe "getaddrinfo"
423+    c_getaddrinfo :: CString -> CString -> Ptr AddrInfo -> Ptr (Ptr AddrInfo)
424+                  -> IO CInt
425+
426+foreign import ccall safe "freeaddrinfo"
427+    c_freeaddrinfo :: Ptr AddrInfo -> IO ()
428+
429+gai_strerror :: CInt -> IO String
430+
431+gai_strerror n = c_gai_strerror n >>= peekCString
432+
433+foreign import ccall safe "gai_strerror"
434+    c_gai_strerror :: CInt -> IO CString
435+
436+withCStringIf :: Bool -> Int -> (CSize -> CString -> IO a) -> IO a
437+withCStringIf False _ f = f 0 nullPtr
438+withCStringIf True n f = allocaBytes n (f (fromIntegral n))
439+                   
440+getNameInfo :: NameInfoFlags
441+            -> Bool
442+            -> Bool
443+            -> SockAddr
444+            -> IO (Maybe HostName, Maybe ServiceName)
445+
446+getNameInfo flags doHost doService addr =
447+  withCStringIf doHost (#const NI_MAXHOST) $ \c_hostlen c_host ->
448+    withCStringIf doService (#const NI_MAXSERV) $ \c_servlen c_serv -> do
449+      withSockAddr addr $ \ptr_addr sz -> do
450+        ret <- c_getnameinfo ptr_addr (fromIntegral sz) c_host c_hostlen
451+                             c_serv c_servlen flags
452+        case ret of
453+          0 -> do
454+            let peekIf doIf c_val = if doIf
455+                                     then liftM Just $ peekCString c_val
456+                                     else return Nothing
457+            host <- peekIf doHost c_host
458+            serv <- peekIf doService c_serv
459+            return (host, serv)
460+          _ -> do err <- gai_strerror ret
461+                  ioError (IOError Nothing NoSuchThing "getNameInfo" err
462+                           Nothing)
463+
464+foreign import ccall safe "getnameinfo"
465+    c_getnameinfo :: Ptr SockAddr -> CInt{-CSockLen???-} -> CString -> CSize -> CString
466+                  -> CSize -> NameInfoFlags -> IO CInt
467+#endif
468hunk ./configure.ac 37
469+dnl --------------------------------------------------
470+dnl * test for in6_addr as proxy for IPv6 support
471+dnl --------------------------------------------------
472+AC_MSG_CHECKING(for in6_addr in netinet/in.h)
473+AC_EGREP_HEADER(in6_addr, netinet/in.h,
474+ [ AC_DEFINE([HAVE_IN6_ADDR], [1], [Define to 1 if in6_addr is available.]) AC_MSG_RESULT(yes) ],
475+ AC_MSG_RESULT(no))
476+
477hunk ./include/HsNet.h 28
478+
479+#ifdef HAVE_IN6_ADDR
480+# define IPV6_SOCKET_SUPPORT 1
481+#else
482+# undef IPV6_SOCKET_SUPPORT
483+#endif
484hunk ./network.cabal 2
485-version:       2.0
486+version:       2.0.1
487}
488
489Context:
490
491[apply changes from patch "make this test deterministic on a multiprocessor" in testsuite
492Simon Marlow <simonmar@microsoft.com>**20070221142822]
493[tests for use with GHC's test framework, moved from the testsuite
494Simon Marlow <simonmar@microsoft.com>**20070221142258]
495[README about building from darcs
496Ross Paterson <ross@soi.city.ac.uk>**20070218110201]
497[don't install Typeable.h, fixes #1106
498Simon Marlow <simonmar@microsoft.com>**20070201125821]
499[network doesn't really need the html package
500Ian Lynagh <igloo@earth.li>**20070111122348]
501[includes -> install-includes
502Ross Paterson <ross@soi.city.ac.uk>**20060829123744]
503[change title to Haskell Hierarchical Libraries
504Ross Paterson <ross@soi.city.ac.uk>**20060827131348]
505[exclude Setup.hs from build
506Ross Paterson <ross@soi.city.ac.uk>**20060824183533]
507[add boilerplate Setup.hs
508Ross Paterson <ross@soi.city.ac.uk>**20060824115019]
509[Update Network.BSD header to not mention symlinks.
510Ian Lynagh <igloo@earth.li>**20060823184314]
511[Removed Network.CGI from the network package. A backwards-compatible new Network.CGI is available in the cgi package.
512bringert@cs.chalmers.se**20060814095652]
513[remove deprecated Network.BSD.{readlink,symlink}
514Simon Marlow <simonmar@microsoft.com>**20060811153755]
515[bump version to 2.0
516Simon Marlow <simonmar@microsoft.com>**20060811152554]
517[depend on html (for the time being)
518Simon Marlow <simonmar@microsoft.com>**20060810121424]
519[initWinSock(): have defn match proto
520sof@galois.com**20060613224903]
521[only GHC has rtsSupportsBoundThreads
522Ross Paterson <ross@soi.city.ac.uk>**20060531200123]
523[add files used by configure
524Ross Paterson <ross@soi.city.ac.uk>**20060518174356]
525[Import unsafePerformIO
526Sven Panne <sven.panne@aedion.de>**20060507171832]
527[Import MVar type
528Sven Panne <sven.panne@aedion.de>**20060507170308]
529[Add various address families
530Simon Marlow <simonmar@microsoft.com>**20060503081915]
531[Fix for #265, build problem on AIX
532Simon Marlow <simonmar@microsoft.com>**20060316143337
533 Not the fix from the ticket, but this one at least doesn't require
534 modifying the configure script.
535]
536[workaround for non-thread-safety of some functions in Network.BSD
537Simon Marlow <simonmar@microsoft.com>**20060126153014
538 Various functions in Network.BSD are non-thread-safe,
539 eg. getHostByName, because the underlying gethostbyname() provided by
540 the C library uses static storage.  The workaround here is to use a
541 giant lock around these functions.
542 
543 In some cases, even the API we provide is itself unsafe, relying on
544 implicit state (eg. getHostEntry), but this commit makes no attempt to
545 fix that.  We should deprecate this library in favour of a complete
546 replacement at some point (before 6.6 would be nice).
547 
548 Thanks to Einar Kartunnen for the patch.
549]
550[Fix Ticket 647, Socket bug on Mac OS X
551wolfgang.thaller@gmx.net**20060121050509
552 Patch kindly provided by Greg Wright
553]
554[TAG Initial conversion from CVS complete
555John Goerzen <jgoerzen@complete.org>**20060112154134]
556Patch bundle hash:
557673d24fc3164ee9f96aa2f3a25829d32fc731d28