diff options
Diffstat (limited to 'xmppServer.hs')
-rw-r--r-- | xmppServer.hs | 119 |
1 files changed, 100 insertions, 19 deletions
diff --git a/xmppServer.hs b/xmppServer.hs index 69b6cec5..7b352b8a 100644 --- a/xmppServer.hs +++ b/xmppServer.hs | |||
@@ -10,7 +10,7 @@ import Network.Socket | |||
10 | , getAddrInfo | 10 | , getAddrInfo |
11 | , defaultHints | 11 | , defaultHints |
12 | , addrFlags | 12 | , addrFlags |
13 | , AddrInfoFlag(AI_CANONNAME,AI_V4MAPPED) | 13 | , AddrInfoFlag(AI_CANONNAME,AI_V4MAPPED,AI_NUMERICHOST) |
14 | , SockAddr(..) | 14 | , SockAddr(..) |
15 | ) | 15 | ) |
16 | import System.Endian (fromBE32) | 16 | import System.Endian (fromBE32) |
@@ -71,9 +71,10 @@ data ClientState = ClientState | |||
71 | 71 | ||
72 | data PresenceState = PresenceState | 72 | data PresenceState = PresenceState |
73 | { clients :: TVar (Map ConnectionKey ClientState) | 73 | { clients :: TVar (Map ConnectionKey ClientState) |
74 | , clientsByUser :: TVar (Map Text (ConnectionKey,ClientState)) -- TODO: should be list | ||
74 | , associatedPeers :: TVar (Map SockAddr ()) | 75 | , associatedPeers :: TVar (Map SockAddr ()) |
75 | , server :: TMVar XMPPServer | 76 | , server :: TMVar XMPPServer |
76 | , writeTos :: TVar (Map ConnectionKey (TChan Stanza)) | 77 | , writeTos :: TVar (Map ConnectionKey Conn) |
77 | } | 78 | } |
78 | 79 | ||
79 | 80 | ||
@@ -84,9 +85,14 @@ resolvePeer :: Text -> IO [SockAddr] | |||
84 | resolvePeer addrtext = do | 85 | resolvePeer addrtext = do |
85 | fmap (map $ make6mapped4 . addrAddress) $ | 86 | fmap (map $ make6mapped4 . addrAddress) $ |
86 | getAddrInfo (Just $ defaultHints { addrFlags = [ AI_CANONNAME, AI_V4MAPPED ]}) | 87 | getAddrInfo (Just $ defaultHints { addrFlags = [ AI_CANONNAME, AI_V4MAPPED ]}) |
87 | (Just $ Text.unpack addrtext) | 88 | (Just $ Text.unpack $ strip_brackets addrtext) |
88 | (Just "5269") | 89 | (Just "5269") |
89 | 90 | ||
91 | strip_brackets s = | ||
92 | case Text.uncons s of | ||
93 | Just ('[',t) -> Text.takeWhile (/=']') t | ||
94 | _ -> s | ||
95 | |||
90 | 96 | ||
91 | getConsolePids :: PresenceState -> IO [(Text,ProcessID)] | 97 | getConsolePids :: PresenceState -> IO [(Text,ProcessID)] |
92 | getConsolePids state = do | 98 | getConsolePids state = do |
@@ -109,8 +115,10 @@ chooseResourceName state k addr desired = do | |||
109 | , clientUser = user | 115 | , clientUser = user |
110 | , clientPid = pid } | 116 | , clientPid = pid } |
111 | 117 | ||
112 | atomically $ | 118 | atomically $ do |
113 | modifyTVar' (clients state) $ Map.insert k client | 119 | modifyTVar' (clients state) $ Map.insert k client |
120 | modifyTVar' (clientsByUser state) $ Map.insert (clientUser client) (k,client) | ||
121 | |||
114 | localJID (clientUser client) (clientResource client) | 122 | localJID (clientUser client) (clientResource client) |
115 | 123 | ||
116 | where | 124 | where |
@@ -174,11 +182,58 @@ rosterGetSolicited = rosterGetStuff ConfigFiles.getSolicited | |||
174 | rosterGetOthers = rosterGetStuff ConfigFiles.getOthers | 182 | rosterGetOthers = rosterGetStuff ConfigFiles.getOthers |
175 | rosterGetSubscribers = rosterGetStuff ConfigFiles.getSubscribers | 183 | rosterGetSubscribers = rosterGetStuff ConfigFiles.getSubscribers |
176 | 184 | ||
177 | newConn state k outchan = | 185 | data Conn = Conn { connChan :: TChan Stanza |
178 | atomically $ modifyTVar' (writeTos state) $ Map.insert k outchan | 186 | , auxAddr :: SockAddr } |
187 | |||
188 | newConn state k addr outchan = | ||
189 | atomically $ modifyTVar' (writeTos state) | ||
190 | $ Map.insert k Conn { connChan = outchan | ||
191 | , auxAddr = addr } | ||
179 | 192 | ||
180 | eofConn state k = atomically $ modifyTVar' (writeTos state) $ Map.delete k | 193 | eofConn state k = atomically $ modifyTVar' (writeTos state) $ Map.delete k |
181 | 194 | ||
195 | rewriteJIDForClient1:: Text -> IO (Maybe ((Maybe Text,Text,Maybe Text),SockAddr)) | ||
196 | rewriteJIDForClient1 jid = do | ||
197 | let (n,h,r) = splitJID jid | ||
198 | maddr <- fmap listToMaybe $ resolvePeer h | ||
199 | flip (maybe $ return Nothing) maddr $ \addr -> do | ||
200 | h' <- peerKeyToResolvedName (PeerKey addr) | ||
201 | return $ Just ((n,h',r), addr) | ||
202 | |||
203 | parseAddress :: Text -> IO (Maybe SockAddr) | ||
204 | parseAddress addr_str = do | ||
205 | info <- getAddrInfo (Just $ defaultHints { addrFlags = [ AI_NUMERICHOST ] }) | ||
206 | (Just . Text.unpack $ addr_str) | ||
207 | (Just "0") | ||
208 | return . listToMaybe $ map addrAddress info | ||
209 | |||
210 | todo = error "Unimplemented" | ||
211 | |||
212 | |||
213 | -- | for example: 2001-db8-85a3-8d3-1319-8a2e-370-7348.ipv6-literal.net | ||
214 | ip6literal :: Text -> Text | ||
215 | ip6literal addr = Text.map dash addr <> ".ipv6-literal.net" | ||
216 | where | ||
217 | dash ':' = '-' | ||
218 | dash x = x | ||
219 | |||
220 | withPort (SockAddrInet _ a) port = SockAddrInet (toEnum port) a | ||
221 | withPort (SockAddrInet6 _ a b c) port = SockAddrInet6 (toEnum port) a b c | ||
222 | |||
223 | -- | The given address is taken to be the local address for the socket this JID | ||
224 | -- came in on. The returned JID parts are suitable for unsplitJID to create a | ||
225 | -- valid JID for communicating to a client. The returned Bool is True when the | ||
226 | -- host part refers to this local host (i.e. it equals the given SockAddr). | ||
227 | rewriteJIDForClient :: SockAddr -> Text -> IO (Bool,(Maybe Text,Text,Maybe Text)) | ||
228 | rewriteJIDForClient laddr jid = do | ||
229 | let (n,h,r) = splitJID jid | ||
230 | maddr <- parseAddress (strip_brackets h) | ||
231 | flip (maybe $ return (False,(n,ip6literal h,r))) maddr $ \addr -> do | ||
232 | let mine = laddr `withPort` 0 == addr `withPort` 0 | ||
233 | h' <- if mine then textHostName | ||
234 | else peerKeyToResolvedName (PeerKey addr) | ||
235 | return (mine,(n,h',r)) | ||
236 | |||
182 | rewriteJIDForPeer :: Text -> IO (Maybe (Text,SockAddr)) | 237 | rewriteJIDForPeer :: Text -> IO (Maybe (Text,SockAddr)) |
183 | rewriteJIDForPeer jid = do | 238 | rewriteJIDForPeer jid = do |
184 | let (n,h,r) = splitJID jid | 239 | let (n,h,r) = splitJID jid |
@@ -188,28 +243,54 @@ rewriteJIDForPeer jid = do | |||
188 | to' = unsplitJID (n,h',r) | 243 | to' = unsplitJID (n,h',r) |
189 | in (to',addr) | 244 | in (to',addr) |
190 | 245 | ||
191 | deliverMessage state fail msg = do | 246 | deliverMessage state fail msg = |
192 | mto <- do | 247 | case stanzaOrigin msg of |
193 | flip (maybe $ return Nothing) (stanzaTo msg) $ \to -> do | 248 | NetworkOrigin senderk@(ClientKey {}) _ -> do |
194 | rewriteJIDForPeer to | 249 | mto <- do |
195 | flip (maybe fail) mto $ \(to',addr) -> do | 250 | flip (maybe $ return Nothing) (stanzaTo msg) $ \to -> do |
196 | from' <- do | 251 | rewriteJIDForPeer to |
197 | flip (maybe $ return Nothing) (stanzaFrom msg) $ \from -> do | 252 | flip (maybe fail) mto $ \(to',addr) -> do |
198 | m <- rewriteJIDForPeer from | 253 | let k = PeerKey addr |
199 | return $ fmap fst m | 254 | chans <- atomically $ readTVar (writeTos state) |
200 | let k = PeerKey addr | 255 | flip (maybe fail) (Map.lookup k chans) $ \(Conn { connChan=chan |
201 | chans <- atomically $ readTVar (writeTos state) | 256 | , auxAddr=laddr }) -> do |
202 | flip (maybe fail) (Map.lookup k chans) $ \chan -> do | 257 | (n,r) <- forClient state senderk (return (Nothing,Nothing)) |
203 | sendModifiedStanzaToPeer (msg { stanzaTo=Just to', stanzaFrom=from' }) chan | 258 | $ \c -> return (Just (clientUser c), Just (clientResource c)) |
259 | -- original 'from' address is discarded. | ||
260 | let from' = unsplitJID (n,addrToText laddr,r) | ||
261 | sendModifiedStanzaToPeer (msg { stanzaTo=Just to', stanzaFrom=Just from' }) chan | ||
262 | NetworkOrigin senderk@(PeerKey {}) _ -> do | ||
263 | chans <- atomically $ readTVar (writeTos state) | ||
264 | flip (maybe fail) (Map.lookup senderk chans) | ||
265 | $ \(Conn { connChan=sender_chan | ||
266 | , auxAddr=laddr }) -> do | ||
267 | flip (maybe fail) (stanzaTo msg) $ \to -> do | ||
268 | (mine,(n,h,r)) <- rewriteJIDForClient laddr to | ||
269 | if not mine then fail else do | ||
270 | let to' = unsplitJID (n,h,r) | ||
271 | from' <- do | ||
272 | flip (maybe $ return Nothing) (stanzaFrom msg) $ \from -> do | ||
273 | (_,trip) <- rewriteJIDForClient laddr from | ||
274 | return . Just $ unsplitJID trip | ||
275 | cmap <- atomically . readTVar $ clientsByUser state | ||
276 | flip (maybe fail) n $ \n -> do | ||
277 | flip (maybe fail) (Map.lookup n cmap) $ \(k,_) -> do | ||
278 | flip (maybe fail) (Map.lookup k chans) $ \Conn { connChan=chan} -> do | ||
279 | sendModifiedStanzaToClient (msg { stanzaTo=Just to' | ||
280 | , stanzaFrom=from' }) | ||
281 | chan | ||
282 | |||
204 | 283 | ||
205 | main = runResourceT $ do | 284 | main = runResourceT $ do |
206 | state <- liftIO . atomically $ do | 285 | state <- liftIO . atomically $ do |
207 | clients <- newTVar Map.empty | 286 | clients <- newTVar Map.empty |
287 | clientsByUser <- newTVar Map.empty | ||
208 | associatedPeers <- newTVar Map.empty | 288 | associatedPeers <- newTVar Map.empty |
209 | xmpp <- newEmptyTMVar | 289 | xmpp <- newEmptyTMVar |
210 | writeTos <- newTVar Map.empty | 290 | writeTos <- newTVar Map.empty |
211 | return PresenceState | 291 | return PresenceState |
212 | { clients = clients | 292 | { clients = clients |
293 | , clientsByUser = clientsByUser | ||
213 | , associatedPeers = associatedPeers | 294 | , associatedPeers = associatedPeers |
214 | , writeTos = writeTos | 295 | , writeTos = writeTos |
215 | , server = xmpp | 296 | , server = xmpp |