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.

client.go 16KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609
  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. "errors"
  8. "fmt"
  9. "log"
  10. "net"
  11. "runtime/debug"
  12. "strconv"
  13. "strings"
  14. "time"
  15. "github.com/DanielOaks/girc-go/ircmsg"
  16. "github.com/DanielOaks/go-ident"
  17. )
  18. const (
  19. IDLE_TIMEOUT = time.Minute + time.Second*30 // how long before a client is considered idle
  20. QUIT_TIMEOUT = time.Minute // how long after idle before a client is kicked
  21. IdentTimeoutSeconds = 5
  22. )
  23. var (
  24. TIMEOUT_STATED_SECONDS = strconv.Itoa(int((IDLE_TIMEOUT + QUIT_TIMEOUT).Seconds()))
  25. ErrNickAlreadySet = errors.New("Nickname is already set")
  26. )
  27. // Client is an IRC client.
  28. type Client struct {
  29. account *ClientAccount
  30. atime time.Time
  31. authorized bool
  32. awayMessage string
  33. capabilities CapabilitySet
  34. capState CapState
  35. capVersion CapVersion
  36. certfp string
  37. channels ChannelSet
  38. class *OperClass
  39. ctime time.Time
  40. flags map[Mode]bool
  41. isDestroyed bool
  42. isQuitting bool
  43. hasQuit bool
  44. hops int
  45. hostname string
  46. rawHostname string
  47. vhost string
  48. idleTimer *time.Timer
  49. monitoring map[string]bool
  50. nick string
  51. nickCasefolded string
  52. nickMaskString string // cache for nickmask string since it's used with lots of replies
  53. nickMaskCasefolded string
  54. operName string
  55. quitTimer *time.Timer
  56. quitMessageSent bool
  57. realname string
  58. registered bool
  59. saslInProgress bool
  60. saslMechanism string
  61. saslValue string
  62. server *Server
  63. socket *Socket
  64. username string
  65. whoisLine string
  66. }
  67. // NewClient returns a client with all the appropriate info setup.
  68. func NewClient(server *Server, conn net.Conn, isTLS bool) *Client {
  69. now := time.Now()
  70. socket := NewSocket(conn, server.MaxSendQBytes)
  71. go socket.RunSocketWriter()
  72. client := &Client{
  73. atime: now,
  74. authorized: server.password == nil,
  75. capabilities: make(CapabilitySet),
  76. capState: CapNone,
  77. capVersion: Cap301,
  78. channels: make(ChannelSet),
  79. ctime: now,
  80. flags: make(map[Mode]bool),
  81. monitoring: make(map[string]bool),
  82. server: server,
  83. socket: &socket,
  84. account: &NoAccount,
  85. nick: "*", // * is used until actual nick is given
  86. nickCasefolded: "*",
  87. nickMaskString: "*", // * is used until actual nick is given
  88. }
  89. if isTLS {
  90. client.flags[TLS] = true
  91. // error is not useful to us here anyways so we can ignore it
  92. client.certfp, _ = client.socket.CertFP()
  93. }
  94. if server.checkIdent {
  95. _, serverPortString, err := net.SplitHostPort(conn.LocalAddr().String())
  96. serverPort, _ := strconv.Atoi(serverPortString)
  97. if err != nil {
  98. log.Fatal(err)
  99. }
  100. clientHost, clientPortString, err := net.SplitHostPort(conn.RemoteAddr().String())
  101. clientPort, _ := strconv.Atoi(clientPortString)
  102. if err != nil {
  103. log.Fatal(err)
  104. }
  105. client.Notice("*** Looking up your username")
  106. resp, err := ident.Query(clientHost, serverPort, clientPort, IdentTimeoutSeconds)
  107. if err == nil {
  108. username := resp.Identifier
  109. _, err := CasefoldName(username) // ensure it's a valid username
  110. if err == nil {
  111. client.Notice("*** Found your username")
  112. client.username = username
  113. // we don't need to updateNickMask here since nickMask is not used for anything yet
  114. } else {
  115. client.Notice("*** Got a malformed username, ignoring")
  116. }
  117. } else {
  118. client.Notice("*** Could not find your username")
  119. }
  120. }
  121. client.Touch()
  122. go client.run()
  123. return client
  124. }
  125. //
  126. // command goroutine
  127. //
  128. func (client *Client) maxlens() (int, int) {
  129. maxlenTags := 512
  130. maxlenRest := 512
  131. if client.capabilities[MessageTags] {
  132. maxlenTags = 4096
  133. }
  134. if client.capabilities[MaxLine] {
  135. if client.server.limits.LineLen.Tags > maxlenTags {
  136. maxlenTags = client.server.limits.LineLen.Tags
  137. }
  138. maxlenRest = client.server.limits.LineLen.Rest
  139. }
  140. return maxlenTags, maxlenRest
  141. }
  142. func (client *Client) run() {
  143. var err error
  144. var isExiting bool
  145. var line string
  146. var msg ircmsg.IrcMessage
  147. // Set the hostname for this client
  148. client.rawHostname = AddrLookupHostname(client.socket.conn.RemoteAddr())
  149. //TODO(dan): Make this a socketreactor from ircbnc
  150. for {
  151. line, err = client.socket.Read()
  152. if err != nil {
  153. client.Quit("connection closed")
  154. break
  155. }
  156. maxlenTags, maxlenRest := client.maxlens()
  157. client.server.logger.Debug("userinput ", client.nick, "<- ", line)
  158. msg, err = ircmsg.ParseLineMaxLen(line, maxlenTags, maxlenRest)
  159. if err == ircmsg.ErrorLineIsEmpty {
  160. continue
  161. } else if err != nil {
  162. client.Quit("received malformed line")
  163. break
  164. }
  165. cmd, exists := Commands[msg.Command]
  166. if !exists {
  167. if len(msg.Command) > 0 {
  168. client.Send(nil, client.server.name, ERR_UNKNOWNCOMMAND, client.nick, msg.Command, "Unknown command")
  169. } else {
  170. client.Send(nil, client.server.name, ERR_UNKNOWNCOMMAND, client.nick, "lastcmd", "No command given")
  171. }
  172. continue
  173. }
  174. isExiting = cmd.Run(client.server, client, msg)
  175. if isExiting || client.isQuitting {
  176. break
  177. }
  178. }
  179. // ensure client connection gets closed
  180. client.destroy()
  181. }
  182. //
  183. // quit timer goroutine
  184. //
  185. func (client *Client) connectionTimeout() {
  186. client.Quit(fmt.Sprintf("Ping timeout: %s seconds", TIMEOUT_STATED_SECONDS))
  187. client.isQuitting = true
  188. }
  189. //
  190. // idle timer goroutine
  191. //
  192. func (client *Client) connectionIdle() {
  193. client.server.idle <- client
  194. }
  195. //
  196. // server goroutine
  197. //
  198. // Active marks the client as 'active' (i.e. the user should be there).
  199. func (client *Client) Active() {
  200. client.atime = time.Now()
  201. }
  202. // Touch marks the client as alive.
  203. func (client *Client) Touch() {
  204. if client.quitTimer != nil {
  205. client.quitTimer.Stop()
  206. }
  207. if client.idleTimer == nil {
  208. client.idleTimer = time.AfterFunc(IDLE_TIMEOUT, client.connectionIdle)
  209. } else {
  210. client.idleTimer.Reset(IDLE_TIMEOUT)
  211. }
  212. }
  213. // Idle resets the timeout handlers and sends the client a PING.
  214. func (client *Client) Idle() {
  215. client.Send(nil, "", "PING", client.nick)
  216. if client.quitTimer == nil {
  217. client.quitTimer = time.AfterFunc(QUIT_TIMEOUT, client.connectionTimeout)
  218. } else {
  219. client.quitTimer.Reset(QUIT_TIMEOUT)
  220. }
  221. }
  222. // Register sets the client details as appropriate when entering the network.
  223. func (client *Client) Register() {
  224. if client.registered {
  225. return
  226. }
  227. client.registered = true
  228. client.Touch()
  229. client.updateNickMask()
  230. client.alertMonitors()
  231. }
  232. // IdleTime returns how long this client's been idle.
  233. func (client *Client) IdleTime() time.Duration {
  234. return time.Since(client.atime)
  235. }
  236. // SignonTime returns this client's signon time as a unix timestamp.
  237. func (client *Client) SignonTime() int64 {
  238. return client.ctime.Unix()
  239. }
  240. // IdleSeconds returns the number of seconds this client's been idle.
  241. func (client *Client) IdleSeconds() uint64 {
  242. return uint64(client.IdleTime().Seconds())
  243. }
  244. // HasNick returns true if the client's nickname is set (used in registration).
  245. func (client *Client) HasNick() bool {
  246. return client.nick != "" && client.nick != "*"
  247. }
  248. // HasUsername returns true if the client's username is set (used in registration).
  249. func (client *Client) HasUsername() bool {
  250. return client.username != "" && client.username != "*"
  251. }
  252. // HasCapabs returns true if client has the given (role) capabilities.
  253. func (client *Client) HasCapabs(capabs ...string) bool {
  254. if client.class == nil {
  255. return false
  256. }
  257. for _, capab := range capabs {
  258. if !client.class.Capabilities[capab] {
  259. return false
  260. }
  261. }
  262. return true
  263. }
  264. // ModeString returns the mode string for this client.
  265. func (client *Client) ModeString() (str string) {
  266. str = "+"
  267. for flag := range client.flags {
  268. str += flag.String()
  269. }
  270. return
  271. }
  272. // Friends refers to clients that share a channel with this client.
  273. func (client *Client) Friends(Capabilities ...Capability) ClientSet {
  274. friends := make(ClientSet)
  275. // make sure that I have the right caps
  276. hasCaps := true
  277. for _, Cap := range Capabilities {
  278. if !client.capabilities[Cap] {
  279. hasCaps = false
  280. break
  281. }
  282. }
  283. if hasCaps {
  284. friends.Add(client)
  285. }
  286. for channel := range client.channels {
  287. channel.membersMutex.RLock()
  288. for member := range channel.members {
  289. // make sure they have all the required caps
  290. for _, Cap := range Capabilities {
  291. if !member.capabilities[Cap] {
  292. continue
  293. }
  294. }
  295. friends.Add(member)
  296. }
  297. channel.membersMutex.RUnlock()
  298. }
  299. return friends
  300. }
  301. // updateNick updates the casefolded nickname.
  302. func (client *Client) updateNick() {
  303. casefoldedName, err := CasefoldName(client.nick)
  304. if err != nil {
  305. log.Println(fmt.Sprintf("ERROR: Nick [%s] couldn't be casefolded... this should never happen. Printing stacktrace.", client.nick))
  306. debug.PrintStack()
  307. }
  308. client.nickCasefolded = casefoldedName
  309. }
  310. // updateNickMask updates the casefolded nickname and nickmask.
  311. func (client *Client) updateNickMask() {
  312. client.updateNick()
  313. if len(client.vhost) > 0 {
  314. client.hostname = client.vhost
  315. } else {
  316. client.hostname = client.rawHostname
  317. }
  318. client.nickMaskString = fmt.Sprintf("%s!%s@%s", client.nick, client.username, client.hostname)
  319. nickMaskCasefolded, err := Casefold(client.nickMaskString)
  320. if err != nil {
  321. log.Println(fmt.Sprintf("ERROR: Nickmask [%s] couldn't be casefolded... this should never happen. Printing stacktrace.", client.nickMaskString))
  322. debug.PrintStack()
  323. }
  324. client.nickMaskCasefolded = nickMaskCasefolded
  325. }
  326. // AllNickmasks returns all the possible nickmasks for the client.
  327. func (client *Client) AllNickmasks() []string {
  328. var masks []string
  329. var mask string
  330. var err error
  331. if len(client.vhost) > 0 {
  332. mask, err = Casefold(fmt.Sprintf("%s!%s@%s", client.nick, client.username, client.vhost))
  333. if err == nil {
  334. masks = append(masks, mask)
  335. }
  336. }
  337. mask, err = Casefold(fmt.Sprintf("%s!%s@%s", client.nick, client.username, client.rawHostname))
  338. if err == nil {
  339. masks = append(masks, mask)
  340. }
  341. mask2, err := Casefold(fmt.Sprintf("%s!%s@%s", client.nick, client.username, IPString(client.socket.conn.RemoteAddr())))
  342. if err == nil && mask2 != mask {
  343. masks = append(masks, mask2)
  344. }
  345. return masks
  346. }
  347. // SetNickname sets the very first nickname for the client.
  348. func (client *Client) SetNickname(nickname string) error {
  349. if client.HasNick() {
  350. client.server.logger.Error("nick", fmt.Sprintf("%s nickname already set, something is wrong with server consistency", client.nickMaskString))
  351. return ErrNickAlreadySet
  352. }
  353. err := client.server.clients.Add(client, nickname)
  354. if err == nil {
  355. client.nick = nickname
  356. client.updateNick()
  357. }
  358. return err
  359. }
  360. // ChangeNickname changes the existing nickname of the client.
  361. func (client *Client) ChangeNickname(nickname string) error {
  362. origNickMask := client.nickMaskString
  363. err := client.server.clients.Replace(client.nick, nickname, client)
  364. if err == nil {
  365. client.server.logger.Debug("nick", fmt.Sprintf("%s changed nickname to %s", client.nick, nickname))
  366. client.server.whoWas.Append(client)
  367. client.nick = nickname
  368. client.updateNickMask()
  369. for friend := range client.Friends() {
  370. friend.Send(nil, origNickMask, "NICK", nickname)
  371. }
  372. }
  373. return err
  374. }
  375. // Quit sends the given quit message to the client (but does not destroy them).
  376. func (client *Client) Quit(message string) {
  377. if !client.quitMessageSent {
  378. client.Send(nil, client.nickMaskString, "QUIT", message)
  379. client.Send(nil, "", "ERROR", message)
  380. client.quitMessageSent = true
  381. }
  382. }
  383. // destroy gets rid of a client, removes them from server lists etc.
  384. func (client *Client) destroy() {
  385. if client.isDestroyed {
  386. return
  387. }
  388. client.server.logger.Debug("quit", fmt.Sprintf("%s is no longer on the server", client.nick))
  389. // send quit/error message to client if they haven't been sent already
  390. client.Quit("Connection closed")
  391. client.isDestroyed = true
  392. client.server.whoWas.Append(client)
  393. friends := client.Friends()
  394. friends.Remove(client)
  395. // remove from connection limits
  396. ipaddr := net.ParseIP(IPString(client.socket.conn.RemoteAddr()))
  397. // this check shouldn't be required but eh
  398. if ipaddr != nil {
  399. client.server.connectionLimitsMutex.Lock()
  400. client.server.connectionLimits.RemoveClient(ipaddr)
  401. client.server.connectionLimitsMutex.Unlock()
  402. }
  403. // remove from opers list
  404. _, exists := client.server.currentOpers[client]
  405. if exists {
  406. delete(client.server.currentOpers, client)
  407. }
  408. // alert monitors
  409. for _, mClient := range client.server.monitoring[client.nickCasefolded] {
  410. mClient.Send(nil, client.server.name, RPL_MONOFFLINE, mClient.nick, client.nick)
  411. }
  412. // remove my monitors
  413. client.clearMonitorList()
  414. // clean up channels
  415. for channel := range client.channels {
  416. channel.Quit(client, &friends)
  417. }
  418. // clean up server
  419. client.server.clients.Remove(client)
  420. // clean up self
  421. if client.idleTimer != nil {
  422. client.idleTimer.Stop()
  423. }
  424. if client.quitTimer != nil {
  425. client.quitTimer.Stop()
  426. }
  427. client.socket.Close()
  428. // send quit messages to friends
  429. for friend := range friends {
  430. //TODO(dan): store quit message in user, if exists use that instead here
  431. friend.Send(nil, client.nickMaskString, "QUIT", "Exited")
  432. }
  433. }
  434. // SendSplitMsgFromClient sends an IRC PRIVMSG/NOTICE coming from a specific client.
  435. // Adds account-tag to the line as well.
  436. func (client *Client) SendSplitMsgFromClient(msgid string, from *Client, tags *map[string]ircmsg.TagValue, command, target string, message SplitMessage) {
  437. if client.capabilities[MaxLine] {
  438. client.SendFromClient(msgid, from, tags, command, target, message.ForMaxLine)
  439. } else {
  440. for _, str := range message.For512 {
  441. client.SendFromClient(msgid, from, tags, command, target, str)
  442. }
  443. }
  444. }
  445. // SendFromClient sends an IRC line coming from a specific client.
  446. // Adds account-tag to the line as well.
  447. func (client *Client) SendFromClient(msgid string, from *Client, tags *map[string]ircmsg.TagValue, command string, params ...string) error {
  448. // attach account-tag
  449. if client.capabilities[AccountTag] && from.account != &NoAccount {
  450. if tags == nil {
  451. tags = ircmsg.MakeTags("account", from.account.Name)
  452. } else {
  453. (*tags)["account"] = ircmsg.MakeTagValue(from.account.Name)
  454. }
  455. }
  456. // attach message-id
  457. if len(msgid) > 0 && client.capabilities[MessageIDs] {
  458. if tags == nil {
  459. tags = ircmsg.MakeTags("draft/msgid", msgid)
  460. } else {
  461. (*tags)["draft/msgid"] = ircmsg.MakeTagValue(msgid)
  462. }
  463. }
  464. return client.Send(tags, from.nickMaskString, command, params...)
  465. }
  466. var (
  467. // these are all the output commands that MUST have their last param be a trailing.
  468. // this is needed because silly clients like to treat trailing as separate from the
  469. // other params in messages.
  470. commandsThatMustUseTrailing = map[string]bool{
  471. "PRIVMSG": true,
  472. "NOTICE": true,
  473. RPL_WHOISCHANNELS: true,
  474. RPL_USERHOST: true,
  475. }
  476. )
  477. // Send sends an IRC line to the client.
  478. func (client *Client) Send(tags *map[string]ircmsg.TagValue, prefix string, command string, params ...string) error {
  479. // attach server-time
  480. if client.capabilities[ServerTime] {
  481. t := time.Now().UTC().Format("2006-01-02T15:04:05.999Z")
  482. if tags == nil {
  483. tags = ircmsg.MakeTags("time", t)
  484. } else {
  485. (*tags)["time"] = ircmsg.MakeTagValue(t)
  486. }
  487. }
  488. // force trailing
  489. var usedSpaceHack bool
  490. if commandsThatMustUseTrailing[strings.ToUpper(command)] && len(params) > 0 {
  491. lastParam := params[len(params)-1]
  492. if !strings.Contains(lastParam, " ") {
  493. params[len(params)-1] = lastParam + " "
  494. usedSpaceHack = true
  495. }
  496. }
  497. // send out the message
  498. message := ircmsg.MakeMessage(tags, prefix, command, params...)
  499. maxlenTags, maxlenRest := client.maxlens()
  500. line, err := message.LineMaxLen(maxlenTags, maxlenRest)
  501. if err != nil {
  502. // try not to fail quietly - especially useful when running tests, as a note to dig deeper
  503. // log.Println("Error assembling message:")
  504. // spew.Dump(message)
  505. // debug.PrintStack()
  506. message = ircmsg.MakeMessage(nil, client.server.name, ERR_UNKNOWNERROR, "*", "Error assembling message for sending")
  507. line, _ := message.Line()
  508. client.socket.Write(line)
  509. return err
  510. }
  511. // strip space hack if we used it
  512. if usedSpaceHack {
  513. line = line[:len(line)-3] + "\r\n"
  514. }
  515. client.server.logger.Debug("useroutput", client.nick, " ->", strings.TrimRight(line, "\r\n"))
  516. client.socket.Write(line)
  517. return nil
  518. }
  519. // Notice sends the client a notice from the server.
  520. func (client *Client) Notice(text string) {
  521. client.Send(nil, client.server.name, "NOTICE", client.nick, text)
  522. }