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_lookup_set.go 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432
  1. // Copyright (c) 2012-2014 Jeremy Latt
  2. // Copyright (c) 2016-2017 Daniel Oaks <daniel@danieloaks.net>
  3. // released under the MIT license
  4. package irc
  5. import (
  6. "regexp"
  7. "strings"
  8. "sync"
  9. "time"
  10. "github.com/oragono/oragono/irc/caps"
  11. "github.com/oragono/oragono/irc/modes"
  12. "github.com/oragono/oragono/irc/utils"
  13. )
  14. // ClientManager keeps track of clients by nick, enforcing uniqueness of casefolded nicks
  15. type ClientManager struct {
  16. sync.RWMutex // tier 2
  17. byNick map[string]*Client
  18. bySkeleton map[string]*Client
  19. }
  20. // Initialize initializes a ClientManager.
  21. func (clients *ClientManager) Initialize() {
  22. clients.byNick = make(map[string]*Client)
  23. clients.bySkeleton = make(map[string]*Client)
  24. }
  25. // Get retrieves a client from the manager, if they exist.
  26. func (clients *ClientManager) Get(nick string) *Client {
  27. casefoldedName, err := CasefoldName(nick)
  28. if err == nil {
  29. clients.RLock()
  30. defer clients.RUnlock()
  31. cli := clients.byNick[casefoldedName]
  32. return cli
  33. }
  34. return nil
  35. }
  36. func (clients *ClientManager) removeInternal(client *Client, oldcfnick, oldskeleton string) (err error) {
  37. // requires holding the writable Lock()
  38. if oldcfnick == "*" || oldcfnick == "" {
  39. return errNickMissing
  40. }
  41. currentEntry, present := clients.byNick[oldcfnick]
  42. if present {
  43. if currentEntry == client {
  44. delete(clients.byNick, oldcfnick)
  45. } else {
  46. // this shouldn't happen, but we can ignore it
  47. client.server.logger.Warning("internal", "clients for nick out of sync", oldcfnick)
  48. err = errNickMissing
  49. }
  50. } else {
  51. err = errNickMissing
  52. }
  53. currentEntry, present = clients.bySkeleton[oldskeleton]
  54. if present {
  55. if currentEntry == client {
  56. delete(clients.bySkeleton, oldskeleton)
  57. } else {
  58. client.server.logger.Warning("internal", "clients for skeleton out of sync", oldskeleton)
  59. err = errNickMissing
  60. }
  61. } else {
  62. err = errNickMissing
  63. }
  64. return
  65. }
  66. // Remove removes a client from the lookup set.
  67. func (clients *ClientManager) Remove(client *Client) error {
  68. clients.Lock()
  69. defer clients.Unlock()
  70. oldcfnick, oldskeleton := client.uniqueIdentifiers()
  71. return clients.removeInternal(client, oldcfnick, oldskeleton)
  72. }
  73. // Handles a RESUME by attaching a session to a designated client. It is the
  74. // caller's responsibility to verify that the resume is allowed (checking tokens,
  75. // TLS status, etc.) before calling this.
  76. func (clients *ClientManager) Resume(oldClient *Client, session *Session) (err error) {
  77. clients.Lock()
  78. defer clients.Unlock()
  79. cfnick := oldClient.NickCasefolded()
  80. if _, ok := clients.byNick[cfnick]; !ok {
  81. return errNickMissing
  82. }
  83. success, _, _, _ := oldClient.AddSession(session)
  84. if !success {
  85. return errNickMissing
  86. }
  87. return nil
  88. }
  89. // SetNick sets a client's nickname, validating it against nicknames in use
  90. func (clients *ClientManager) SetNick(client *Client, session *Session, newNick string) (setNick string, err error, returnedFromAway bool) {
  91. config := client.server.Config()
  92. var newCfNick, newSkeleton string
  93. client.stateMutex.RLock()
  94. account := client.account
  95. accountName := client.accountName
  96. settings := client.accountSettings
  97. registered := client.registered
  98. realname := client.realname
  99. client.stateMutex.RUnlock()
  100. // recompute always-on status, because client.alwaysOn is not set for unregistered clients
  101. var alwaysOn, useAccountName bool
  102. if account != "" {
  103. alwaysOn = persistenceEnabled(config.Accounts.Multiclient.AlwaysOn, settings.AlwaysOn)
  104. useAccountName = alwaysOn || config.Accounts.NickReservation.ForceNickEqualsAccount
  105. }
  106. if useAccountName {
  107. if registered && newNick != accountName && newNick != "" {
  108. return "", errNickAccountMismatch, false
  109. }
  110. newNick = accountName
  111. newCfNick = account
  112. newSkeleton, err = Skeleton(newNick)
  113. if err != nil {
  114. return "", errNicknameInvalid, false
  115. }
  116. } else {
  117. newNick = strings.TrimSpace(newNick)
  118. if len(newNick) == 0 {
  119. return "", errNickMissing, false
  120. }
  121. if account == "" && config.Accounts.NickReservation.ForceGuestFormat {
  122. newCfNick, err = CasefoldName(newNick)
  123. if err != nil {
  124. return "", errNicknameInvalid, false
  125. }
  126. if !config.Accounts.NickReservation.guestRegexpFolded.MatchString(newCfNick) {
  127. newNick = strings.Replace(config.Accounts.NickReservation.GuestFormat, "*", newNick, 1)
  128. newCfNick = "" // re-fold it below
  129. }
  130. }
  131. if newCfNick == "" {
  132. newCfNick, err = CasefoldName(newNick)
  133. }
  134. if err != nil {
  135. return "", errNicknameInvalid, false
  136. }
  137. if len(newNick) > config.Limits.NickLen || len(newCfNick) > config.Limits.NickLen {
  138. return "", errNicknameInvalid, false
  139. }
  140. newSkeleton, err = Skeleton(newNick)
  141. if err != nil {
  142. return "", errNicknameInvalid, false
  143. }
  144. if config.isRelaymsgIdentifier(newNick) {
  145. return "", errNicknameInvalid, false
  146. }
  147. if restrictedCasefoldedNicks.Has(newCfNick) || restrictedSkeletons.Has(newSkeleton) {
  148. return "", errNicknameInvalid, false
  149. }
  150. reservedAccount, method := client.server.accounts.EnforcementStatus(newCfNick, newSkeleton)
  151. if method == NickEnforcementStrict && reservedAccount != "" && reservedAccount != account {
  152. return "", errNicknameReserved, false
  153. }
  154. }
  155. var bouncerAllowed bool
  156. if config.Accounts.Multiclient.Enabled {
  157. if useAccountName {
  158. bouncerAllowed = true
  159. } else {
  160. if config.Accounts.Multiclient.AllowedByDefault && settings.AllowBouncer != MulticlientDisallowedByUser {
  161. bouncerAllowed = true
  162. } else if settings.AllowBouncer == MulticlientAllowedByUser {
  163. bouncerAllowed = true
  164. }
  165. }
  166. }
  167. clients.Lock()
  168. defer clients.Unlock()
  169. currentClient := clients.byNick[newCfNick]
  170. // the client may just be changing case
  171. if currentClient != nil && currentClient != client && session != nil {
  172. // these conditions forbid reattaching to an existing session:
  173. if registered || !bouncerAllowed || account == "" || account != currentClient.Account() {
  174. return "", errNicknameInUse, false
  175. }
  176. // check TLS modes
  177. if client.HasMode(modes.TLS) != currentClient.HasMode(modes.TLS) {
  178. if useAccountName {
  179. // #955: this is fatal because they can't fix it by trying a different nick
  180. return "", errInsecureReattach, false
  181. } else {
  182. return "", errNicknameInUse, false
  183. }
  184. }
  185. reattachSuccessful, numSessions, lastSeen, back := currentClient.AddSession(session)
  186. if !reattachSuccessful {
  187. return "", errNicknameInUse, false
  188. }
  189. if numSessions == 1 {
  190. invisible := currentClient.HasMode(modes.Invisible)
  191. operator := currentClient.HasMode(modes.Operator) || currentClient.HasMode(modes.LocalOperator)
  192. client.server.stats.AddRegistered(invisible, operator)
  193. }
  194. session.autoreplayMissedSince = lastSeen
  195. // TODO: transition mechanism for #1065, clean this up eventually:
  196. if currentClient.Realname() == "" {
  197. currentClient.SetRealname(realname)
  198. }
  199. // successful reattach!
  200. return newNick, nil, back
  201. } else if currentClient == client && currentClient.Nick() == newNick {
  202. return "", errNoop, false
  203. }
  204. // analogous checks for skeletons
  205. skeletonHolder := clients.bySkeleton[newSkeleton]
  206. if skeletonHolder != nil && skeletonHolder != client {
  207. return "", errNicknameInUse, false
  208. }
  209. formercfnick, formerskeleton := client.uniqueIdentifiers()
  210. if changeSuccess := client.SetNick(newNick, newCfNick, newSkeleton); !changeSuccess {
  211. return "", errClientDestroyed, false
  212. }
  213. clients.removeInternal(client, formercfnick, formerskeleton)
  214. clients.byNick[newCfNick] = client
  215. clients.bySkeleton[newSkeleton] = client
  216. return newNick, nil, false
  217. }
  218. func (clients *ClientManager) AllClients() (result []*Client) {
  219. clients.RLock()
  220. defer clients.RUnlock()
  221. result = make([]*Client, len(clients.byNick))
  222. i := 0
  223. for _, client := range clients.byNick {
  224. result[i] = client
  225. i++
  226. }
  227. return
  228. }
  229. // AllWithCapsNotify returns all clients with the given capabilities, and that support cap-notify.
  230. func (clients *ClientManager) AllWithCapsNotify(capabs ...caps.Capability) (sessions []*Session) {
  231. capabs = append(capabs, caps.CapNotify)
  232. clients.RLock()
  233. defer clients.RUnlock()
  234. for _, client := range clients.byNick {
  235. for _, session := range client.Sessions() {
  236. // cap-notify is implicit in cap version 302 and above
  237. if session.capabilities.HasAll(capabs...) || 302 <= session.capVersion {
  238. sessions = append(sessions, session)
  239. }
  240. }
  241. }
  242. return
  243. }
  244. // FindAll returns all clients that match the given userhost mask.
  245. func (clients *ClientManager) FindAll(userhost string) (set ClientSet) {
  246. set = make(ClientSet)
  247. userhost, err := CanonicalizeMaskWildcard(userhost)
  248. if err != nil {
  249. return set
  250. }
  251. matcher, err := utils.CompileGlob(userhost, false)
  252. if err != nil {
  253. // not much we can do here
  254. return
  255. }
  256. clients.RLock()
  257. defer clients.RUnlock()
  258. for _, client := range clients.byNick {
  259. if matcher.MatchString(client.NickMaskCasefolded()) {
  260. set.Add(client)
  261. }
  262. }
  263. return set
  264. }
  265. //
  266. // usermask to regexp
  267. //
  268. //TODO(dan): move this over to generally using glob syntax instead?
  269. // kinda more expected in normal ban/etc masks, though regex is useful (probably as an extban?)
  270. type MaskInfo struct {
  271. TimeCreated time.Time
  272. CreatorNickmask string
  273. CreatorAccount string
  274. }
  275. // UserMaskSet holds a set of client masks and lets you match hostnames to them.
  276. type UserMaskSet struct {
  277. sync.RWMutex
  278. serialCacheUpdateMutex sync.Mutex
  279. masks map[string]MaskInfo
  280. regexp *regexp.Regexp
  281. }
  282. func NewUserMaskSet() *UserMaskSet {
  283. return new(UserMaskSet)
  284. }
  285. // Add adds the given mask to this set.
  286. func (set *UserMaskSet) Add(mask, creatorNickmask, creatorAccount string) (maskAdded string, err error) {
  287. casefoldedMask, err := CanonicalizeMaskWildcard(mask)
  288. if err != nil {
  289. return
  290. }
  291. set.serialCacheUpdateMutex.Lock()
  292. defer set.serialCacheUpdateMutex.Unlock()
  293. set.Lock()
  294. if set.masks == nil {
  295. set.masks = make(map[string]MaskInfo)
  296. }
  297. _, present := set.masks[casefoldedMask]
  298. if !present {
  299. maskAdded = casefoldedMask
  300. set.masks[casefoldedMask] = MaskInfo{
  301. TimeCreated: time.Now().UTC(),
  302. CreatorNickmask: creatorNickmask,
  303. CreatorAccount: creatorAccount,
  304. }
  305. }
  306. set.Unlock()
  307. if !present {
  308. set.setRegexp()
  309. }
  310. return
  311. }
  312. // Remove removes the given mask from this set.
  313. func (set *UserMaskSet) Remove(mask string) (maskRemoved string, err error) {
  314. mask, err = CanonicalizeMaskWildcard(mask)
  315. if err != nil {
  316. return
  317. }
  318. set.serialCacheUpdateMutex.Lock()
  319. defer set.serialCacheUpdateMutex.Unlock()
  320. set.Lock()
  321. _, removed := set.masks[mask]
  322. if removed {
  323. maskRemoved = mask
  324. delete(set.masks, mask)
  325. }
  326. set.Unlock()
  327. if removed {
  328. set.setRegexp()
  329. }
  330. return
  331. }
  332. func (set *UserMaskSet) SetMasks(masks map[string]MaskInfo) {
  333. set.Lock()
  334. set.masks = masks
  335. set.Unlock()
  336. set.setRegexp()
  337. }
  338. func (set *UserMaskSet) Masks() (result map[string]MaskInfo) {
  339. set.RLock()
  340. defer set.RUnlock()
  341. result = make(map[string]MaskInfo, len(set.masks))
  342. for mask, info := range set.masks {
  343. result[mask] = info
  344. }
  345. return
  346. }
  347. // Match matches the given n!u@h.
  348. func (set *UserMaskSet) Match(userhost string) bool {
  349. set.RLock()
  350. regexp := set.regexp
  351. set.RUnlock()
  352. if regexp == nil {
  353. return false
  354. }
  355. return regexp.MatchString(userhost)
  356. }
  357. func (set *UserMaskSet) Length() int {
  358. set.RLock()
  359. defer set.RUnlock()
  360. return len(set.masks)
  361. }
  362. func (set *UserMaskSet) setRegexp() {
  363. set.RLock()
  364. maskExprs := make([]string, len(set.masks))
  365. for mask := range set.masks {
  366. maskExprs = append(maskExprs, mask)
  367. }
  368. set.RUnlock()
  369. re, _ := utils.CompileMasks(maskExprs)
  370. set.Lock()
  371. set.regexp = re
  372. set.Unlock()
  373. }