You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

server.go 32KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994
  1. // Copyright (c) 2012-2014 Jeremy Latt
  2. // Copyright (c) 2014-2015 Edmund Huber
  3. // Copyright (c) 2016-2017 Daniel Oaks <daniel@danieloaks.net>
  4. // released under the MIT license
  5. package irc
  6. import (
  7. "bufio"
  8. "crypto/tls"
  9. "fmt"
  10. "net"
  11. "net/http"
  12. _ "net/http/pprof"
  13. "os"
  14. "os/signal"
  15. "strconv"
  16. "strings"
  17. "sync"
  18. "syscall"
  19. "time"
  20. "unsafe"
  21. "github.com/goshuirc/irc-go/ircfmt"
  22. "github.com/oragono/oragono/irc/caps"
  23. "github.com/oragono/oragono/irc/connection_limits"
  24. "github.com/oragono/oragono/irc/history"
  25. "github.com/oragono/oragono/irc/logger"
  26. "github.com/oragono/oragono/irc/modes"
  27. "github.com/oragono/oragono/irc/mysql"
  28. "github.com/oragono/oragono/irc/sno"
  29. "github.com/tidwall/buntdb"
  30. )
  31. var (
  32. // common error line to sub values into
  33. errorMsg = "ERROR :%s\r\n"
  34. // supportedUserModesString acts as a cache for when we introduce users
  35. supportedUserModesString = modes.SupportedUserModes.String()
  36. // supportedChannelModesString acts as a cache for when we introduce users
  37. supportedChannelModesString = modes.SupportedChannelModes.String()
  38. // whitelist of caps to serve on the STS-only listener. In particular,
  39. // never advertise SASL, to discourage people from sending their passwords:
  40. stsOnlyCaps = caps.NewSet(caps.STS, caps.MessageTags, caps.ServerTime, caps.LabeledResponse, caps.Nope)
  41. // we only have standard channels for now. TODO: any updates to this
  42. // will also need to be reflected in CasefoldChannel
  43. chanTypes = "#"
  44. throttleMessage = "You have attempted to connect too many times within a short duration. Wait a while, and you will be able to connect."
  45. )
  46. // ListenerWrapper wraps a listener so it can be safely reconfigured or stopped
  47. type ListenerWrapper struct {
  48. // protects atomic update of config and shouldStop:
  49. sync.Mutex // tier 1
  50. listener net.Listener
  51. config listenerConfig
  52. shouldStop bool
  53. }
  54. // Server is the main Oragono server.
  55. type Server struct {
  56. accounts AccountManager
  57. channels ChannelManager
  58. channelRegistry ChannelRegistry
  59. clients ClientManager
  60. config unsafe.Pointer
  61. configFilename string
  62. connectionLimiter connection_limits.Limiter
  63. ctime time.Time
  64. dlines *DLineManager
  65. helpIndexManager HelpIndexManager
  66. klines *KLineManager
  67. listeners map[string]*ListenerWrapper
  68. logger *logger.Manager
  69. monitorManager MonitorManager
  70. name string
  71. nameCasefolded string
  72. rehashMutex sync.Mutex // tier 4
  73. rehashSignal chan os.Signal
  74. pprofServer *http.Server
  75. resumeManager ResumeManager
  76. signals chan os.Signal
  77. snomasks SnoManager
  78. store *buntdb.DB
  79. historyDB mysql.MySQL
  80. torLimiter connection_limits.TorLimiter
  81. whoWas WhoWasList
  82. stats Stats
  83. semaphores ServerSemaphores
  84. }
  85. var (
  86. // ServerExitSignals are the signals the server will exit on.
  87. ServerExitSignals = []os.Signal{
  88. syscall.SIGINT,
  89. syscall.SIGTERM,
  90. syscall.SIGQUIT,
  91. }
  92. )
  93. type clientConn struct {
  94. Conn net.Conn
  95. Config listenerConfig
  96. }
  97. // NewServer returns a new Oragono server.
  98. func NewServer(config *Config, logger *logger.Manager) (*Server, error) {
  99. // initialize data structures
  100. server := &Server{
  101. ctime: time.Now().UTC(),
  102. listeners: make(map[string]*ListenerWrapper),
  103. logger: logger,
  104. rehashSignal: make(chan os.Signal, 1),
  105. signals: make(chan os.Signal, len(ServerExitSignals)),
  106. }
  107. server.clients.Initialize()
  108. server.semaphores.Initialize()
  109. server.resumeManager.Initialize(server)
  110. server.whoWas.Initialize(config.Limits.WhowasEntries)
  111. server.monitorManager.Initialize()
  112. server.snomasks.Initialize()
  113. if err := server.applyConfig(config); err != nil {
  114. return nil, err
  115. }
  116. // Attempt to clean up when receiving these signals.
  117. signal.Notify(server.signals, ServerExitSignals...)
  118. signal.Notify(server.rehashSignal, syscall.SIGHUP)
  119. return server, nil
  120. }
  121. // Shutdown shuts down the server.
  122. func (server *Server) Shutdown() {
  123. //TODO(dan): Make sure we disallow new nicks
  124. for _, client := range server.clients.AllClients() {
  125. client.Notice("Server is shutting down")
  126. }
  127. if err := server.store.Close(); err != nil {
  128. server.logger.Error("shutdown", fmt.Sprintln("Could not close datastore:", err))
  129. }
  130. server.historyDB.Close()
  131. }
  132. // Run starts the server.
  133. func (server *Server) Run() {
  134. // defer closing db/store
  135. defer server.store.Close()
  136. for {
  137. select {
  138. case <-server.signals:
  139. server.Shutdown()
  140. return
  141. case <-server.rehashSignal:
  142. go func() {
  143. server.logger.Info("server", "Rehashing due to SIGHUP")
  144. err := server.rehash()
  145. if err != nil {
  146. server.logger.Error("server", fmt.Sprintln("Failed to rehash:", err.Error()))
  147. }
  148. }()
  149. }
  150. }
  151. }
  152. func (server *Server) checkBans(ipaddr net.IP) (banned bool, message string) {
  153. // check DLINEs
  154. isBanned, info := server.dlines.CheckIP(ipaddr)
  155. if isBanned {
  156. server.logger.Info("localconnect-ip", fmt.Sprintf("Client from %v rejected by d-line", ipaddr))
  157. return true, info.BanMessage("You are banned from this server (%s)")
  158. }
  159. // check connection limits
  160. err := server.connectionLimiter.AddClient(ipaddr)
  161. if err == connection_limits.ErrLimitExceeded {
  162. // too many connections from one client, tell the client and close the connection
  163. server.logger.Info("localconnect-ip", fmt.Sprintf("Client from %v rejected for connection limit", ipaddr))
  164. return true, "Too many clients from your network"
  165. } else if err == connection_limits.ErrThrottleExceeded {
  166. duration := server.Config().Server.IPLimits.BanDuration
  167. if duration == 0 {
  168. return false, ""
  169. }
  170. server.dlines.AddIP(ipaddr, duration, throttleMessage, "Exceeded automated connection throttle", "auto.connection.throttler")
  171. // they're DLINE'd for 15 minutes or whatever, so we can reset the connection throttle now,
  172. // and once their temporary DLINE is finished they can fill up the throttler again
  173. server.connectionLimiter.ResetThrottle(ipaddr)
  174. // this might not show up properly on some clients, but our objective here is just to close it out before it has a load impact on us
  175. server.logger.Info(
  176. "localconnect-ip",
  177. fmt.Sprintf("Client from %v exceeded connection throttle, d-lining for %v", ipaddr, duration))
  178. return true, throttleMessage
  179. } else if err != nil {
  180. server.logger.Warning("internal", "unexpected ban result", err.Error())
  181. }
  182. return false, ""
  183. }
  184. func (server *Server) checkTorLimits() (banned bool, message string) {
  185. switch server.torLimiter.AddClient() {
  186. case connection_limits.ErrLimitExceeded:
  187. return true, "Too many clients from the Tor network"
  188. case connection_limits.ErrThrottleExceeded:
  189. return true, "Exceeded connection throttle for the Tor network"
  190. default:
  191. return false, ""
  192. }
  193. }
  194. //
  195. // IRC protocol listeners
  196. //
  197. // createListener starts a given listener.
  198. func (server *Server) createListener(addr string, conf listenerConfig, bindMode os.FileMode) (*ListenerWrapper, error) {
  199. // make listener
  200. var listener net.Listener
  201. var err error
  202. addr = strings.TrimPrefix(addr, "unix:")
  203. if strings.HasPrefix(addr, "/") {
  204. // https://stackoverflow.com/a/34881585
  205. os.Remove(addr)
  206. listener, err = net.Listen("unix", addr)
  207. if err == nil && bindMode != 0 {
  208. os.Chmod(addr, bindMode)
  209. }
  210. } else {
  211. listener, err = net.Listen("tcp", addr)
  212. }
  213. if err != nil {
  214. return nil, err
  215. }
  216. // throw our details to the server so we can be modified/killed later
  217. wrapper := ListenerWrapper{
  218. listener: listener,
  219. config: conf,
  220. shouldStop: false,
  221. }
  222. var shouldStop bool
  223. // setup accept goroutine
  224. go func() {
  225. for {
  226. conn, err := listener.Accept()
  227. // synchronously access config data:
  228. wrapper.Lock()
  229. shouldStop = wrapper.shouldStop
  230. conf := wrapper.config
  231. wrapper.Unlock()
  232. if shouldStop {
  233. if conn != nil {
  234. conn.Close()
  235. }
  236. listener.Close()
  237. return
  238. } else if err == nil {
  239. var proxyLine string
  240. if conf.ProxyBeforeTLS {
  241. proxyLine = readRawProxyLine(conn)
  242. if proxyLine == "" {
  243. server.logger.Error("internal", "bad TLS-proxy line from", addr)
  244. conn.Close()
  245. continue
  246. }
  247. }
  248. if conf.TLSConfig != nil {
  249. conn = tls.Server(conn, conf.TLSConfig)
  250. }
  251. newConn := clientConn{
  252. Conn: conn,
  253. Config: conf,
  254. }
  255. // hand off the connection
  256. go server.RunClient(newConn, proxyLine)
  257. } else {
  258. server.logger.Error("internal", "accept error", addr, err.Error())
  259. }
  260. }
  261. }()
  262. return &wrapper, nil
  263. }
  264. //
  265. // server functionality
  266. //
  267. func (server *Server) tryRegister(c *Client, session *Session) (exiting bool) {
  268. // if the session just sent us a RESUME line, try to resume
  269. if session.resumeDetails != nil {
  270. session.tryResume()
  271. return // whether we succeeded or failed, either way `c` is not getting registered
  272. }
  273. // try to complete registration normally
  274. if c.preregNick == "" || !c.HasUsername() || session.capState == caps.NegotiatingState {
  275. return
  276. }
  277. if c.isSTSOnly {
  278. server.playRegistrationBurst(session)
  279. return true
  280. }
  281. // client MUST send PASS if necessary, or authenticate with SASL if necessary,
  282. // before completing the other registration commands
  283. authOutcome := c.isAuthorized(server.Config(), session)
  284. var quitMessage string
  285. switch authOutcome {
  286. case authFailPass:
  287. quitMessage = c.t("Password incorrect")
  288. c.Send(nil, server.name, ERR_PASSWDMISMATCH, "*", quitMessage)
  289. case authFailSaslRequired, authFailTorSaslRequired:
  290. quitMessage = c.t("You must log in with SASL to join this server")
  291. c.Send(nil, c.server.name, "FAIL", "*", "ACCOUNT_REQUIRED", quitMessage)
  292. }
  293. if authOutcome != authSuccess {
  294. c.Quit(quitMessage, nil)
  295. return true
  296. }
  297. // we have the final value of the IP address: do the hostname lookup
  298. // (nickmask will be set below once nickname assignment succeeds)
  299. if session.rawHostname == "" {
  300. session.client.lookupHostname(session, false)
  301. }
  302. rb := NewResponseBuffer(session)
  303. nickAssigned := performNickChange(server, c, c, session, c.preregNick, rb)
  304. rb.Send(true)
  305. if !nickAssigned {
  306. c.preregNick = ""
  307. return
  308. }
  309. if session.client != c {
  310. // reattached, bail out.
  311. // we'll play the reg burst later, on the new goroutine associated with
  312. // (thisSession, otherClient). This is to avoid having to transfer state
  313. // like nickname, hostname, etc. to show the correct values in the reg burst.
  314. return
  315. }
  316. // check KLINEs
  317. isBanned, info := server.klines.CheckMasks(c.AllNickmasks()...)
  318. if isBanned {
  319. c.Quit(info.BanMessage(c.t("You are banned from this server (%s)")), nil)
  320. return true
  321. }
  322. // registration has succeeded:
  323. c.SetRegistered()
  324. // count new user in statistics
  325. server.stats.Register()
  326. server.monitorManager.AlertAbout(c, true)
  327. server.playRegistrationBurst(session)
  328. return false
  329. }
  330. func (server *Server) playRegistrationBurst(session *Session) {
  331. c := session.client
  332. // continue registration
  333. d := c.Details()
  334. server.logger.Info("localconnect", fmt.Sprintf("Client connected [%s] [u:%s] [r:%s]", d.nick, d.username, d.realname))
  335. server.snomasks.Send(sno.LocalConnects, fmt.Sprintf("Client connected [%s] [u:%s] [h:%s] [ip:%s] [r:%s]", d.nick, d.username, session.rawHostname, session.IP().String(), d.realname))
  336. // send welcome text
  337. //NOTE(dan): we specifically use the NICK here instead of the nickmask
  338. // see http://modern.ircdocs.horse/#rplwelcome-001 for details on why we avoid using the nickmask
  339. session.Send(nil, server.name, RPL_WELCOME, d.nick, fmt.Sprintf(c.t("Welcome to the Internet Relay Network %s"), d.nick))
  340. session.Send(nil, server.name, RPL_YOURHOST, d.nick, fmt.Sprintf(c.t("Your host is %[1]s, running version %[2]s"), server.name, Ver))
  341. session.Send(nil, server.name, RPL_CREATED, d.nick, fmt.Sprintf(c.t("This server was created %s"), server.ctime.Format(time.RFC1123)))
  342. //TODO(dan): Look at adding last optional [<channel modes with a parameter>] parameter
  343. session.Send(nil, server.name, RPL_MYINFO, d.nick, server.name, Ver, supportedUserModesString, supportedChannelModesString)
  344. if c.isSTSOnly {
  345. for _, line := range server.Config().Server.STS.bannerLines {
  346. c.Notice(line)
  347. }
  348. return
  349. }
  350. rb := NewResponseBuffer(session)
  351. server.RplISupport(c, rb)
  352. server.Lusers(c, rb)
  353. server.MOTD(c, rb)
  354. rb.Send(true)
  355. modestring := c.ModeString()
  356. if modestring != "+" {
  357. session.Send(nil, d.nickMask, RPL_UMODEIS, d.nick, modestring)
  358. }
  359. c.attemptAutoOper(session)
  360. if server.logger.IsLoggingRawIO() {
  361. session.Send(nil, c.server.name, "NOTICE", d.nick, c.t("This server is in debug mode and is logging all user I/O. If you do not wish for everything you send to be readable by the server owner(s), please disconnect."))
  362. }
  363. // #572: defer nick warnings to the end of the registration burst
  364. session.client.nickTimer.Touch(nil)
  365. }
  366. // RplISupport outputs our ISUPPORT lines to the client. This is used on connection and in VERSION responses.
  367. func (server *Server) RplISupport(client *Client, rb *ResponseBuffer) {
  368. translatedISupport := client.t("are supported by this server")
  369. nick := client.Nick()
  370. config := server.Config()
  371. for _, cachedTokenLine := range config.Server.isupport.CachedReply {
  372. length := len(cachedTokenLine) + 2
  373. tokenline := make([]string, length)
  374. tokenline[0] = nick
  375. copy(tokenline[1:], cachedTokenLine)
  376. tokenline[length-1] = translatedISupport
  377. rb.Add(nil, server.name, RPL_ISUPPORT, tokenline...)
  378. }
  379. }
  380. func (server *Server) Lusers(client *Client, rb *ResponseBuffer) {
  381. nick := client.Nick()
  382. stats := server.stats.GetValues()
  383. rb.Add(nil, server.name, RPL_LUSERCLIENT, nick, fmt.Sprintf(client.t("There are %[1]d users and %[2]d invisible on %[3]d server(s)"), stats.Total-stats.Invisible, stats.Invisible, 1))
  384. rb.Add(nil, server.name, RPL_LUSEROP, nick, strconv.Itoa(stats.Operators), client.t("IRC Operators online"))
  385. rb.Add(nil, server.name, RPL_LUSERUNKNOWN, nick, strconv.Itoa(stats.Unknown), client.t("unregistered connections"))
  386. rb.Add(nil, server.name, RPL_LUSERCHANNELS, nick, strconv.Itoa(server.channels.Len()), client.t("channels formed"))
  387. rb.Add(nil, server.name, RPL_LUSERME, nick, fmt.Sprintf(client.t("I have %[1]d clients and %[2]d servers"), stats.Total, 1))
  388. total := strconv.Itoa(stats.Total)
  389. max := strconv.Itoa(stats.Max)
  390. rb.Add(nil, server.name, RPL_LOCALUSERS, nick, total, max, fmt.Sprintf(client.t("Current local users %[1]s, max %[2]s"), total, max))
  391. rb.Add(nil, server.name, RPL_GLOBALUSERS, nick, total, max, fmt.Sprintf(client.t("Current global users %[1]s, max %[2]s"), total, max))
  392. }
  393. // MOTD serves the Message of the Day.
  394. func (server *Server) MOTD(client *Client, rb *ResponseBuffer) {
  395. motdLines := server.Config().Server.motdLines
  396. if len(motdLines) < 1 {
  397. rb.Add(nil, server.name, ERR_NOMOTD, client.nick, client.t("MOTD File is missing"))
  398. return
  399. }
  400. rb.Add(nil, server.name, RPL_MOTDSTART, client.nick, fmt.Sprintf(client.t("- %s Message of the day - "), server.name))
  401. for _, line := range motdLines {
  402. rb.Add(nil, server.name, RPL_MOTD, client.nick, line)
  403. }
  404. rb.Add(nil, server.name, RPL_ENDOFMOTD, client.nick, client.t("End of MOTD command"))
  405. }
  406. // WhoisChannelsNames returns the common channel names between two users.
  407. func (client *Client) WhoisChannelsNames(target *Client, multiPrefix bool) []string {
  408. var chstrs []string
  409. for _, channel := range target.Channels() {
  410. // channel is secret and the target can't see it
  411. if !client.HasMode(modes.Operator) {
  412. if (target.HasMode(modes.Invisible) || channel.flags.HasMode(modes.Secret)) && !channel.hasClient(client) {
  413. continue
  414. }
  415. }
  416. chstrs = append(chstrs, channel.ClientPrefixes(target, multiPrefix)+channel.name)
  417. }
  418. return chstrs
  419. }
  420. func (client *Client) getWhoisOf(target *Client, rb *ResponseBuffer) {
  421. cnick := client.Nick()
  422. targetInfo := target.Details()
  423. rb.Add(nil, client.server.name, RPL_WHOISUSER, cnick, targetInfo.nick, targetInfo.username, targetInfo.hostname, "*", targetInfo.realname)
  424. tnick := targetInfo.nick
  425. whoischannels := client.WhoisChannelsNames(target, rb.session.capabilities.Has(caps.MultiPrefix))
  426. if whoischannels != nil {
  427. rb.Add(nil, client.server.name, RPL_WHOISCHANNELS, cnick, tnick, strings.Join(whoischannels, " "))
  428. }
  429. tOper := target.Oper()
  430. if tOper != nil {
  431. rb.Add(nil, client.server.name, RPL_WHOISOPERATOR, cnick, tnick, tOper.WhoisLine)
  432. }
  433. if client.HasMode(modes.Operator) || client == target {
  434. rb.Add(nil, client.server.name, RPL_WHOISACTUALLY, cnick, tnick, fmt.Sprintf("%s@%s", targetInfo.username, target.RawHostname()), target.IPString(), client.t("Actual user@host, Actual IP"))
  435. }
  436. if target.HasMode(modes.TLS) {
  437. rb.Add(nil, client.server.name, RPL_WHOISSECURE, cnick, tnick, client.t("is using a secure connection"))
  438. }
  439. if targetInfo.accountName != "*" {
  440. rb.Add(nil, client.server.name, RPL_WHOISACCOUNT, cnick, tnick, targetInfo.accountName, client.t("is logged in as"))
  441. }
  442. if target.HasMode(modes.Bot) {
  443. rb.Add(nil, client.server.name, RPL_WHOISBOT, cnick, tnick, ircfmt.Unescape(fmt.Sprintf(client.t("is a $bBot$b on %s"), client.server.Config().Network.Name)))
  444. }
  445. if client == target || client.HasMode(modes.Operator) {
  446. for _, session := range target.Sessions() {
  447. if session.certfp != "" {
  448. rb.Add(nil, client.server.name, RPL_WHOISCERTFP, cnick, tnick, fmt.Sprintf(client.t("has client certificate fingerprint %s"), session.certfp))
  449. }
  450. }
  451. }
  452. rb.Add(nil, client.server.name, RPL_WHOISIDLE, cnick, tnick, strconv.FormatUint(target.IdleSeconds(), 10), strconv.FormatInt(target.SignonTime(), 10), client.t("seconds idle, signon time"))
  453. }
  454. // rplWhoReply returns the WHO reply between one user and another channel/user.
  455. // <channel> <user> <host> <server> <nick> ( "H" / "G" ) ["*"] [ ( "@" / "+" ) ]
  456. // :<hopcount> <real name>
  457. func (client *Client) rplWhoReply(channel *Channel, target *Client, rb *ResponseBuffer) {
  458. channelName := "*"
  459. flags := ""
  460. if target.Away() {
  461. flags = "G"
  462. } else {
  463. flags = "H"
  464. }
  465. if target.HasMode(modes.Operator) {
  466. flags += "*"
  467. }
  468. if channel != nil {
  469. // TODO is this right?
  470. flags += channel.ClientPrefixes(target, rb.session.capabilities.Has(caps.MultiPrefix))
  471. channelName = channel.name
  472. }
  473. details := target.Details()
  474. // hardcode a hopcount of 0 for now
  475. rb.Add(nil, client.server.name, RPL_WHOREPLY, client.Nick(), channelName, details.username, details.hostname, client.server.name, details.nick, flags, "0 "+details.realname)
  476. }
  477. // rehash reloads the config and applies the changes from the config file.
  478. func (server *Server) rehash() error {
  479. server.logger.Debug("server", "Starting rehash")
  480. // only let one REHASH go on at a time
  481. server.rehashMutex.Lock()
  482. defer server.rehashMutex.Unlock()
  483. server.logger.Debug("server", "Got rehash lock")
  484. config, err := LoadConfig(server.configFilename)
  485. if err != nil {
  486. return fmt.Errorf("Error loading config file config: %s", err.Error())
  487. }
  488. err = server.applyConfig(config)
  489. if err != nil {
  490. return fmt.Errorf("Error applying config changes: %s", err.Error())
  491. }
  492. return nil
  493. }
  494. func (server *Server) applyConfig(config *Config) (err error) {
  495. oldConfig := server.Config()
  496. initial := oldConfig == nil
  497. if initial {
  498. server.configFilename = config.Filename
  499. server.name = config.Server.Name
  500. server.nameCasefolded = config.Server.nameCasefolded
  501. globalCasemappingSetting = config.Server.Casemapping
  502. } else {
  503. // enforce configs that can't be changed after launch:
  504. if server.name != config.Server.Name {
  505. return fmt.Errorf("Server name cannot be changed after launching the server, rehash aborted")
  506. } else if oldConfig.Datastore.Path != config.Datastore.Path {
  507. return fmt.Errorf("Datastore path cannot be changed after launching the server, rehash aborted")
  508. } else if globalCasemappingSetting != config.Server.Casemapping {
  509. return fmt.Errorf("Casemapping cannot be changed after launching the server, rehash aborted")
  510. }
  511. }
  512. server.logger.Info("server", "Using config file", server.configFilename)
  513. // first, reload config sections for functionality implemented in subpackages:
  514. wasLoggingRawIO := !initial && server.logger.IsLoggingRawIO()
  515. err = server.logger.ApplyConfig(config.Logging)
  516. if err != nil {
  517. return err
  518. }
  519. nowLoggingRawIO := server.logger.IsLoggingRawIO()
  520. // notify existing clients if raw i/o logging was enabled by a rehash
  521. sendRawOutputNotice := !wasLoggingRawIO && nowLoggingRawIO
  522. server.connectionLimiter.ApplyConfig(&config.Server.IPLimits)
  523. tlConf := &config.Server.TorListeners
  524. server.torLimiter.Configure(tlConf.MaxConnections, tlConf.ThrottleDuration, tlConf.MaxConnectionsPerDuration)
  525. // Translations
  526. server.logger.Debug("server", "Regenerating HELP indexes for new languages")
  527. server.helpIndexManager.GenerateIndices(config.languageManager)
  528. if oldConfig != nil {
  529. // if certain features were enabled by rehash, we need to load the corresponding data
  530. // from the store
  531. if !oldConfig.Accounts.NickReservation.Enabled {
  532. server.accounts.buildNickToAccountIndex(config)
  533. }
  534. if !oldConfig.Accounts.VHosts.Enabled {
  535. server.accounts.initVHostRequestQueue(config)
  536. }
  537. if !oldConfig.Channels.Registration.Enabled {
  538. server.channels.loadRegisteredChannels(config)
  539. }
  540. // resize history buffers as needed
  541. if oldConfig.History != config.History {
  542. for _, channel := range server.channels.Channels() {
  543. channel.resizeHistory(config)
  544. }
  545. for _, client := range server.clients.AllClients() {
  546. client.resizeHistory(config)
  547. }
  548. }
  549. }
  550. // activate the new config
  551. server.SetConfig(config)
  552. // burst new and removed caps
  553. addedCaps, removedCaps := config.Diff(oldConfig)
  554. var capBurstSessions []*Session
  555. added := make(map[caps.Version][]string)
  556. var removed []string
  557. if !addedCaps.Empty() || !removedCaps.Empty() {
  558. capBurstSessions = server.clients.AllWithCapsNotify()
  559. added[caps.Cap301] = addedCaps.Strings(caps.Cap301, config.Server.capValues, 0)
  560. added[caps.Cap302] = addedCaps.Strings(caps.Cap302, config.Server.capValues, 0)
  561. // removed never has values, so we leave it as Cap301
  562. removed = removedCaps.Strings(caps.Cap301, config.Server.capValues, 0)
  563. }
  564. for _, sSession := range capBurstSessions {
  565. // DEL caps and then send NEW ones so that updated caps get removed/added correctly
  566. if !removedCaps.Empty() {
  567. for _, capStr := range removed {
  568. sSession.Send(nil, server.name, "CAP", sSession.client.Nick(), "DEL", capStr)
  569. }
  570. }
  571. if !addedCaps.Empty() {
  572. for _, capStr := range added[sSession.capVersion] {
  573. sSession.Send(nil, server.name, "CAP", sSession.client.Nick(), "NEW", capStr)
  574. }
  575. }
  576. }
  577. server.logger.Info("server", "Using datastore", config.Datastore.Path)
  578. if initial {
  579. if err := server.loadDatastore(config); err != nil {
  580. return err
  581. }
  582. } else {
  583. if config.Datastore.MySQL.Enabled {
  584. server.historyDB.SetExpireTime(time.Duration(config.History.Restrictions.ExpireTime))
  585. }
  586. }
  587. server.setupPprofListener(config)
  588. // set RPL_ISUPPORT
  589. var newISupportReplies [][]string
  590. if oldConfig != nil {
  591. newISupportReplies = oldConfig.Server.isupport.GetDifference(&config.Server.isupport)
  592. }
  593. // we are now open for business
  594. err = server.setupListeners(config)
  595. if !initial {
  596. // push new info to all of our clients
  597. for _, sClient := range server.clients.AllClients() {
  598. for _, tokenline := range newISupportReplies {
  599. sClient.Send(nil, server.name, RPL_ISUPPORT, append([]string{sClient.nick}, tokenline...)...)
  600. }
  601. if sendRawOutputNotice {
  602. sClient.Notice(sClient.t("This server is in debug mode and is logging all user I/O. If you do not wish for everything you send to be readable by the server owner(s), please disconnect."))
  603. }
  604. if !oldConfig.Accounts.NickReservation.Enabled && config.Accounts.NickReservation.Enabled {
  605. sClient.nickTimer.Initialize(sClient)
  606. sClient.nickTimer.Touch(nil)
  607. } else if oldConfig.Accounts.NickReservation.Enabled && !config.Accounts.NickReservation.Enabled {
  608. sClient.nickTimer.Stop()
  609. }
  610. }
  611. }
  612. return err
  613. }
  614. func (server *Server) setupPprofListener(config *Config) {
  615. pprofListener := ""
  616. if config.Debug.PprofListener != nil {
  617. pprofListener = *config.Debug.PprofListener
  618. }
  619. if server.pprofServer != nil {
  620. if pprofListener == "" || (pprofListener != server.pprofServer.Addr) {
  621. server.logger.Info("server", "Stopping pprof listener", server.pprofServer.Addr)
  622. server.pprofServer.Close()
  623. server.pprofServer = nil
  624. }
  625. }
  626. if pprofListener != "" && server.pprofServer == nil {
  627. ps := http.Server{
  628. Addr: pprofListener,
  629. }
  630. go func() {
  631. if err := ps.ListenAndServe(); err != nil {
  632. server.logger.Error("server", "pprof listener failed", err.Error())
  633. }
  634. }()
  635. server.pprofServer = &ps
  636. server.logger.Info("server", "Started pprof listener", server.pprofServer.Addr)
  637. }
  638. }
  639. func (config *Config) loadMOTD() (err error) {
  640. if config.Server.MOTD != "" {
  641. file, err := os.Open(config.Server.MOTD)
  642. if err == nil {
  643. defer file.Close()
  644. reader := bufio.NewReader(file)
  645. for {
  646. line, err := reader.ReadString('\n')
  647. if err != nil {
  648. break
  649. }
  650. line = strings.TrimRight(line, "\r\n")
  651. if config.Server.MOTDFormatting {
  652. line = ircfmt.Unescape(line)
  653. }
  654. // "- " is the required prefix for MOTD, we just add it here to make
  655. // bursting it out to clients easier
  656. line = fmt.Sprintf("- %s", line)
  657. config.Server.motdLines = append(config.Server.motdLines, line)
  658. }
  659. }
  660. }
  661. return
  662. }
  663. func (server *Server) loadDatastore(config *Config) error {
  664. // open the datastore and load server state for which it (rather than config)
  665. // is the source of truth
  666. _, err := os.Stat(config.Datastore.Path)
  667. if os.IsNotExist(err) {
  668. server.logger.Warning("server", "database does not exist, creating it", config.Datastore.Path)
  669. err = initializeDB(config.Datastore.Path)
  670. if err != nil {
  671. return err
  672. }
  673. }
  674. db, err := OpenDatabase(config)
  675. if err == nil {
  676. server.store = db
  677. } else {
  678. return fmt.Errorf("Failed to open datastore: %s", err.Error())
  679. }
  680. // load *lines (from the datastores)
  681. server.logger.Debug("server", "Loading D/Klines")
  682. server.loadDLines()
  683. server.loadKLines()
  684. server.channelRegistry.Initialize(server)
  685. server.channels.Initialize(server)
  686. server.accounts.Initialize(server)
  687. if config.Datastore.MySQL.Enabled {
  688. server.historyDB.Initialize(server.logger, time.Duration(config.History.Restrictions.ExpireTime))
  689. err = server.historyDB.Open(config.Datastore.MySQL.User, config.Datastore.MySQL.Password, config.Datastore.MySQL.Host, config.Datastore.MySQL.Port, config.Datastore.MySQL.HistoryDatabase)
  690. if err != nil {
  691. server.logger.Error("internal", "could not connect to mysql", err.Error())
  692. return err
  693. }
  694. }
  695. return nil
  696. }
  697. func (server *Server) setupListeners(config *Config) (err error) {
  698. logListener := func(addr string, config listenerConfig) {
  699. server.logger.Info("listeners",
  700. fmt.Sprintf("now listening on %s, tls=%t, tlsproxy=%t, tor=%t.", addr, (config.TLSConfig != nil), config.ProxyBeforeTLS, config.Tor),
  701. )
  702. }
  703. // update or destroy all existing listeners
  704. for addr := range server.listeners {
  705. currentListener := server.listeners[addr]
  706. newConfig, stillConfigured := config.Server.trueListeners[addr]
  707. currentListener.Lock()
  708. currentListener.shouldStop = !stillConfigured
  709. currentListener.config = newConfig
  710. currentListener.Unlock()
  711. if stillConfigured {
  712. logListener(addr, newConfig)
  713. } else {
  714. // tell the listener it should stop by interrupting its Accept() call:
  715. currentListener.listener.Close()
  716. delete(server.listeners, addr)
  717. server.logger.Info("listeners", fmt.Sprintf("stopped listening on %s.", addr))
  718. }
  719. }
  720. publicPlaintextListener := ""
  721. // create new listeners that were not previously configured
  722. for newAddr, newConfig := range config.Server.trueListeners {
  723. if strings.HasPrefix(newAddr, ":") && !newConfig.Tor && !newConfig.STSOnly && newConfig.TLSConfig == nil {
  724. publicPlaintextListener = newAddr
  725. }
  726. _, exists := server.listeners[newAddr]
  727. if !exists {
  728. // make new listener
  729. listener, listenerErr := server.createListener(newAddr, newConfig, config.Server.UnixBindMode)
  730. if listenerErr != nil {
  731. server.logger.Error("server", "couldn't listen on", newAddr, listenerErr.Error())
  732. err = listenerErr
  733. continue
  734. }
  735. server.listeners[newAddr] = listener
  736. logListener(newAddr, newConfig)
  737. }
  738. }
  739. if publicPlaintextListener != "" {
  740. server.logger.Warning("listeners", fmt.Sprintf("Your server is configured with public plaintext listener %s. Consider disabling it for improved security and privacy.", publicPlaintextListener))
  741. }
  742. return
  743. }
  744. // Gets the abstract sequence from which we're going to query history;
  745. // we may already know the channel we're querying, or we may have
  746. // to look it up via a string target. This function is responsible for
  747. // privilege checking.
  748. func (server *Server) GetHistorySequence(providedChannel *Channel, client *Client, target string) (channel *Channel, sequence history.Sequence, err error) {
  749. config := server.Config()
  750. var sender, recipient string
  751. var hist *history.Buffer
  752. if target == "*" {
  753. if client.AlwaysOn() {
  754. recipient = client.NickCasefolded()
  755. } else {
  756. hist = &client.history
  757. }
  758. } else {
  759. channel = providedChannel
  760. if channel == nil {
  761. channel = server.channels.Get(target)
  762. }
  763. if channel != nil {
  764. if !channel.hasClient(client) {
  765. err = errInsufficientPrivs
  766. return
  767. }
  768. persistent, ephemeral, cfTarget := channel.historyStatus(config)
  769. if persistent {
  770. recipient = cfTarget
  771. } else if ephemeral {
  772. hist = &channel.history
  773. } else {
  774. return
  775. }
  776. } else {
  777. sender = client.NickCasefolded()
  778. var cfTarget string
  779. cfTarget, err = CasefoldName(target)
  780. if err != nil {
  781. return
  782. }
  783. recipient = cfTarget
  784. if !client.AlwaysOn() {
  785. hist = &client.history
  786. }
  787. }
  788. }
  789. var cutoff time.Time
  790. if config.History.Restrictions.ExpireTime != 0 {
  791. cutoff = time.Now().UTC().Add(-time.Duration(config.History.Restrictions.ExpireTime))
  792. }
  793. if config.History.Restrictions.EnforceRegistrationDate {
  794. regCutoff := client.historyCutoff()
  795. regCutoff.Add(-time.Duration(config.History.Restrictions.GracePeriod))
  796. // take the earlier of the two cutoffs
  797. if regCutoff.After(cutoff) {
  798. cutoff = regCutoff
  799. }
  800. }
  801. if hist != nil {
  802. sequence = hist.MakeSequence(recipient, cutoff)
  803. } else if recipient != "" {
  804. sequence = server.historyDB.MakeSequence(sender, recipient, cutoff)
  805. }
  806. return
  807. }
  808. // elistMatcher takes and matches ELIST conditions
  809. type elistMatcher struct {
  810. MinClientsActive bool
  811. MinClients int
  812. MaxClientsActive bool
  813. MaxClients int
  814. }
  815. // Matches checks whether the given channel matches our matches.
  816. func (matcher *elistMatcher) Matches(channel *Channel) bool {
  817. if matcher.MinClientsActive {
  818. if len(channel.Members()) < matcher.MinClients {
  819. return false
  820. }
  821. }
  822. if matcher.MaxClientsActive {
  823. if len(channel.Members()) < len(channel.members) {
  824. return false
  825. }
  826. }
  827. return true
  828. }
  829. // RplList returns the RPL_LIST numeric for the given channel.
  830. func (target *Client) RplList(channel *Channel, rb *ResponseBuffer) {
  831. // get the correct number of channel members
  832. var memberCount int
  833. if target.HasMode(modes.Operator) || channel.hasClient(target) {
  834. memberCount = len(channel.Members())
  835. } else {
  836. for _, member := range channel.Members() {
  837. if !member.HasMode(modes.Invisible) {
  838. memberCount++
  839. }
  840. }
  841. }
  842. rb.Add(nil, target.server.name, RPL_LIST, target.nick, channel.name, strconv.Itoa(memberCount), channel.topic)
  843. }
  844. var (
  845. infoString1 = strings.Split(` ▄▄▄ ▄▄▄· ▄▄ • ▐ ▄
  846. ▪ ▀▄ █·▐█ ▀█ ▐█ ▀ ▪▪ •█▌▐█▪
  847. ▄█▀▄ ▐▀▀▄ ▄█▀▀█ ▄█ ▀█▄ ▄█▀▄▪▐█▐▐▌ ▄█▀▄
  848. ▐█▌.▐▌▐█•█▌▐█ ▪▐▌▐█▄▪▐█▐█▌ ▐▌██▐█▌▐█▌.▐▌
  849. ▀█▄▀▪.▀ ▀ ▀ ▀ ·▀▀▀▀ ▀█▄▀ ▀▀ █▪ ▀█▄▀▪
  850. https://oragono.io/
  851. https://github.com/oragono/oragono
  852. https://crowdin.com/project/oragono
  853. `, "\n")
  854. infoString2 = strings.Split(` Daniel Oakley, DanielOaks, <daniel@danieloaks.net>
  855. Shivaram Lingamneni, slingamn, <slingamn@cs.stanford.edu>
  856. `, "\n")
  857. infoString3 = strings.Split(` 3onyc
  858. Edmund Huber
  859. Euan Kemp (euank)
  860. Jeremy Latt
  861. Martin Lindhe (martinlindhe)
  862. Roberto Besser (besser)
  863. Robin Burchell (rburchell)
  864. Sean Enck (enckse)
  865. soul9
  866. Vegax
  867. `, "\n")
  868. )