Example #1
0
func New(b bot.Bot, isupportPlugin *isupport.Plugin, modePlugin *mode.Plugin) *Plugin {
	plugin := &Plugin{
		bot:          b,
		tbmgr:        map[string]*TemporaryBanManager{},
		isupport:     isupportPlugin,
		mode:         modePlugin,
		OldHostmasks: []string{},
	}

	modePlugin.HandleFunc("-b", func(e *mode.ModeChangeEvent) {
		if ok, _, _ := isupportPlugin.IsChannel(e.Target); !ok {
			return // not a channel
		}

		hostmask := e.Argument
		tbmgr := plugin.ensureTemporaryBanManager(e.Target)
		if ban, ok := tbmgr.Remove(hostmask); ok {
			logging.Debug("%v: %v removed the temporary ban for %v",
				e.Target, e.Nick, ban.Hostmask)
			plugin.syncBans(e.Target)
		}
	})

	b.HandleFunc("join",
		func(conn *client.Conn, line *client.Line) {
			if line.Nick != conn.Me().Nick {
				return
			}

			plugin.loadBans(line.Args[0])
			go plugin.dumpBans(line.Args[0])
		})

	return plugin
}
Example #2
0
func (p *Plugin) syncBans(target string) {
	// Synchronize bans to file
	logging.Debug("Synchronizing temporary bans for %v to disk...", target)
	fn := p.getTempbansFilename(target)
	f, err := os.Create(fn)
	if err != nil {
		logging.Warn("Could not save temporary bans for %v: %v",
			fn, target, err.Error())
	}
	defer f.Close()

	// Load temporary bans from this file
	if err := p.ensureTemporaryBanManager(target).Export(f); err != nil {
		logging.Warn("Could not save temporary bans: %v", err.Error())
	}
}
Example #3
0
// write writes a \r\n terminated line of output to the connected server,
// using Hybrid's algorithm to rate limit if conn.cfg.Flood is false.
func (conn *Conn) write(line string) error {
	if !conn.cfg.Flood {
		if t := conn.rateLimit(len(line)); t != 0 {
			// sleep for the current line's time value before sending it
			logging.Info("irc.rateLimit(): Flood! Sleeping for %.2f secs.",
				t.Seconds())
			<-time.After(t)
		}
	}

	if _, err := conn.io.WriteString(line + "\r\n"); err != nil {
		return err
	}
	if err := conn.io.Flush(); err != nil {
		return err
	}
	logging.Debug("-> %s", line)
	return nil
}
Example #4
0
func Migrate() error {
	ms.Lock()
	defer ms.Unlock()
	ms.db.Init(Bolt, COLLECTION, nil)

	failed := []string{}
	for coll, m := range ms.migrators {
		if m.migrated {
			continue
		}
		logging.Debug("Migrating %q.", coll)
		if err := m.Migrate(); err != nil {
			logging.Error("Migrating %q failed: %v", coll, err)
			failed = append(failed, coll)
			continue
		}
		if differ, ok := m.Migrator.(Differ); ok {
			before, after, err := differ.Diff()
			if err != nil {
				logging.Error("Diffing %q failed: %v", coll, err)
				failed = append(failed, coll)
				continue
			}
			sort.Strings(before)
			sort.Strings(after)
			unified, err := diff.Unified(before, after)
			if err != nil {
				logging.Error("Migration diff: %v\n%s", err, strings.Join(unified, "\n"))
				failed = append(failed, coll)
				continue
			}
		}
		if err := ms.db.Put(K{{"collection", coll}}, &done{true}); err != nil {
			logging.Warn("Setting migrated status for %q: %v", coll, err)
		}
		m.migrated = true
	}
	if len(failed) > 0 {
		return fmt.Errorf("migration failed for: \"%s\"",
			strings.Join(failed, "\", \""))
	}
	return nil
}
Example #5
0
func (p *Plugin) loadBans(target string) {
	logging.Debug("Loading temporary bans for %v from disk...", target)

	// Check if file exists
	fn := p.getTempbansFilename(target)
	f, err := os.Open(fn)
	switch {
	case os.IsNotExist(err):
		return
	case err == nil:
	default:
		logging.Warn("Could not load temporary bans for %v: %v",
			fn, target, err.Error())
	}
	defer f.Close()

	// Load temporary bans from this file
	if err := p.ensureTemporaryBanManager(target).Import(f); err != nil {
		logging.Warn("Could not load temporary bans: %v", err.Error())
	}
}
Example #6
0
// receive one \r\n terminated line from peer, parse and dispatch it
func (conn *Conn) recv() {
	for {
		s, err := conn.io.ReadString('\n')
		if err != nil {
			if err != io.EOF {
				logging.Error("irc.recv(): %s", err.Error())
			}
			// We can't defer this, because shutdown() waits for it.
			conn.wg.Done()
			conn.shutdown()
			return
		}
		s = strings.Trim(s, "\r\n")
		logging.Debug("<- %s", s)

		if line := ParseLine(s); line != nil {
			line.Time = time.Now()
			conn.in <- line
		} else {
			logging.Warn("irc.recv(): problems parsing line:\n  %s", s)
		}
	}
}
Example #7
0
func (plugin *Plugin) OnJoin(conn *client.Conn, line *client.Line) {
	logging.Info("vpnbot.Plugin: %v joined %v", line.Src, line.Target())

	if lastCheck, ok := plugin.lastCheckNicks[line.Nick]; ok && time.Now().Sub(lastCheck) < 15*time.Minute {
		// There is a check in progress or another one has been done earlier
		logging.Debug("vpnbot.Plugin: Not checking %v, last check was %v",
			line.Nick, humanize.Time(plugin.lastCheckNicks[line.Nick]))
		return
	}
	logging.Debug("vpnbot.Plugin: Checking %v...", line.Nick)
	plugin.lastCheckNicks[line.Nick] = time.Now()

	// Is this me?
	if line.Nick == conn.Me().Nick {
		logging.Debug("vpnbot.Plugin: %v is actually me, skipping.", line.Nick)
		return
	}

	// Nickname == Ident? (9 chars cut)
	if !strings.HasPrefix(nonWordCharsRegex.ReplaceAllString(line.Nick, ""),
		strings.TrimLeft(line.Ident, "~")) {
		logging.Debug("vpnbot.Plugin: %v's nick doesn't match the ident, skipping.", line.Nick)
		return
	}

	// Hostname is masked RDNS vhost/IP?
	// TODO - Use regex to avoid banning due to similar vhosts
	if !maskedAddrRegex.MatchString(line.Host) {
		// Detected custom vHost
		logging.Debug("vpnbot.Plugin: %v has a custom vhost, skipping.", line.Nick)
		return
	}

	go func() {
		botNick := line.Nick

		nobotActivityChan := make(chan string)
		defer plugin.bot.HandleFunc("privmsg",
			func(conn *client.Conn, line *client.Line) {
				if line.Nick == botNick {
					nobotActivityChan <- "User sent a message"
				}
			}).Remove()
		defer plugin.bot.HandleFunc("noticed",
			func(conn *client.Conn, line *client.Line) {
				if line.Nick == botNick {
					nobotActivityChan <- "User sent a notice"
				}
			}).Remove()
		defer plugin.bot.HandleFunc("part",
			func(conn *client.Conn, line *client.Line) {
				if line.Nick == botNick {
					nobotActivityChan <- "User left"
				}
			}).Remove()
		defer plugin.bot.HandleFunc("part",
			func(conn *client.Conn, line *client.Line) {
				if line.Nick == botNick {
				}

				if len(line.Args) > 0 {
					switch line.Args[0] {
					case "Excess flood":
						// If this was an excess flood, consider it spam that should
						// be good to ban anyways
						nobotActivityChan <- "Excess flood, banning early"
						banmask := fmt.Sprintf("%v!%v@%v", "*", "*", line.Host)
						// TODO - Ramp up/down the duration with increasing/decreasing activity of the bots
						plugin.banGlobal(plugin.generateBan(line.Nick, banmask,
							"Instant excess flood", 2*24*time.Hour))
					default:
						nobotActivityChan <- "User quit normally"
					}
				}
			}).Remove()

		// Give nobotActivityChan some time to prove this is not a bot
		select {
		case reason := <-nobotActivityChan:
			logging.Info("vpnbot.Plugin: %v, skipping.", reason)
			return
		case <-time.After(1 * time.Second):
		}

		// Get WHOIS info
		info, err := plugin.whois.WhoIs(line.Nick)
		if err != nil && err != whois.ErrNoSuchNick {
			logging.Warn("vpnbot.Plugin: Can't get WHOIS info for %v, skipping: %v",
				line.Nick, err.Error())
			return
		}

		// Not an oper?
		if info.IsOperator {
			logging.Debug("vpnbot.Plugin: %v is operator, skipping.",
				line.Nick)
			return
		}

		// Not away
		if info.IsAway {
			logging.Debug("vpnbot.Plugin: %v is away, skipping.", line.Nick)
			return
		}

		// Realname == Nickname?
		if info.Realname != line.Nick {
			logging.Debug(
				"vpnbot.Plugin: %v's nick doesn't match the realname, skipping.",
				line.Nick)
			return
		}

		// Count of channels at least 48
		if len(info.Channels) < 48 {
			logging.Debug(
				"vpnbot.Plugin: %v is not in a high amount of channels, skipping.",
				line.Nick)
			return
		}

		// Not halfop, op, aop or owner in any channel
		for _, prefix := range info.Channels {
			if prefix == '%' || prefix == '@' ||
				prefix == '&' || prefix == '~' {
				logging.Debug(
					"vpnbot.Plugin: %v is opped in a channel, skipping.",
					line.Nick)
				return
			}
		}

		// Give nobotActivityChan some time to prove this is not a bot
		select {
		case reason := <-nobotActivityChan:
			logging.Info("vpnbot.Plugin: %v, skipping.", reason)
			return
		case <-time.After(250 * time.Millisecond):
		}

		// More expensive tests below...

		// Make sure we deal with an unregistered client

		status := plugin.nickserv.Status(line.Nick)[line.Nick]
		if status.Error != nil {
			logging.Warn("vpnbot.Plugin: Can't get auth status for %v, skipping: %v",
				line.Nick, status.Error)
			return
		}
		if status.Level >= nickserv.Status_IdentifiedViaPassword {
			logging.Debug("vpnbot.Plugin: %v is identified, skipping.",
				line.Nick)
			return
		}

		// Give nobotActivityChan some time to prove this is not a bot
		select {
		case reason := <-nobotActivityChan:
			logging.Info("vpnbot.Plugin: %v, skipping.", reason)
			return
		case <-time.After(250 * time.Millisecond):
		}

		// This is a bot we need to ban!
		banmask := fmt.Sprintf("%v!%v@%v", "*", "*", line.Host)
		// TODO - Ramp up/down the duration with increasing/decreasing activity of the bots
		plugin.banGlobal(plugin.generateBan(line.Nick, banmask,
			"Known pattern of ban-evading/logging bot", 2*24*time.Hour))
	}()
}
Example #8
0
func (m *RWMutex) RUnlock() {
	m.init()
	_, f, l, _ := runtime.Caller(1)
	logging.Debug("%v: Read-unlocked at %v:%v", m, f, l)
	m.m.RUnlock()
}
Example #9
0
func (m *RWMutex) Lock() {
	m.init()
	_, f, l, _ := runtime.Caller(1)
	logging.Debug("%v: Locked at %v:%v", m, f, l)
	m.m.Lock()
}