def ping(self, intervals): timeout = self.factory.servconfig["client_timeout_delay"] + self.factory.servconfig["client_ping_interval"] * (intervals - 1) if (now() - self.type.lastpong).total_seconds() > timeout: log.msg("Client has stopped responding to PING and is now disconnecting.") self.transport.loseConnection() self.connectionLost(None) elif self.type.lastactivity > self.type.lastpong: self.type.lastpong = now() else: self.sendMessage("PING",":{}".format(self.factory.name))
def execute(self, server, data): if "lostserver" in data: return True if data["dest"] == self.ircd.serverID: if data["source"] == server.serverID: server.cache["pongtime"] = now() else: self.ircd.servers[data["source"]].cache["pongtime"] = now() return True self.ircd.servers[data["dest"]].sendMessage("PONG", data["source"], data["dest"], prefix=data["prefix"]) return True
def pingServer(self, server): if "pingtime" not in server.cache or "pongtime" not in server.cache: server.cache["pingtime"] = now() server.cache["pongtime"] = now() pingTime = server.cache["pingtime"] pongTime = server.cache["pongtime"] if pongTime < pingTime: self.ircd.log.debug("Server {server.serverID} pinged out (last pong time '{pongTime}' was less than last ping time '{pingTime}' at the next ping interval)", server=server, pongTime=pongTime, pingTime=pingTime) server.disconnect("Ping timeout") return server.sendMessage("PING", self.ircd.serverID, server.serverID, prefix=self.ircd.serverID) server.cache["pingtime"] = now()
def __init__(self, ircd, name): if not isValidChannelName(name): raise InvalidChannelNameError self.ircd = ircd self.name = name[:self.ircd.config.get("channel_name_length", 64)] self.users = WeakKeyDictionary() self.modes = {} self.existedSince = now() self.topic = "" self.topicSetter = "" self.topicTime = now() self._metadata = CaseInsensitiveDictionary() self.cache = {}
def ChannelFactory(self, name): logfile = "{}/{}".format(self.app_log_dir, irc_lower(name)) if not os.path.exists(logfile): os.makedirs(logfile) c = Channel( name, now(), {"message": None, "author": "", "created": now()}, CaseInsensitiveDictionary(), ChannelModes(self, None), DailyLogFile("log", logfile), ) c.mode.parent = c c.mode.combine("nt", [], name) return c
def execute(self, user, data): if "server" in data: server = data["server"] server.sendMessage("USERTIMEREQ", server.serverID, prefix=user.uuid) else: user.sendMessage(irc.RPL_TIME, self.ircd.name, str(now())) return True
def pingUser(self, user): if "pingtime" not in user.cache or "pongtime" not in user.cache: user.cache["pingtime"] = now() user.cache["pongtime"] = now() pingTime = user.cache["pingtime"] pongTime = user.cache["pongtime"] if pongTime < pingTime: self.ircd.log.debug("User {user.uuid} pinged out (last pong time '{pongTime}' was less than last ping time '{pingTime}' at the next ping interval)", user=user, pongTime=pongTime, pingTime=pingTime) user.disconnect("Ping timeout") return if user.idleSince > user.cache["pongtime"]: user.cache["pingtime"] = now() user.cache["pongtime"] = now() return user.sendMessage("PING", self.ircd.name, to=None, prefix=None) user.cache["pingtime"] = now()
def apply(self, actionName, channel, param, user, data): if "targetchans" not in data or channel not in data["targetchans"]: return if self.ircd.runActionUntilValue("checkexemptchanops", "chanflood", channel, user): return if "floodhistory" not in channel.users[user]: channel.users[user]["floodhistory"] = [] currentTime = now() channel.users[user]["floodhistory"].append((data["targetchans"][channel], currentTime)) maxLines, seconds = param.split(":") maxLines = int(maxLines) seconds = int(seconds) duration = timedelta(seconds=seconds) floodTime = currentTime - duration floodHistory = channel.users[user]["floodhistory"] while floodHistory: if floodHistory[0][1] <= floodTime: del floodHistory[0] else: break channel.users[user]["floodhistory"] = floodHistory if len(floodHistory) > maxLines: user.leaveChannel(channel, "KICK", { "byuser": False, "server": self.ircd, "reason": "Channel flood limit reached" })
def disconnect(self, reason): """ Disconnects the user from the server. """ self.ircd.log.debug("Disconnecting user {user.uuid} ({user.hostmask()}): {reason}", user=self, reason=reason) if self._pinger: if self._pinger.running: self._pinger.stop() self._pinger = None if self._registrationTimeoutTimer: if self._registrationTimeoutTimer.active(): self._registrationTimeoutTimer.cancel() self._registrationTimeoutTimer = None self.ircd.recentlyQuitUsers[self.uuid] = now() del self.ircd.users[self.uuid] if self.isRegistered(): del self.ircd.userNicks[self.nick] userSendList = [self] while self.channels: channel = self.channels[0] userSendList.extend(channel.users.keys()) self._leaveChannel(channel) userSendList = [u for u in set(userSendList) if u.uuid[:3] == self.ircd.serverID] userSendList.remove(self) self.ircd.runActionProcessing("quitmessage", userSendList, self, reason, users=[self] + userSendList) self.ircd.runActionStandard("quit", self, reason, users=self) self.transport.loseConnection()
def disconnect(self, reason): """ Disconnects the user from the server. """ self.ircd.log.debug("Disconnecting user {user.uuid} ({user.hostmask()}): {reason}", user=self, reason=reason) # Sometimes, actions deferred from initial connection may cause registration to occur after disconnection if # disconnection happens before registration completes. If the user is unregistered on disconnection, this prevents # the user from completing registration. self.addRegisterHold("QUIT") if self._pinger: if self._pinger.running: self._pinger.stop() self._pinger = None if self._registrationTimeoutTimer: if self._registrationTimeoutTimer.active(): self._registrationTimeoutTimer.cancel() self._registrationTimeoutTimer = None if self._connectHandlerTimer and self._connectHandlerTimer.active(): self._connectHandlerTimer.cancel() self._connectHandlerTimer = None self.ircd.recentlyQuitUsers[self.uuid] = now() del self.ircd.users[self.uuid] if self.isRegistered(): del self.ircd.userNicks[self.nick] userSendList = [self] while self.channels: channel = self.channels[0] userSendList.extend(channel.users.keys()) self._leaveChannel(channel) userSendList = [u for u in set(userSendList) if u.uuid[:3] == self.ircd.serverID] userSendList.remove(self) self.ircd.runActionProcessing("quitmessage", userSendList, self, reason, users=[self] + userSendList) self.ircd.runActionStandard("quit", self, reason, users=self) self.transport.loseConnection()
def execute(self, user, data): banmask = data["mask"] if "reason" in data: if not self.module.addLine(banmask, now(), data["duration"], user.hostmask(), data["reason"]): user.sendMessage( "NOTICE", "*** Q:Line for {} is already set.".format(banmask)) return True for checkUser in self.module.ircd.users.itervalues(): reason = self.module.matchUser(checkUser) if reason is not None: self.module.changeNick(checkUser, reason, True) if data["duration"] > 0: user.sendMessage( "NOTICE", "*** Timed q:line for {} has been set, to expire in {} seconds." .format(banmask, data["duration"])) else: user.sendMessage( "NOTICE", "*** Permanent q:line for {} has been set.".format( banmask)) return True if not self.module.delLine(banmask): user.sendMessage( "NOTICE", "*** Q:Line for {} doesn't exist.".format(banmask)) return True user.sendMessage("NOTICE", "*** Q:Line for {} has been removed.".format(banmask)) return True
def onUse(self, user, data): if "reason" in data: self.exceptList[data["mask"]] = { "setter": user.nickname, "created": epoch(now()), "duration": data["duration"], "reason": data["reason"] } user.sendMessage( "NOTICE", ":*** E:Line set on {}, to expire in {} seconds".format( data["mask"], data["duration"])) else: mask = data["mask"] del self.exceptList[mask] user.sendMessage("NOTICE", ":*** E:Line removed on {}".format(mask)) for u in self.ircd.users.itervalues(): if self.match_eline(u): u.cache["except_line"] = True now_banned = {} for uid, udata in self.ircd.users.iteritems(): for modfunc in self.ircd.actions["xline_rematch"]: reason = modfunc(udata) if reason: now_banned[uid] = reason break # If the user is banned, the user is banned. We don't need to gather a consensus or something. for uid, reason in now_banned.iteritems(): udata = self.ircd.users[uid] udata.sendMessage( "NOTICE", ":{}".format(self.ircd.servconfig["client_ban_msg"])) udata.disconnect( "Banned: Exception Removed ({})".format(reason))
def execute(self, user, data): banmask = data["mask"] if "reason" in data: if not self.module.addLine(banmask, now(), data["duration"], user.hostmask(), data["reason"]): user.sendMessage( "NOTICE", "*** E:Line for {} is already set.".format(banmask)) return True if data["duration"] > 0: user.sendMessage( "NOTICE", "*** Timed e:line for {} has been set, to expire in {} seconds." .format(banmask, data["duration"])) else: user.sendMessage( "NOTICE", "*** Permanent e:line for {} has been set.".format( banmask)) return True if not self.module.delLine(banmask): user.sendMessage( "NOTICE", "*** E:Line for {} doesn't exist.".format(banmask)) return True user.sendMessage("NOTICE", "*** E:Line for {} has been removed.".format(banmask)) return True
def getPeriodData(self): """Returns (period as integer, time to end of period)""" nowTS = timestamp(now()) interval = self.ircd.config["rate_interval"] period = int(nowTS / interval) timeToEnd = (period + 1) * interval - nowTS return period, timeToEnd
def execute(self, user, data): banmask = data["mask"] if "reason" in data: if not self.addLine(banmask, now(), data["duration"], user.hostmask(), data["reason"]): user.sendMessage( "NOTICE", "*** K:Line for {} is already set.".format(banmask)) return True badUsers = [] for checkUser in self.ircd.users.itervalues(): reason = self.matchUser(checkUser) if reason is not None: badUsers.append((checkUser, reason)) for badUser in badUsers: self.killUser(*badUser) if data["duration"] > 0: user.sendMessage( "NOTICE", "*** Timed k:line for {} has been set, to expire in {} seconds." .format(banmask, data["duration"])) else: user.sendMessage( "NOTICE", "*** Permanent k:line for {} has been set.".format( banmask)) return True if not self.delLine(banmask): user.sendMessage( "NOTICE", "*** K:Line for {} doesn't exist.".format(banmask)) return True user.sendMessage("NOTICE", "*** K:Line for {} has been removed.".format(banmask)) return True
def flush_stats(self): return users = {} countries = {} uptime = now() - self.created for u in self.users.itervalues(): users[u.nickname] = [u.latitude, u.longitude] if u.country not in countries: countries[u.country] = 0 countries[u.country] += 1 line = json.dumps( { "io": self.stats_data, "users": users, "countries": countries, "uptime": "{}".format(uptime if uptime.days > 0 else "0 days, {}".format(uptime)), }, separators=(",", ":"), ) self.stats_data["bytes_in"] = 0 self.stats_data["bytes_out"] = 0 self.stats_data["lines_in"] = 0 self.stats_data["lines_out"] = 0 # if not self.stats_log.closed: # self.stats_log.write(line+"\n") if self.stats: self.stats.broadcast(line + "\r\n")
def disconnect(self, reason, fromRemote=False): """ Disconnects the remote user from the remote server. """ if fromRemote: if self.isRegistered(): del self.ircd.userNicks[self.nick] self.ircd.recentlyQuitUsers[self.uuid] = now() del self.ircd.users[self.uuid] userSendList = [] while self.channels: channel = self.channels[0] userSendList.extend(channel.users.keys()) self._leaveChannel(channel) userSendList = [ u for u in set(userSendList) if u.uuid[:3] == self.ircd.serverID ] self.ircd.runActionProcessing("quitmessage", userSendList, self, reason, users=userSendList) self.ircd.runActionStandard("remotequit", self, reason, users=[self]) else: self.ircd.runActionUntilTrue("remotequitrequest", self, reason, users=[self])
def startService(self): self.log.info("Starting up...") self.startupTime = now() self.log.info("Loading configuration...") self.config.reload() self.name = self.config["server_name"] self.serverID = self.config["server_id"] self.log.info("Loading storage...") self.storage = shelve.open(self.config["datastore_path"], writeback=True) self.storageSyncer = LoopingCall(self.storage.sync) self.storageSyncer.start(self.config.get("storage_sync_interval", 5), now=False) self.log.info("Starting processes...") self.pruneRecentlyQuit = LoopingCall(self.pruneQuit) self.pruneRecentlyQuit.start(10, now=False) self.pruneRecentChannels = LoopingCall(self.pruneChannels) self.pruneRecentChannels.start(15, now=False) self.log.info("Loading modules...") self._loadModules() self.log.info("Binding ports...") self._bindPorts() self.log.info("txircd started!") try: self._logFilter.setLogLevelForNamespace("txircd", LogLevel.levelWithName(self.config["log_level"])) except (KeyError, InvalidLogLevelError): self._logFilter.setLogLevelForNamespace("txircd", LogLevel.warn) self.runActionStandard("startup")
def disconnect(self, reason, netsplitQuitMsg = None): """ Disconnects the server. """ if self.nextClosest == self.ircd.serverID: self.ircd.log.warn("Disconnecting server {server.name}: {reason}", server=self, reason=reason) else: self.ircd.log.warn("Removing server {server.name}: {reason}", server=self, reason=reason) self.ircd.runActionStandard("serverquit", self, reason) if self.serverID in self.ircd.servers: if netsplitQuitMsg is None: netsplitQuitMsg = "{} {}".format(self.ircd.servers[self.nextClosest].name if self.nextClosest in self.ircd.servers else self.ircd.name, self.name) allUsers = self.ircd.users.values() for user in allUsers: if user.uuid[:3] == self.serverID: user.disconnect(netsplitQuitMsg, True) allServers = self.ircd.servers.values() for server in allServers: if server.nextClosest == self.serverID: server.disconnect(reason, netsplitQuitMsg) self.ircd.recentlyQuitServers[self.serverID] = now() del self.ircd.servers[self.serverID] del self.ircd.serverNames[self.name] self.bursted = None if self._pinger.running: self._pinger.stop() if self._registrationTimeoutTimer.active(): self._registrationTimeoutTimer.cancel() self._endConnection()
def addUserToWhowas(self, user, reason): if not user.isRegistered(): # user never registered a nick, so no whowas entry to add return lowerNick = ircLower(user.nick) allWhowas = self.ircd.storage["whowas"] if lowerNick in allWhowas: whowasEntries = allWhowas[lowerNick] else: whowasEntries = [] serverName = self.ircd.name if user.uuid[:3] != self.ircd.serverID: serverName = self.ircd.servers[user.uuid[:3]].name whowasEntries.append({ "nick": user.nick, "ident": user.ident, "host": user.host(), "gecos": user.gecos, "server": serverName, "when": timestamp(now()) }) whowasEntries = self.removeOldEntries(whowasEntries) if whowasEntries: allWhowas[lowerNick] = whowasEntries elif lowerNick in allWhowas: del allWhowas[lowerNick]
def nick(self, newNick): newNick = str(newNick) if newNick in self.ircd.users: return del self.ircd.users[self.nickname] self.ircd.users[newNick] = self notify = set() notify.add(self) for cdata in self.ircd.channels.itervalues(): if self in cdata.users: for cuser in cdata.users.iterkeys(): notify.add(cuser) prefix = self.prefix() for u in notify: u.sendMessage("NICK", to=newNick, prefix=prefix) oldNick = self.nickname self.nickname = newNick self.nicktime = now() for server in self.ircd.servers.itervalues(): if server.nearHop == self.ircd.name: server.callRemote(ChangeNick, user=self.uuid, newnick=self.nickname) for modfunc in self.ircd.actions["nick"]: modfunc(self, oldNick)
def onUse(self, user, data): mask = data["mask"] if "reason" in data: self.banList[mask] = { "setter": user.nickname, "created": epoch(now()), "duration": data["duration"], "reason": data["reason"] } user.sendMessage( "NOTICE", ":*** Q:Line set on {}, to expire in {} seconds".format( mask, data["duration"])) if "*" not in mask and "?" not in mask: if mask in self.ircd.users: self.ircd.users[mask].disconnect("Q:Lined: {}".format( data["reason"])) else: now_banned = {} for user in self.ircd.users.itervalues(): reason = self.match_qline(user) if reason: now_banned[user] = reason for user, reason in now_banned.iteritems(): user.disconnect("Q:Lined: {}".format(reason)) else: del self.banList[mask] user.sendMessage("NOTICE", ":*** Q:Line removed on {}".format(mask))
def changeNick(self, newNick, fromServer=None): """ Changes this user's nickname. If initiated by a remote server, that server should be specified in the fromServer parameter. """ if newNick == self.nick: return if newNick in self.ircd.userNicks and self.ircd.userNicks[ newNick] != self.uuid: return oldNick = self.nick if oldNick and oldNick in self.ircd.userNicks: del self.ircd.userNicks[self.nick] self.nick = newNick self.nickSince = now() if self.isRegistered(): self.ircd.userNicks[self.nick] = self.uuid userSendList = [self] for channel in self.channels: userSendList.extend(channel.users.keys()) userSendList = [ u for u in set(userSendList) if u.uuid[:3] == self.ircd.serverID ] self.ircd.runActionProcessing("changenickmessage", userSendList, self, oldNick, users=userSendList) self.ircd.runActionStandard("changenick", self, oldNick, fromServer, users=[self])
def onUse(self, user, data): if "reason" in data: self.banList[data["mask"]] = { "setter": user.prefix(), "created": epoch(now()), "duration": data["duration"], "reason": data["reason"] } user.sendMessage( "NOTICE", ":*** K:Line added on {}, to expire in {} seconds".format( data["mask"], data["duration"])) now_banned = {} for nick, u in self.ircd.users.iteritems(): if u.server == self.ircd.name: result = self.match_kline(u) if result: now_banned[nick] = result for uid, reason in now_banned.iteritems(): udata = self.ircd.users[uid] udata.sendMessage( "NOTICE", ":{}".format(self.ircd.servconfig["client_ban_msg"])) udata.disconnect("K:Lined: {}".format(reason)) else: del self.banList[data["mask"]] user.sendMessage("NOTICE", ":*** K:Line removed on {}".format(data["mask"]))
def onUse(self, user, data): if "reason" in data: self.exceptList[data["mask"]] = { "setter": user.nickname, "created": epoch(now()), "duration": data["duration"], "reason": data["reason"], } user.sendMessage( "NOTICE", ":*** E:Line set on {}, to expire in {} seconds".format(data["mask"], data["duration"]) ) else: mask = data["mask"] del self.exceptList[mask] user.sendMessage("NOTICE", ":*** E:Line removed on {}".format(mask)) for u in self.ircd.users.itervalues(): if self.match_eline(u): u.cache["except_line"] = True now_banned = {} for uid, udata in self.ircd.users.iteritems(): for modfunc in self.ircd.actions["xline_rematch"]: reason = modfunc(udata) if reason: now_banned[uid] = reason break # If the user is banned, the user is banned. We don't need to gather a consensus or something. for uid, reason in now_banned.iteritems(): udata = self.ircd.users[uid] udata.sendMessage("NOTICE", ":{}".format(self.ircd.servconfig["client_ban_msg"])) udata.disconnect("Banned: Exception Removed ({})".format(reason))
def onUse(self, user, data): if "targetuser" not in data: return targets = data["targetuser"] for u in targets: user.sendMessage(irc.RPL_WHOISUSER, u.nickname, u.username, u.hostname, "*", ":{}".format(u.realname)) if "o" in user.mode or user == u: user.sendMessage(irc.RPL_WHOISHOST, u.nickname, ":is connecting from {}@{} {}".format(u.username, u.realhost, u.ip)) chanlist = [] for chan in self.ircd.channels.itervalues(): if u in chan.users: chanlist.append(chan) chandisplay = [] for cdata in chanlist: if user in cdata.users or ("s" not in cdata.mode and "p" not in cdata.mode): statuses = cdata.users[u] if u in cdata.users else "" status = self.ircd.prefixes[statuses[0]][0] if statuses else "" chandisplay.append("{}{}".format(status, cdata.name)) if chandisplay: user.sendMessage(irc.RPL_WHOISCHANNELS, u.nickname, ":{}".format(" ".join(chandisplay))) user.sendMessage(irc.RPL_WHOISSERVER, u.nickname, u.server, ":{}".format(self.ircd.servconfig["server_description"] if u.server == self.ircd.name else self.ircd.servers[u.server].description)) if "accountname" in u.metadata["ext"]: user.sendMessage(irc.RPL_WHOISACCOUNT, u.nickname, u.metadata["ext"]["accountname"], ":is logged in as") if u.socket.secure: user.sendMessage(irc.RPL_WHOISSECURE, u.nickname, ":is using a secure connection") if "certfp" in u.metadata["server"]: user.sendMessage(irc.RPL_WHOISCERTFP, u.nickname, ":has client certificate fingerprint {}".format(u.metadata["server"]["certfp"])) if "o" in u.mode: user.sendMessage(irc.RPL_WHOISOPERATOR, u.nickname, ":is an IRC operator") if "whoisdata" in self.ircd.actions: for action in self.ircd.actions["whoisdata"]: action(user, u) user.sendMessage(irc.RPL_WHOISIDLE, u.nickname, str(epoch(now()) - epoch(u.lastactivity)), str(epoch(u.signon)), ":seconds idle, signon time") user.sendMessage(irc.RPL_ENDOFWHOIS, u.nickname, ":End of /WHOIS list")
def unregistered(self): for channel in self.channels.iterkeys(): c = self.ircd.channels[channel] m, b, f = c.mode.combine("-{}".format(self.ircd.prefix_order),[self.nickname for _ in self.ircd.prefix_order],c.name) if m: # Should always be true!? c.log.write("[{:02d}:{:02d}:{:02d}] {} set modes {}\n".format(now().hour, now().minute, now().second, "BidServ", m)) for u in c.users.itervalues(): u.sendMessage("MODE", m, to=c.name, prefix=self.service_prefix("BidServ"))
def expire_zlines(self): current_time = epoch(now()) expired = [] for mask, linedata in self.banList.iteritems(): if linedata["duration"] and current_time > linedata["created"] + linedata["duration"]: expired.append(mask) for mask in expired: del self.banList[mask]
def __init__(self, ircd, name): self.ircd = ircd self.name = str(name) self.created = now() self.topic = "" self.topicSetter = "" self.topicTime = now() self.mode = deepcopy(self.ircd.servconfig["channel_default_mode"]) # If the user specifies default bans or other lists, references to those will still be problematic self.users = {} self.metadata = { # split into metadata key namespaces, see http://ircv3.atheme.org/specification/metadata-3.2 "server": {}, "user": {}, "client": {}, "ext": {}, "private": {} } self.cache = {}
def expire_qlines(self): current_time = epoch(now()) expired = [] for mask, linedata in self.banList.iteritems(): if linedata["duration"] and current_time > linedata["created"] + linedata["duration"]: expired.append(mask) for mask in expired: del self.banList[mask]
def __init__(self, parent): # Mask the IP ip = parent.transport.getPeer().host mapped = IPV4_MAPPED_ADDR.match(ip) if mapped: ip = mapped.group(1) try: hostname = socket.gethostbyaddr(ip)[0] except: hostname = ip # Set attributes self.ircd = parent.factory self.socket = parent self.uuid = str(uuid.uuid1()) while self.uuid in self.ircd.userid: self.uuid = str(uuid.uuid1()) for server in self.ircd.servers.itervalues(): if server.nearHop == self.ircd.name: # only operate on servers with the attribute server.ignoreUsers.discard(self.uuid) self.password = None self.nickname = None self.username = None self.realname = None self.hostname = hostname self.ip = ip self.realhost = hostname self.server = parent.factory.name self.signon = now() self.nicktime = now() self.lastactivity = now() self.lastpong = now() self.mode = {} self.disconnected = Deferred() self.registered = 2 self.metadata = { # split into metadata key namespaces, see http://ircv3.atheme.org/specification/metadata-3.2 "server": {}, "user": {}, "client": {}, "ext": {}, "private": {} } self.cache = {} self.cmd_extra = False # used by the command handler to determine whether the extras hook was called during processing self.ircd.userid[self.uuid] = self
def removeOldEntries(self, whowasEntries): expireDuration = durationToSeconds(self.ircd.config.get("whowas_duration", "1d")) maxCount = self.ircd.config.get("whowas_max_entries", 10) while whowasEntries and len(whowasEntries) > maxCount: whowasEntries.pop(0) expireTime = timestamp(now()) - expireDuration while whowasEntries and whowasEntries[0]["when"] < expireTime: whowasEntries.pop(0) return whowasEntries
def setModes(self, modes, defaultSource): """ Sets modes on the user. Accepts modes as a list of tuples in the format: [ (adding, mode, param, setBy, setTime) ] - adding: True if we're setting the mode; False if unsetting - mode: The mode letter - param: The mode's parameter; None if no parameter is needed for that mode - setBy: Optional, only used for list modes; a human-readable string (typically server name or nick!user@host) for who/what set this mode) - setTime: Optional, only used for list modes; a datetime object containing when the mode was set The defaultSource is a valid user ID or server ID of someone who set the modes. It is used as the source for announcements about the mode change and as the default setter for any list modes who do not have the setBy parameter specified. The default time for list modes with no setTime specified is now(). """ modeChanges = [] defaultSourceName = self._sourceName(defaultSource) if defaultSourceName is None: raise ValueError ("Source must be a valid user or server ID.") nowTime = now() for modeData in modes: mode = modeData[1] if mode not in self.ircd.userModeTypes: continue setBy = defaultSourceName setTime = nowTime modeType = self.ircd.userModeTypes[mode] adding = modeData[0] if modeType in (ModeType.List, ModeType.ParamOnUnset, ModeType.Param): param = modeData[2] else: param = None if modeType == ModeType.List: dataCount = len(modeData) if dataCount >= 4: setBy = modeData[3] if dataCount >= 5: setTime = modeData[4] if adding: paramList = self.ircd.userModes[modeType][mode].checkSet(self, param) else: paramList = self.ircd.userModes[modeType][mode].checkUnset(self, param) if paramList is None: continue for parameter in paramList: if self._applyMode(adding, modeType, mode, parameter, setBy, setTime): modeChanges.append((adding, mode, parameter, setBy, setTime)) self._notifyModeChanges(modeChanges, defaultSource, defaultSourceName) return modeChanges
def execute(self, server, data): if "lostsource" in data or "losttarget" in data: return True if "server" in data: destServer = data["server"] destServer.sendMessage("USERTIMEREQ", destServer.serverID, prefix=data["fromuser"].uuid) else: server.sendMessage("USERTIME", data["fromuser"].uuid, str(now()), prefix=self.ircd.serverID) return True
def setModesByUser(self, user, modes, params, override = False): """ Parses a mode string specified by a user and sets those modes on the user. The user parameter should be the user who set the modes (usually, but not always, this user). The modes parameter is the actual modes string; parameters specified by the user should be as a list of strings in params. The override parameter should be used only when all permission checks should be overridden. """ adding = True changes = [] setBy = self._sourceName(user.uuid) setTime = now() for mode in modes: if len(changes) >= self.ircd.config.get("modes_per_line", 20): break if mode == "+": adding = True continue if mode == "-": adding = False continue if mode not in self.ircd.userModeTypes: user.sendMessage(irc.ERR_UNKNOWNMODE, mode, "is unknown mode char to me") continue modeType = self.ircd.userModeTypes[mode] param = None if modeType in (ModeType.List, ModeType.ParamOnUnset) or (adding and modeType == ModeType.Param): try: param = params.pop(0) except IndexError: if modeType == ModeType.List: self.ircd.userModes[modeType][mode].showListParams(user, self) continue if adding: paramList = self.ircd.userModes[modeType][mode].checkSet(self, param) else: paramList = self.ircd.userModes[modeType][mode].checkUnset(self, param) if paramList is None: continue for parameter in paramList: if len(changes) >= self.ircd.config.get("modes_per_line", 20): break if not override and self.ircd.runActionUntilValue("modepermission-user-{}".format(mode), self, user, adding, parameter, users=[self, user]) is False: continue if adding: if modeType == ModeType.List: if mode in self.modes and len(self.modes[mode]) > self.ircd.config.get("user_listmode_limit", 128): user.sendMessage(irc.ERR_BANLISTFULL, self.name, parameter, "Channel +{} list is full".format(mode)) continue if self._applyMode(adding, modeType, mode, parameter, setBy, setTime): changes.append((adding, mode, parameter, setBy, setTime)) self._notifyModeChanges(changes, user.uuid, setBy) return changes
def registered(self): for channel in self.channels.iterkeys(): c = self.ircd.channels[channel] mode = self.ircd.channel_auto_ops[self.nickserv_id] if self.nickserv_id in self.ircd.channel_auto_ops else "v" m, b, f = c.mode.combine("+{}".format(mode),[self.nickname],c.name) if m: # Should always be true!? c.log.write("[{:02d}:{:02d}:{:02d}] {} set modes {}\n".format(now().hour, now().minute, now().second, "BidServ", m)) for u in c.users.itervalues(): u.sendMessage("MODE", m, to=c.name, prefix=self.service_prefix("BidServ"))
def __init__(self, parent): # Mask the IP ip = parent.transport.getPeer().host mapped = IPV4_MAPPED_ADDR.match(ip) if mapped: ip = mapped.group(1) try: hostname = socket.gethostbyaddr(ip)[0] except: hostname = ip # Set attributes self.ircd = parent.factory self.socket = parent self.uuid = str(uuid.uuid1()) while self.uuid in self.ircd.userid: self.uuid = str(uuid.uuid1()) for server in self.ircd.servers.itervalues(): if server.nearHop == self.ircd.name: # only operate on servers with the attribute server.ignoreUsers.discard(self.uuid) self.password = None self.nickname = None self.username = None self.realname = None self.hostname = hostname self.ip = ip self.realhost = hostname self.server = parent.factory.name self.signon = now() self.nicktime = now() self.lastactivity = now() self.lastpong = now() self.mode = {} self.disconnected = Deferred() self.registered = 2 self.metadata = { # split into metadata key namespaces, see http://ircv3.atheme.org/specification/metadata-3.2 "server": {}, "user": {}, "client": {}, "ext": {}, "private": {} } self.cache = {} self.ircd.userid[self.uuid] = self
def execute(self, server, data): connectTime = data["connecttime"] nickTime = data["nicktime"] newUser = RemoteUser(self.ircd, data["ip"], data["uuid"], data["host"]) newUser.changeHost(data["hosttype"], data["displayhost"], True) newUser.changeIdent(data["ident"], server) newUser.changeGecos(data["gecos"], True) newUser.connectedSince = connectTime newUser.nickSince = nickTime newUser.idleSince = now() if data["nick"] in self.ircd.userNicks: # Handle nick collisions otherUser = self.ircd.users[self.ircd.userNicks[data["nick"]]] if otherUser.localOnly: changeOK = self.ircd.runActionUntilValue("localnickcollision", otherUser, newUser, server, users=[otherUser, newUser]) if changeOK is None: return None sameUser = ("{}@{}".format(otherUser.ident, otherUser.ip) == "{}@{}".format(newUser.ident, newUser.ip)) if sameUser and newUser.nickSince < otherUser.nickSince: # If the user@ip is the same, the newer nickname should win newUser.changeNick(newUser.uuid, server) elif sameUser and otherUser.nickSince < newUser.nickSince: otherUser.changeNick(otherUser.uuid, server) elif newUser.nickSince < otherUser.nickSince: # Otherwise, the older nickname should win otherUser.changeNick(otherUser.uuid, server) elif otherUser.nickSince < newUser.nickSince: newUser.changeNick(newUser.uuid, server) else: # If the nickname times are the same, fall back on connection times, with the same hierarchy as before if sameUser and newUser.connectedSince < otherUser.connectedSince: newUser.changeNick(newUser.uuid, server) elif sameUser and otherUser.connectedSince < newUser.connectedSince: otherUser.changeNick(otherUser.uuid, server) elif newUser.connectedSince < otherUser.connectedSince: otherUser.changeNick(otherUser.uuid, server) elif otherUser.connectedSince < newUser.connectedSince: newUser.changeNick(newUser.uuid, server) else: # As a final fallback, change both nicknames otherUser.changeNick(otherUser.uuid, server) newUser.changeNick(newUser.uuid, server) if newUser.nick is None: # wasn't set by above logic newUser.changeNick(data["nick"], server) modeList = [] for mode, param in data["modes"].iteritems(): modeType = self.ircd.userModeTypes[mode] if modeType == ModeType.List: for paramData in param: modeList.append((True, mode, paramData)) else: modeList.append((True, mode, param)) newUser.setModes(modeList, server.serverID) newUser.register("connection", True) newUser.register("USER", True) newUser.register("NICK", True) connectTimestamp = str(timestamp(connectTime)) nickTimestamp = str(timestamp(nickTime)) modeString = newUser.modeString(None) self.ircd.broadcastToServers(server, "UID", newUser.uuid, connectTimestamp, newUser.nick, newUser.realHost, newUser.host(), newUser.currentHostType(), newUser.ident, newUser.ip, nickTimestamp, modeString, newUser.gecos, prefix=self.ircd.serverID) return True
def addToWhowas(self, user, reason): if user.registered > 0: return # don't process users who haven't yet registered newEntry = { "nick": user.nickname, "ident": user.username, "host": user.hostname, "gecos": user.realname, "server": user.server, "time": now() } if user.nickname in self.history: self.history[user.nickname].append(newEntry) self.history[user.nickname] = self.history[user.nickname][-self.ircd.servconfig["client_whowas_limit"]:] expiryTime = epoch(now()) - parse_duration(self.ircd.servconfig["client_whowas_expire"]) while epoch(self.history[user.nickname][0]["time"]) < expiryTime: self.history[user.nickname].pop(0) else: self.history[user.nickname] = [newEntry]
def showParam(self, user, target): if "s" in target.mode: for mask in target.mode["s"]: time = target.cache["servernoticedata"][ mask] if "servernoticedata" in target.cache and mask in target.cache[ "servernoticedata"] else epoch(now()) user.sendMessage(irc.RPL_LISTMODE, target.nickname, "s", mask, target.nickname, str(time)) user.sendMessage(irc.RPL_ENDOFLISTMODE, target.nickname, "s", ":End of server notice type list")
def removeOldEntries(self, whowasEntries): expireDuration = durationToSeconds( self.ircd.config.get("whowas_duration", "1d")) maxCount = self.ircd.config.get("whowas_max_entries", 10) while whowasEntries and len(whowasEntries) > maxCount: whowasEntries.pop(0) expireTime = timestamp(now()) - expireDuration while whowasEntries and whowasEntries[0]["when"] < expireTime: whowasEntries.pop(0) return whowasEntries
def setTopic(self, topic, setter): for action in self.ircd.actions["topic"]: action(self, topic, setter) self.topic = str(topic) self.topicSetter = str(setter) self.topicTime = now() from txircd.server import SetTopic for server in self.ircd.servers.itervalues(): if server.nearHop == self.ircd.name: server.callRemote(SetTopic, channel=self.name, chants=epoch(self.created), topic=topic, topicsetter=setter, topicts=epoch(self.topicTime))
def pingServer(self, server): if "pingtime" not in server.cache or "pongtime" not in server.cache: server.cache["pingtime"] = now() server.cache["pongtime"] = now() pingTime = server.cache["pingtime"] pongTime = server.cache["pongtime"] if pongTime < pingTime: self.ircd.log.debug( "Server {server.serverID} pinged out (last pong time '{pongTime}' was less than last ping time '{pingTime}' at the next ping interval)", server=server, pongTime=pongTime, pingTime=pingTime) server.disconnect("Ping timeout") return server.sendMessage("PING", self.ircd.serverID, server.serverID, prefix=self.ircd.serverID) server.cache["pingtime"] = now()
def __init__(self, ircd, name): self.ircd = ircd self.name = str(name) self.created = now() self.topic = "" self.topicSetter = "" self.topicTime = now() self.mode = deepcopy( self.ircd.servconfig["channel_default_mode"] ) # If the user specifies default bans or other lists, references to those will still be problematic self.users = {} self.metadata = { # split into metadata key namespaces, see http://ircv3.atheme.org/specification/metadata-3.2 "server": {}, "user": {}, "client": {}, "ext": {}, "private": {} } self.cache = {}
def dataReceived(self, data): if self.dead: return # self.factory.stats_data["bytes_in"] += len(data) # self.factory.stats_data["total_bytes_in"] += len(data) self.data += len(data) self.last_message = now() if self.pinger.running: self.pinger.reset() irc.IRC.dataReceived(self, data)
def pingUser(self, user): if "pingtime" not in user.cache or "pongtime" not in user.cache: user.cache["pingtime"] = now() user.cache["pongtime"] = now() pingTime = user.cache["pingtime"] pongTime = user.cache["pongtime"] if pongTime < pingTime: self.ircd.log.debug( "User {user.uuid} pinged out (last pong time '{pongTime}' was less than last ping time '{pingTime}' at the next ping interval)", user=user, pongTime=pongTime, pingTime=pingTime) user.disconnect("Ping timeout") return if user.idleSince > user.cache["pongtime"]: user.cache["pingtime"] = now() user.cache["pongtime"] = now() return user.sendMessage("PING", self.ircd.name, to=None, prefix=None) user.cache["pingtime"] = now()
def data_serialize(self): expiryTime = epoch(now()) - parse_duration(self.ircd.servconfig["client_whowas_expire"]) remove = [] for nick, data in self.whowasCmd.history.iteritems(): while data and epoch(data[0]["time"]) < expiryTime: data.pop(0) if not data: remove.append(nick) for nick in remove: del self.whowasCmd.history[nick] return [self.whowasCmd.history._data, {}]
def expireKnocks(self, user): if "knocks" not in user.cache: return expiredKnocks = [] nowTime = now() timeDiff = timedelta(seconds=self.ircd.config.get("knock_delay", 300)) for channel, knockTime in user.cache["knocks"].iteritems(): if knockTime + timeDiff < nowTime: expiredKnocks.append(channel) for channel in expiredKnocks: del user.cache["knocks"][channel]
def statsChars(self, user, statschar): if statschar == "o": for user in self.ircd.users.itervalues(): if "o" in user.mode: user.sendMessage( irc.RPL_STATSOPERS, ":{} ({}@{}) Idle: {} secs".format( user.nickname, user.username, user.hostname, epoch(now()) - epoch(user.lastactivity))) elif statschar == "p": for port in self.ircd.client_ports.iterkeys(): user.sendMessage(irc.RPL_STATSPORTS, ":{} (clients)".format(port)) for port in self.ircd.server_ports.iterkeys(): user.sendMessage(irc.RPL_STATSPORTS, ":{} (servers)".format(port)) elif statschar == "u": uptime = now() - self.ircd.created user.sendMessage( irc.RPL_STATSUPTIME, ":Server up {}".format(uptime if uptime.days > 0 else "0 days, {}".format(uptime)))
def addToWhowas(self, user, reason): if user.registered > 0: return # don't process users who haven't yet registered newEntry = { "nick": user.nickname, "ident": user.username, "host": user.hostname, "realhost": user.realhost, "ip": user.ip, "gecos": user.realname, "server": user.server, "time": now() } if user.nickname in self.history: self.history[user.nickname].append(newEntry) self.history[user.nickname] = self.history[user.nickname][-self.ircd.servconfig["client_whowas_limit"]:] expiryTime = epoch(now()) - parse_duration(self.ircd.servconfig["client_whowas_expire"]) while epoch(self.history[user.nickname][0]["time"]) < expiryTime: self.history[user.nickname].pop(0) else: self.history[user.nickname] = [newEntry]
def __init__(self, ircd, ip, uuid=None, host=None): self.ircd = ircd self.uuid = ircd.createUUID() if uuid is None else uuid registrationTimeout = self.ircd.config.get("user_registration_timeout", 10) self.nick = None self.ident = None if ip[0] == ":": # Normalize IPv6 address for IRC ip = "0{}".format(ip) if host is None: self.realHost = ip else: self.realHost = host self.ip = ip self._hostStack = [] self._hostsByType = {} self.gecos = None self._metadata = CaseInsensitiveDictionary() self.cache = {} self.channels = [] self.modes = {} self.connectedSince = now() self.nickSince = now() self.idleSince = now() self._registerHolds = set(("connection", "dns", "NICK", "USER")) self.disconnectedDeferred = Deferred() self._messageBatches = {} self._errorBatchName = None self._errorBatch = [] self.ircd.users[self.uuid] = self self.localOnly = False self.secureConnection = False self._pinger = LoopingCall(self._ping) self._registrationTimeoutTimer = reactor.callLater( registrationTimeout, self._timeoutRegistration) self._connectHandlerTimer = None self._startDNSResolving(registrationTimeout)
def checkSet(self, user, target, param): if "o" not in user.mode: user.sendMessage( irc.ERR_NOPRIVILEGES, ":Permission denied - Only operators may set user mode s") return [False, param] mask = irc_lower(param) if mask not in self.tellLists: self.tellLists[mask] = [] self.tellLists[mask].append(target) if "servernoticedata" not in target.cache: target.cache["servernoticedata"] = {} target.cache["servernoticedata"][mask] = epoch(now()) return [True, mask]