def setNetworkBan(self, cidr, on_off): # See if this is a valid 1.2.3.4/5 CIDR string. try: ipmask = ipv4.CidrStringToIPMask(cidr) except ValueError, e: LOG.error("Bad CIDR string: %s") return
def handleCmd_ENDBURST(self, prefix, args): if self.ism.syncd: # FIXME LOG.warning("Ignoring ENDBURST") return CHECK(self.server_name) LOG.info("Finished receiving IRC sync data.") self.showirc = True # Check for conflicting bridges. if self.ism.findConflictingBridge(): LOG.error("My nick prefix is in use! Terminating.") self.transport.loseConnection() reactor.stop() return # Set up nick reservation scfg = getServiceConfig() self.ism.killConflictingUsers() self.sendLine(":%s ADDLINE Q %s* %s %d 0 :%s" % (self.sid, cfg.dc_to_irc_prefix, scfg.my_host, time.time(), self.qline_reason)) # Send my own bridge nick self.pushBotJoin(do_nick=True) # When we enter the syncd state, register this instance with Dtella. # This will eventually trigger event_DtellaUp, where we send our state. self.schedulePing() self.ism.addMeToMain()
def handleCmd_EOS(self, prefix, args): if prefix != self.server_name: return if self.ism.syncd: return LOG.info("Finished receiving IRC sync data.") self.showirc = True # Check for conflicting bridges. if self.ism.findConflictingBridge(): LOG.error("My nick prefix is in use! Terminating.") self.transport.loseConnection() reactor.stop() return # Set up nick reservation scfg = getServiceConfig() self.sendLine("TKL + Q * %s* %s 0 %d :Reserved for Dtella" % (cfg.dc_to_irc_prefix, scfg.my_host, time.time())) self.ism.killConflictingUsers() # Send my own bridge nick self.pushBotJoin(do_nick=True) # When we enter the syncd state, register this instance with Dtella. # This will eventually trigger event_DtellaUp, where we send our state. self.schedulePing() self.ism.addMeToMain()
def handleCmd_ENDBURST(self, prefix, args): if self.ism.syncd: # FIXME LOG.warning("Ignoring ENDBURST") return CHECK(self.server_name) LOG.info("Finished receiving IRC sync data.") self.showirc = True # Check for conflicting bridges. if self.ism.findConflictingBridge(): LOG.error("My nick prefix is in use! Terminating.") self.transport.loseConnection() reactor.stop() return # Set up nick reservation scfg = getServiceConfig() self.ism.killConflictingUsers() self.sendLine( ":%s ADDLINE Q %s* %s %d 0 :%s" % (self.sid, cfg.dc_to_irc_prefix, scfg.my_host, time.time(), self.qline_reason)) # Send my own bridge nick self.pushBotJoin(do_nick=True) # When we enter the syncd state, register this instance with Dtella. # This will eventually trigger event_DtellaUp, where we send our state. self.schedulePing() self.ism.addMeToMain()
def handleCmd_TKL(self, prefix, args): addrem = args[0] kind = args[1] if addrem == '+': on_off = True elif addrem == '-': on_off = False else: LOG.error("TKL: invalid modifier: '%s'" % addrem) return # :irc1.dhirc.com TKL + Z * 128.10.12.0/24 [email protected] 0 1171427130 :no reason if kind == 'Z' and args[2] == '*': cidr = args[3] self.ism.setNetworkBan(cidr, on_off) # :%s TKL + Q * %s* %s 0 %d :Reserved for Dtella elif kind == 'Q': nickmask = args[3] if on_off: reason = args[-1] self.ism.addQLine(nickmask, reason) else: self.ism.removeQLine(nickmask)
def handleCmd_EOS(self, prefix, args): if prefix != self.server_name: return if self.ism.syncd: return LOG.info("Finished receiving IRC sync data.") self.showirc = True # Check for conflicting bridges. if self.ism.findConflictingBridge(): LOG.error("My nick prefix is in use! Terminating.") self.transport.loseConnection() reactor.stop() return # Set up nick reservation scfg = getServiceConfig() self.sendLine( "TKL + Q * %s* %s 0 %d :Reserved for Dtella" % (cfg.dc_to_irc_prefix, scfg.my_host, time.time())) self.ism.killConflictingUsers() # Send my own bridge nick self.pushBotJoin(do_nick=True) # When we enter the syncd state, register this instance with Dtella. # This will eventually trigger event_DtellaUp, where we send our state. self.schedulePing() self.ism.addMeToMain()
def removeQLine(self, nickmask): self.qlines.pop(nickmask, None) LOG.info("Removed Q-line: " + nickmask) # If some other bridge removes our reservation, abort. if self.syncd and (nickmask == cfg.dc_to_irc_prefix + "*"): LOG.error("My own Q-line was removed! Terminating.") if self.ircs: self.ircs.transport.loseConnection() reactor.stop()
def cb(): self.ping_dcall = None if self.ping_waiting: LOG.error("Ping timeout!") self.transport.loseConnection() else: scfg = getServiceConfig() self.sendLine("PING :%s" % scfg.my_host) self.ping_waiting = True self.ping_dcall = reactor.callLater(60.0, cb)
def startConnecting(self): udp_state = self.ph.getSocketState() if udp_state == 'dead': try: reactor.listenUDP(cfg.udp_port, self.ph) except twisted.internet.error.BindError: LOG.error("Failed to bind UDP port!") raise SystemExit elif udp_state == 'dying': return CHECK(self.ph.getSocketState() == 'alive') self.startInitialContact()
def cb(first): try: reactor.listenTCP(dc_port, dfactory, interface='127.0.0.1') except twisted.internet.error.CannotListenError: if first: LOG.warning("TCP bind failed. Killing old process...") if terminate(dc_port): LOG.info("Ok. Sleeping...") reactor.callLater(2.0, cb, False) else: LOG.error("Kill failed. Giving up.") reactor.stop() else: LOG.error("Bind failed again. Giving up.") reactor.stop() else: LOG.info("Listening on 127.0.0.1:%d" % dc_port) dtMain.startConnecting()
def joinChannel(self, u): if u in self.chanusers: LOG.error("joinChannel: %r already in channel." % u) return self.chanusers.add(u) u.chanmodes.clear() try: osm = self.getOnlineStateManager() except NotOnline: return scfg = getServiceConfig() infoindex = scfg.chan_umodes.getUserInfoIndex(u) chunks = [] osm.bsm.addNickChunk(chunks, irc_to_dc(u.inick), infoindex) osm.bsm.sendBridgeChange(chunks)
def joinChannel(self, u): if u in self.chanusers: LOG.error("joinChannel: %r already in channel." % u) return self.chanusers.add(u) u.chanmodes.clear() try: osm = self.getOnlineStateManager() except NotOnline: return scfg = getServiceConfig() infoindex = scfg.chan_umodes.getUserInfoIndex(u) chunks = [] osm.bsm.addNickChunk( chunks, irc_to_dc(u.inick), infoindex) osm.bsm.sendBridgeChange(chunks)
def setChannelUserModes(self, whoset, u, changes): # changes: dict of {mode -> on_off} if u not in self.chanusers: LOG.error("setChannelUserModes: %r not in channel." % u) return scfg = getServiceConfig() # Save old index, apply changes, and get new index. old_infoindex = scfg.chan_umodes.getUserInfoIndex(u) for mode, on_off in changes.iteritems(): if on_off: u.chanmodes.add(mode) else: u.chanmodes.discard(mode) new_infoindex = scfg.chan_umodes.getUserInfoIndex(u) try: osm = self.getOnlineStateManager() except NotOnline: return chunks = [] if new_infoindex == old_infoindex: friendly_change = "well that was pointless" else: friendly_change = "%s -> %s" % ( scfg.chan_umodes.friendly[old_infoindex], scfg.chan_umodes.friendly[new_infoindex]) osm.bsm.addNickChunk( chunks, irc_to_dc(u.inick), new_infoindex) osm.bsm.addChatChunk( chunks, cfg.irc_to_dc_bot, "%s set mode %s for %s: %s" % ( irc_to_dc(whoset), self.formatChannelUserModes(changes), irc_to_dc(u.inick), friendly_change)) osm.bsm.sendBridgeChange(chunks)
def handleCmd_SQUIT(self, prefix, args): # :n.dhirc.com SQUIT remote.dtella.org :Remote host closed hostname = args[0] try: # All servers have been registered as users. sid = self.ism.findUser(hostname).uuid.lower() if len(sid) != 3: raise KeyError except KeyError: LOG.error("SQUIT: unknown server: %s" % hostname) return # Find all the users belonging to this server. This should # never match nicks, because only UUIDs start with a number. remove_uuids = [uuid for uuid in self.ism.users if uuid.startswith(sid)] # Drop them off the network. for uuid in remove_uuids: LOG.info("SQUIT: removing user: %s" % uuid) self.ism.removeUser(self.ism.findUser(uuid))
def handleCmd_SQUIT(self, prefix, args): # :n.dhirc.com SQUIT remote.dtella.org :Remote host closed hostname = args[0] try: # All servers have been registered as users. sid = self.ism.findUser(hostname).uuid.lower() if len(sid) != 3: raise KeyError except KeyError: LOG.error("SQUIT: unknown server: %s" % hostname) return # Find all the users belonging to this server. This should # never match nicks, because only UUIDs start with a number. remove_uuids = [ uuid for uuid in self.ism.users if uuid.startswith(sid) ] # Drop them off the network. for uuid in remove_uuids: LOG.info("SQUIT: removing user: %s" % uuid) self.ism.removeUser(self.ism.findUser(uuid))
def setChannelUserModes(self, whoset, u, changes): # changes: dict of {mode -> on_off} if u not in self.chanusers: LOG.error("setChannelUserModes: %r not in channel." % u) return scfg = getServiceConfig() # Save old index, apply changes, and get new index. old_infoindex = scfg.chan_umodes.getUserInfoIndex(u) for mode, on_off in changes.iteritems(): if on_off: u.chanmodes.add(mode) else: u.chanmodes.discard(mode) new_infoindex = scfg.chan_umodes.getUserInfoIndex(u) try: osm = self.getOnlineStateManager() except NotOnline: return chunks = [] if new_infoindex == old_infoindex: friendly_change = "well that was pointless" else: friendly_change = "%s -> %s" % ( scfg.chan_umodes.friendly[old_infoindex], scfg.chan_umodes.friendly[new_infoindex]) osm.bsm.addNickChunk(chunks, irc_to_dc(u.inick), new_infoindex) osm.bsm.addChatChunk( chunks, cfg.irc_to_dc_bot, "%s set mode %s for %s: %s" % (irc_to_dc(whoset), self.formatChannelUserModes(changes), irc_to_dc(u.inick), friendly_change)) osm.bsm.sendBridgeChange(chunks)
def handleCmd_ADDLINE(self, prefix, args): kind = args[0] # :268 ADDLINE Z 69.69.69.69 <Config> 1237917434 0 :hello if kind == 'Z': cidr = args[1] self.ism.setNetworkBan(cidr, True) # :268 ADDLINE Q [P]* setter 1238300707 0 :Reserved elif kind == 'Q': nickmask = args[1] timeset = int(args[3]) reason = args[-1] if self.ism.syncd and nickmask == cfg.dc_to_irc_prefix + "*": # If reason matches, it's a self-echo. Otherwise, someone # took my prefix first. if reason != self.qline_reason: LOG.error("Someone stole my Q-line! Terminating.") self.transport.loseConnection() reactor.stop() return self.ism.addQLine(nickmask, reason)
def handleCmd_MODE(self, prefix, args): # :Paul MODE #dtella +vv aaahhh Big_Guy whoset = prefix chan = args[0] change = args[1] nicks = args[2:] scfg = getServiceConfig() if chan != scfg.channel: return on_off = True i = 0 # User() -> {mode -> on_off} user_changes = {} # Dtella node modes that need unsetting. unset_modes = [] unset_nicks = [] for c in change: if c == '+': on_off = True elif c == '-': on_off = False elif c == 't': self.ism.setTopicLocked(whoset, on_off) elif c == 'm': self.ism.setModerated(whoset, on_off) elif c == 'k': # Skip over channel key i += 1 elif c == 'l': # Skip over channel user limit i += 1 elif c == 'b': banmask = nicks[i] i += 1 self.ism.setChannelBan(whoset, on_off, banmask) elif c in scfg.chan_umodes.modes: # Grab affected nick nick = nicks[i] i += 1 n = self.ism.findDtellaNode(inick=nick) if n: # If someone set a mode for a Dt node, unset it. if on_off: unset_modes.append(c) unset_nicks.append(nick) continue # Get the IRC user we're modifying. try: u = self.ism.findUser(nick) except KeyError: LOG.error("MODE: unknown nick: %s" % nick) continue # Schedule a mode change for this user. user_changes.setdefault(u, {})[c] = on_off # Undo mode changes for Dtella nodes. if unset_modes: self.sendLine( ":%s MODE %s -%s %s" % ( self.ism.bot_user.inick, scfg.channel, ''.join(unset_modes), ' '.join(unset_nicks))) # Send IRC user mode changes to Dtella for u, changes in user_changes.iteritems(): self.ism.setChannelUserModes(whoset, u, changes)
def handleCmd_BURST(self, prefix, args): return #FIXME if self.ism.syncd: LOG.error("Can't handle BURST after sync. Restarting.") self.transport.loseConnection()
def handleCmd_FMODE(self, prefix, args): # :268AAAAAF FMODE #dtella 1238298761 +h-o 268AAAAAF 268AAAAAF whoset_uuid = prefix chan = args[0] change = args[2] margs = args[3:] scfg = getServiceConfig() if chan != scfg.channel: return try: whoset = self.ism.findUser(whoset_uuid).inick except KeyError: # Could be a server? whoset = "" on_off = True i = 0 # User() -> {mode -> on_off} user_changes = {} # Dtella node modes that need unsetting. unset_modes = [] unset_uuids = [] for c in change: if c == '+': on_off = True elif c == '-': on_off = False elif c == 't': self.ism.setTopicLocked(whoset, on_off) elif c == 'm': self.ism.setModerated(whoset, on_off) elif c == 'k': # Skip over channel key i += 1 elif c == 'l': # Skip over channel user limit i += 1 elif c == 'b': banmask = margs[i] i += 1 self.ism.setChannelBan(whoset, on_off, banmask) elif c in scfg.chan_umodes.modes: # Grab affected user uuid = margs[i] i += 1 n = self.ism.findDtellaNode(inick=uuid) if n: # If someone set a mode for a Dt node, unset it. if on_off: unset_modes.append(c) unset_uuids.append(uuid) continue # Get the IRC user we're modifying. try: u = self.ism.findUser(uuid) except KeyError: LOG.error("MODE: unknown user: %s" % uuid) continue # Schedule a mode change for this user. user_changes.setdefault(u, {})[c] = on_off # Undo mode changes for Dtella nodes. if unset_modes: self.sendLine( ":%s FMODE %s %d -%s %s" % (self.ism.bot_user.uuid, scfg.channel, self.chan_time, ''.join(unset_modes), ' '.join(unset_uuids))) # Send IRC user mode changes to Dtella for u, changes in user_changes.iteritems(): self.ism.setChannelUserModes(whoset, u, changes)
def handleCmd_MODE(self, prefix, args): # :Paul MODE #dtella +vv aaahhh Big_Guy whoset = prefix chan = args[0] change = args[1] nicks = args[2:] scfg = getServiceConfig() if chan != scfg.channel: return on_off = True i = 0 # User() -> {mode -> on_off} user_changes = {} # Dtella node modes that need unsetting. unset_modes = [] unset_nicks = [] for c in change: if c == '+': on_off = True elif c == '-': on_off = False elif c == 't': self.ism.setTopicLocked(whoset, on_off) elif c == 'm': self.ism.setModerated(whoset, on_off) elif c == 'k': # Skip over channel key i += 1 elif c == 'l': # Skip over channel user limit i += 1 elif c == 'b': banmask = nicks[i] i += 1 self.ism.setChannelBan(whoset, on_off, banmask) elif c in scfg.chan_umodes.modes: # Grab affected nick nick = nicks[i] i += 1 n = self.ism.findDtellaNode(inick=nick) if n: # If someone set a mode for a Dt node, unset it. if on_off: unset_modes.append(c) unset_nicks.append(nick) continue # Get the IRC user we're modifying. try: u = self.ism.findUser(nick) except KeyError: LOG.error("MODE: unknown nick: %s" % nick) continue # Schedule a mode change for this user. user_changes.setdefault(u, {})[c] = on_off # Undo mode changes for Dtella nodes. if unset_modes: self.sendLine(":%s MODE %s -%s %s" % (self.ism.bot_user.inick, scfg.channel, ''.join(unset_modes), ' '.join(unset_nicks))) # Send IRC user mode changes to Dtella for u, changes in user_changes.iteritems(): self.ism.setChannelUserModes(whoset, u, changes)