hosts.go 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772
  1. package logic
  2. import (
  3. "crypto/md5"
  4. "encoding/json"
  5. "errors"
  6. "fmt"
  7. "os"
  8. "sort"
  9. "strings"
  10. "sync"
  11. "github.com/google/uuid"
  12. "golang.org/x/crypto/bcrypt"
  13. "golang.org/x/exp/slog"
  14. "github.com/gravitl/netmaker/database"
  15. "github.com/gravitl/netmaker/logger"
  16. "github.com/gravitl/netmaker/models"
  17. "github.com/gravitl/netmaker/servercfg"
  18. "github.com/gravitl/netmaker/utils"
  19. )
  20. var (
  21. hostCacheMutex = &sync.RWMutex{}
  22. hostsCacheMap = make(map[string]models.Host)
  23. hostPortMutex = &sync.Mutex{}
  24. )
  25. var (
  26. // ErrHostExists error indicating that host exists when trying to create new host
  27. ErrHostExists error = errors.New("host already exists")
  28. // ErrInvalidHostID
  29. ErrInvalidHostID error = errors.New("invalid host id")
  30. )
  31. var GetHostLocInfo = func(ip, token string) string { return "" }
  32. func getHostsFromCache() (hosts []models.Host) {
  33. hostCacheMutex.RLock()
  34. for _, host := range hostsCacheMap {
  35. hosts = append(hosts, host)
  36. }
  37. hostCacheMutex.RUnlock()
  38. return
  39. }
  40. func getHostsMapFromCache() (hostsMap map[string]models.Host) {
  41. hostCacheMutex.RLock()
  42. hostsMap = hostsCacheMap
  43. hostCacheMutex.RUnlock()
  44. return
  45. }
  46. func getHostFromCache(hostID string) (host models.Host, ok bool) {
  47. hostCacheMutex.RLock()
  48. host, ok = hostsCacheMap[hostID]
  49. hostCacheMutex.RUnlock()
  50. return
  51. }
  52. func storeHostInCache(h models.Host) {
  53. hostCacheMutex.Lock()
  54. hostsCacheMap[h.ID.String()] = h
  55. hostCacheMutex.Unlock()
  56. }
  57. func deleteHostFromCache(hostID string) {
  58. hostCacheMutex.Lock()
  59. delete(hostsCacheMap, hostID)
  60. hostCacheMutex.Unlock()
  61. }
  62. func loadHostsIntoCache(hMap map[string]models.Host) {
  63. hostCacheMutex.Lock()
  64. hostsCacheMap = hMap
  65. hostCacheMutex.Unlock()
  66. }
  67. const (
  68. maxPort = 1<<16 - 1
  69. minPort = 1025
  70. )
  71. // GetAllHosts - returns all hosts in flat list or error
  72. func GetAllHosts() ([]models.Host, error) {
  73. var currHosts []models.Host
  74. if servercfg.CacheEnabled() {
  75. currHosts := getHostsFromCache()
  76. if len(currHosts) != 0 {
  77. return currHosts, nil
  78. }
  79. }
  80. records, err := database.FetchRecords(database.HOSTS_TABLE_NAME)
  81. if err != nil && !database.IsEmptyRecord(err) {
  82. return nil, err
  83. }
  84. currHostsMap := make(map[string]models.Host)
  85. if servercfg.CacheEnabled() {
  86. defer loadHostsIntoCache(currHostsMap)
  87. }
  88. for k := range records {
  89. var h models.Host
  90. err = json.Unmarshal([]byte(records[k]), &h)
  91. if err != nil {
  92. return nil, err
  93. }
  94. currHosts = append(currHosts, h)
  95. currHostsMap[h.ID.String()] = h
  96. }
  97. return currHosts, nil
  98. }
  99. // GetAllHostsWithStatus - returns all hosts with at least one
  100. // node with given status.
  101. func GetAllHostsWithStatus(status models.NodeStatus) ([]models.Host, error) {
  102. hosts, err := GetAllHosts()
  103. if err != nil {
  104. return nil, err
  105. }
  106. var validHosts []models.Host
  107. for _, host := range hosts {
  108. if len(host.Nodes) == 0 {
  109. continue
  110. }
  111. nodes := GetHostNodes(&host)
  112. for _, node := range nodes {
  113. getNodeCheckInStatus(&node, false)
  114. if node.Status == status {
  115. validHosts = append(validHosts, host)
  116. break
  117. }
  118. }
  119. }
  120. return validHosts, nil
  121. }
  122. // GetAllHostsAPI - get's all the hosts in an API usable format
  123. func GetAllHostsAPI(hosts []models.Host) []models.ApiHost {
  124. apiHosts := []models.ApiHost{}
  125. for i := range hosts {
  126. newApiHost := hosts[i].ConvertNMHostToAPI()
  127. apiHosts = append(apiHosts, *newApiHost)
  128. }
  129. return apiHosts[:]
  130. }
  131. // GetHostsMap - gets all the current hosts on machine in a map
  132. func GetHostsMap() (map[string]models.Host, error) {
  133. if servercfg.CacheEnabled() {
  134. hostsMap := getHostsMapFromCache()
  135. if len(hostsMap) != 0 {
  136. return hostsMap, nil
  137. }
  138. }
  139. records, err := database.FetchRecords(database.HOSTS_TABLE_NAME)
  140. if err != nil && !database.IsEmptyRecord(err) {
  141. return nil, err
  142. }
  143. currHostMap := make(map[string]models.Host)
  144. if servercfg.CacheEnabled() {
  145. defer loadHostsIntoCache(currHostMap)
  146. }
  147. for k := range records {
  148. var h models.Host
  149. err = json.Unmarshal([]byte(records[k]), &h)
  150. if err != nil {
  151. return nil, err
  152. }
  153. currHostMap[h.ID.String()] = h
  154. }
  155. return currHostMap, nil
  156. }
  157. func DoesHostExistinTheNetworkAlready(h *models.Host, network models.NetworkID) bool {
  158. if len(h.Nodes) > 0 {
  159. for _, nodeID := range h.Nodes {
  160. node, err := GetNodeByID(nodeID)
  161. if err == nil && node.Network == network.String() {
  162. return true
  163. }
  164. }
  165. }
  166. return false
  167. }
  168. // GetHost - gets a host from db given id
  169. func GetHost(hostid string) (*models.Host, error) {
  170. if servercfg.CacheEnabled() {
  171. if host, ok := getHostFromCache(hostid); ok {
  172. return &host, nil
  173. }
  174. }
  175. record, err := database.FetchRecord(database.HOSTS_TABLE_NAME, hostid)
  176. if err != nil {
  177. return nil, err
  178. }
  179. var h models.Host
  180. if err = json.Unmarshal([]byte(record), &h); err != nil {
  181. return nil, err
  182. }
  183. if servercfg.CacheEnabled() {
  184. storeHostInCache(h)
  185. }
  186. return &h, nil
  187. }
  188. // GetHostByPubKey - gets a host from db given pubkey
  189. func GetHostByPubKey(hostPubKey string) (*models.Host, error) {
  190. hosts, err := GetAllHosts()
  191. if err != nil {
  192. return nil, err
  193. }
  194. for _, host := range hosts {
  195. if host.PublicKey.String() == hostPubKey {
  196. return &host, nil
  197. }
  198. }
  199. return nil, errors.New("host not found")
  200. }
  201. // CreateHost - creates a host if not exist
  202. func CreateHost(h *models.Host) error {
  203. hosts, hErr := GetAllHosts()
  204. clients, cErr := GetAllExtClients()
  205. if (hErr != nil && !database.IsEmptyRecord(hErr)) ||
  206. (cErr != nil && !database.IsEmptyRecord(cErr)) ||
  207. len(hosts)+len(clients) >= MachinesLimit {
  208. return errors.New("free tier limits exceeded on machines")
  209. }
  210. _, err := GetHost(h.ID.String())
  211. if (err != nil && !database.IsEmptyRecord(err)) || (err == nil) {
  212. return ErrHostExists
  213. }
  214. // encrypt that password so we never see it
  215. hash, err := bcrypt.GenerateFromPassword([]byte(h.HostPass), 5)
  216. if err != nil {
  217. return err
  218. }
  219. h.HostPass = string(hash)
  220. h.AutoUpdate = AutoUpdateEnabled()
  221. if GetServerSettings().ManageDNS {
  222. h.DNS = "yes"
  223. } else {
  224. h.DNS = "no"
  225. }
  226. if h.EndpointIP != nil {
  227. h.Location = GetHostLocInfo(h.EndpointIP.String(), os.Getenv("IP_INFO_TOKEN"))
  228. } else if h.EndpointIPv6 != nil {
  229. h.Location = GetHostLocInfo(h.EndpointIPv6.String(), os.Getenv("IP_INFO_TOKEN"))
  230. }
  231. checkForZombieHosts(h)
  232. return UpsertHost(h)
  233. }
  234. // UpdateHost - updates host data by field
  235. func UpdateHost(newHost, currentHost *models.Host) {
  236. // unchangeable fields via API here
  237. newHost.DaemonInstalled = currentHost.DaemonInstalled
  238. newHost.OS = currentHost.OS
  239. newHost.IPForwarding = currentHost.IPForwarding
  240. newHost.HostPass = currentHost.HostPass
  241. newHost.MacAddress = currentHost.MacAddress
  242. newHost.Debug = currentHost.Debug
  243. newHost.Nodes = currentHost.Nodes
  244. newHost.PublicKey = currentHost.PublicKey
  245. newHost.TrafficKeyPublic = currentHost.TrafficKeyPublic
  246. // changeable fields
  247. if len(newHost.Version) == 0 {
  248. newHost.Version = currentHost.Version
  249. }
  250. if len(newHost.Name) == 0 {
  251. newHost.Name = currentHost.Name
  252. }
  253. if newHost.MTU == 0 {
  254. newHost.MTU = currentHost.MTU
  255. }
  256. if newHost.ListenPort == 0 {
  257. newHost.ListenPort = currentHost.ListenPort
  258. }
  259. if newHost.PersistentKeepalive == 0 {
  260. newHost.PersistentKeepalive = currentHost.PersistentKeepalive
  261. }
  262. if strings.TrimSpace(newHost.DNS) == "" {
  263. newHost.DNS = currentHost.DNS
  264. }
  265. }
  266. // UpdateHostFromClient - used for updating host on server with update recieved from client
  267. func UpdateHostFromClient(newHost, currHost *models.Host) (sendPeerUpdate bool) {
  268. if newHost.PublicKey != currHost.PublicKey {
  269. currHost.PublicKey = newHost.PublicKey
  270. sendPeerUpdate = true
  271. }
  272. if newHost.ListenPort != 0 && currHost.ListenPort != newHost.ListenPort {
  273. currHost.ListenPort = newHost.ListenPort
  274. sendPeerUpdate = true
  275. }
  276. if newHost.WgPublicListenPort != 0 &&
  277. currHost.WgPublicListenPort != newHost.WgPublicListenPort {
  278. currHost.WgPublicListenPort = newHost.WgPublicListenPort
  279. sendPeerUpdate = true
  280. }
  281. isEndpointChanged := false
  282. if !currHost.EndpointIP.Equal(newHost.EndpointIP) {
  283. currHost.EndpointIP = newHost.EndpointIP
  284. sendPeerUpdate = true
  285. isEndpointChanged = true
  286. }
  287. if !currHost.EndpointIPv6.Equal(newHost.EndpointIPv6) {
  288. currHost.EndpointIPv6 = newHost.EndpointIPv6
  289. sendPeerUpdate = true
  290. isEndpointChanged = true
  291. }
  292. for i := range newHost.Interfaces {
  293. newHost.Interfaces[i].AddressString = newHost.Interfaces[i].Address.String()
  294. }
  295. utils.SortIfacesByName(currHost.Interfaces)
  296. utils.SortIfacesByName(newHost.Interfaces)
  297. if !utils.CompareIfaces(currHost.Interfaces, newHost.Interfaces) {
  298. currHost.Interfaces = newHost.Interfaces
  299. sendPeerUpdate = true
  300. }
  301. if isEndpointChanged {
  302. for _, nodeID := range currHost.Nodes {
  303. node, err := GetNodeByID(nodeID)
  304. if err != nil {
  305. slog.Error("failed to get node:", "id", node.ID, "error", err)
  306. continue
  307. }
  308. if node.FailedOverBy != uuid.Nil {
  309. ResetFailedOverPeer(&node)
  310. }
  311. if len(node.AutoRelayedPeers) > 0 {
  312. ResetAutoRelayedPeer(&node)
  313. }
  314. }
  315. }
  316. currHost.DaemonInstalled = newHost.DaemonInstalled
  317. currHost.Debug = newHost.Debug
  318. currHost.Verbosity = newHost.Verbosity
  319. currHost.Version = newHost.Version
  320. currHost.IsStaticPort = newHost.IsStaticPort
  321. currHost.IsStatic = newHost.IsStatic
  322. currHost.MTU = newHost.MTU
  323. currHost.Name = newHost.Name
  324. if len(newHost.NatType) > 0 && newHost.NatType != currHost.NatType {
  325. currHost.NatType = newHost.NatType
  326. sendPeerUpdate = true
  327. }
  328. return
  329. }
  330. // UpsertHost - upserts into DB a given host model, does not check for existence*
  331. func UpsertHost(h *models.Host) error {
  332. data, err := json.Marshal(h)
  333. if err != nil {
  334. return err
  335. }
  336. err = database.Insert(h.ID.String(), string(data), database.HOSTS_TABLE_NAME)
  337. if err != nil {
  338. return err
  339. }
  340. if servercfg.CacheEnabled() {
  341. storeHostInCache(*h)
  342. }
  343. return nil
  344. }
  345. // UpdateHostNode - handles updates from client nodes
  346. func UpdateHostNode(h *models.Host, newNode *models.Node) (publishDeletedNodeUpdate, publishPeerUpdate bool) {
  347. currentNode, err := GetNodeByID(newNode.ID.String())
  348. if err != nil {
  349. return
  350. }
  351. ifaceDelta := IfaceDelta(&currentNode, newNode)
  352. newNode.SetLastCheckIn()
  353. if err := UpdateNode(&currentNode, newNode); err != nil {
  354. slog.Error("error saving node", "name", h.Name, "network", newNode.Network, "error", err)
  355. return
  356. }
  357. if ifaceDelta { // reduce number of unneeded updates, by only sending on iface changes
  358. if !newNode.Connected {
  359. publishDeletedNodeUpdate = true
  360. }
  361. publishPeerUpdate = true
  362. // reset failover data for this node
  363. ResetFailedOverPeer(newNode)
  364. ResetAutoRelayedPeer(newNode)
  365. }
  366. return
  367. }
  368. // RemoveHost - removes a given host from server
  369. func RemoveHost(h *models.Host, forceDelete bool) error {
  370. if !forceDelete && len(h.Nodes) > 0 {
  371. return fmt.Errorf("host still has associated nodes")
  372. }
  373. if len(h.Nodes) > 0 {
  374. if err := DisassociateAllNodesFromHost(h.ID.String()); err != nil {
  375. return err
  376. }
  377. }
  378. err := database.DeleteRecord(database.HOSTS_TABLE_NAME, h.ID.String())
  379. if err != nil {
  380. return err
  381. }
  382. if servercfg.CacheEnabled() {
  383. deleteHostFromCache(h.ID.String())
  384. }
  385. go func() {
  386. if servercfg.IsDNSMode() {
  387. SetDNS()
  388. }
  389. }()
  390. return nil
  391. }
  392. // RemoveHostByID - removes a given host by id from server
  393. func RemoveHostByID(hostID string) error {
  394. err := database.DeleteRecord(database.HOSTS_TABLE_NAME, hostID)
  395. if err != nil {
  396. return err
  397. }
  398. if servercfg.CacheEnabled() {
  399. deleteHostFromCache(hostID)
  400. }
  401. return nil
  402. }
  403. // UpdateHostNetwork - adds/deletes host from a network
  404. func UpdateHostNetwork(h *models.Host, network string, add bool) (*models.Node, error) {
  405. for _, nodeID := range h.Nodes {
  406. node, err := GetNodeByID(nodeID)
  407. if err != nil || node.PendingDelete {
  408. continue
  409. }
  410. if node.Network == network {
  411. if !add {
  412. return &node, nil
  413. } else {
  414. return &node, errors.New("host already part of network " + network)
  415. }
  416. }
  417. }
  418. if !add {
  419. return nil, errors.New("host not part of the network " + network)
  420. } else {
  421. newNode := models.Node{}
  422. newNode.Server = servercfg.GetServer()
  423. newNode.Network = network
  424. newNode.HostID = h.ID
  425. if err := AssociateNodeToHost(&newNode, h); err != nil {
  426. return nil, err
  427. }
  428. return &newNode, nil
  429. }
  430. }
  431. // AssociateNodeToHost - associates and creates a node with a given host
  432. // should be the only way nodes get created as of 0.18
  433. func AssociateNodeToHost(n *models.Node, h *models.Host) error {
  434. if len(h.ID.String()) == 0 || h.ID == uuid.Nil {
  435. return ErrInvalidHostID
  436. }
  437. n.HostID = h.ID
  438. err := createNode(n)
  439. if err != nil {
  440. return err
  441. }
  442. currentHost, err := GetHost(h.ID.String())
  443. if err != nil {
  444. return err
  445. }
  446. h.HostPass = currentHost.HostPass
  447. h.Nodes = append(currentHost.Nodes, n.ID.String())
  448. return UpsertHost(h)
  449. }
  450. // DissasociateNodeFromHost - deletes a node and removes from host nodes
  451. // should be the only way nodes are deleted as of 0.18
  452. func DissasociateNodeFromHost(n *models.Node, h *models.Host) error {
  453. if len(h.ID.String()) == 0 || h.ID == uuid.Nil {
  454. return ErrInvalidHostID
  455. }
  456. if n.HostID != h.ID { // check if node actually belongs to host
  457. return fmt.Errorf("node is not associated with host")
  458. }
  459. if len(h.Nodes) == 0 {
  460. return fmt.Errorf("no nodes present in given host")
  461. }
  462. nList := []string{}
  463. for i := range h.Nodes {
  464. if h.Nodes[i] != n.ID.String() {
  465. nList = append(nList, h.Nodes[i])
  466. }
  467. }
  468. h.Nodes = nList
  469. go func() {
  470. if servercfg.IsPro {
  471. if clients, err := GetNetworkExtClients(n.Network); err != nil {
  472. for i := range clients {
  473. AllowClientNodeAccess(&clients[i], n.ID.String())
  474. }
  475. }
  476. }
  477. }()
  478. if err := DeleteNodeByID(n); err != nil {
  479. return err
  480. }
  481. return UpsertHost(h)
  482. }
  483. // DisassociateAllNodesFromHost - deletes all nodes of the host
  484. func DisassociateAllNodesFromHost(hostID string) error {
  485. host, err := GetHost(hostID)
  486. if err != nil {
  487. return err
  488. }
  489. for _, nodeID := range host.Nodes {
  490. node, err := GetNodeByID(nodeID)
  491. if err != nil {
  492. logger.Log(0, "failed to get host node, node id:", nodeID, err.Error())
  493. continue
  494. }
  495. if err := DeleteNode(&node, true); err != nil {
  496. logger.Log(0, "failed to delete node", node.ID.String(), err.Error())
  497. continue
  498. }
  499. logger.Log(3, "deleted node", node.ID.String(), "of host", host.ID.String())
  500. }
  501. host.Nodes = []string{}
  502. return UpsertHost(host)
  503. }
  504. // GetDefaultHosts - retrieve all hosts marked as default from DB
  505. func GetDefaultHosts() []models.Host {
  506. defaultHostList := []models.Host{}
  507. hosts, err := GetAllHosts()
  508. if err != nil {
  509. return defaultHostList
  510. }
  511. for i := range hosts {
  512. if hosts[i].IsDefault {
  513. defaultHostList = append(defaultHostList, hosts[i])
  514. }
  515. }
  516. return defaultHostList[:]
  517. }
  518. // GetHostNetworks - fetches all the networks
  519. func GetHostNetworks(hostID string) []string {
  520. currHost, err := GetHost(hostID)
  521. if err != nil {
  522. return nil
  523. }
  524. nets := []string{}
  525. for i := range currHost.Nodes {
  526. n, err := GetNodeByID(currHost.Nodes[i])
  527. if err != nil {
  528. return nil
  529. }
  530. nets = append(nets, n.Network)
  531. }
  532. return nets
  533. }
  534. // GetRelatedHosts - fetches related hosts of a given host
  535. func GetRelatedHosts(hostID string) []models.Host {
  536. relatedHosts := []models.Host{}
  537. networks := GetHostNetworks(hostID)
  538. networkMap := make(map[string]struct{})
  539. for _, network := range networks {
  540. networkMap[network] = struct{}{}
  541. }
  542. hosts, err := GetAllHosts()
  543. if err == nil {
  544. for _, host := range hosts {
  545. if host.ID.String() == hostID {
  546. continue
  547. }
  548. networks := GetHostNetworks(host.ID.String())
  549. for _, network := range networks {
  550. if _, ok := networkMap[network]; ok {
  551. relatedHosts = append(relatedHosts, host)
  552. break
  553. }
  554. }
  555. }
  556. }
  557. return relatedHosts
  558. }
  559. // CheckHostPort checks host endpoints to ensures that hosts on the same server
  560. // with the same endpoint have different listen ports
  561. // in the case of 64535 hosts or more with same endpoint, ports will not be changed
  562. func CheckHostPorts(h *models.Host) (changed bool) {
  563. if h.IsStaticPort {
  564. return false
  565. }
  566. if h.EndpointIP == nil {
  567. return
  568. }
  569. // Get the current host from database to check if it already has a valid port assigned
  570. // This check happens before the mutex to avoid unnecessary locking
  571. currentHost, err := GetHost(h.ID.String())
  572. if err == nil && currentHost.ListenPort > 0 {
  573. // If the host already has a port in the database, use that instead of the incoming port
  574. // This prevents the host from being reassigned when the client sends the old port
  575. if currentHost.ListenPort != h.ListenPort {
  576. h.ListenPort = currentHost.ListenPort
  577. }
  578. }
  579. // Only acquire mutex when we need to check for port conflicts
  580. // This reduces contention for the common case where ports are already valid
  581. hostPortMutex.Lock()
  582. defer hostPortMutex.Unlock()
  583. originalPort := h.ListenPort
  584. defer func() {
  585. if originalPort != h.ListenPort {
  586. changed = true
  587. }
  588. }()
  589. hosts, err := GetAllHosts()
  590. if err != nil {
  591. return
  592. }
  593. // Build map of ports in use by other hosts with the same endpoint
  594. portsInUse := make(map[int]bool)
  595. for _, host := range hosts {
  596. if host.ID.String() == h.ID.String() {
  597. // skip self
  598. continue
  599. }
  600. if host.EndpointIP == nil {
  601. continue
  602. }
  603. if !host.EndpointIP.Equal(h.EndpointIP) {
  604. continue
  605. }
  606. if host.ListenPort > 0 {
  607. portsInUse[host.ListenPort] = true
  608. }
  609. }
  610. // If current port is not in use, no change needed
  611. if !portsInUse[h.ListenPort] {
  612. return
  613. }
  614. // Find an available port
  615. maxIterations := maxPort - minPort + 1
  616. checkedPorts := make(map[int]bool)
  617. initialPort := h.ListenPort
  618. for i := 0; i < maxIterations; i++ {
  619. // Special case: skip port 443 by jumping to 51821
  620. if h.ListenPort == 443 {
  621. h.ListenPort = 51821
  622. } else {
  623. h.ListenPort++
  624. }
  625. // Wrap around if we exceed maxPort
  626. if h.ListenPort > maxPort {
  627. h.ListenPort = minPort
  628. }
  629. // Avoid infinite loop - if we've checked this port before, we've cycled through all
  630. if checkedPorts[h.ListenPort] {
  631. // All ports are in use, keep original port
  632. h.ListenPort = originalPort
  633. break
  634. }
  635. checkedPorts[h.ListenPort] = true
  636. // Re-read hosts to get the latest state (in case another host just changed its port)
  637. // This is important to avoid conflicts when multiple hosts are being processed
  638. latestHosts, err := GetAllHosts()
  639. if err == nil {
  640. // Update portsInUse with latest state
  641. for _, host := range latestHosts {
  642. if host.ID.String() == h.ID.String() {
  643. continue
  644. }
  645. if host.EndpointIP == nil {
  646. continue
  647. }
  648. if !host.EndpointIP.Equal(h.EndpointIP) {
  649. continue
  650. }
  651. if host.ListenPort > 0 {
  652. portsInUse[host.ListenPort] = true
  653. }
  654. }
  655. }
  656. // If this port is not in use, we found an available port
  657. if !portsInUse[h.ListenPort] {
  658. break
  659. }
  660. // If we've wrapped back to the initial port, all ports are in use
  661. if h.ListenPort == initialPort && i > 0 {
  662. h.ListenPort = originalPort
  663. break
  664. }
  665. }
  666. return
  667. }
  668. // HostExists - checks if given host already exists
  669. func HostExists(h *models.Host) bool {
  670. _, err := GetHost(h.ID.String())
  671. return (err != nil && !database.IsEmptyRecord(err)) || (err == nil)
  672. }
  673. // GetHostByNodeID - returns a host if found to have a node's ID, else nil
  674. func GetHostByNodeID(id string) *models.Host {
  675. hosts, err := GetAllHosts()
  676. if err != nil {
  677. return nil
  678. }
  679. for i := range hosts {
  680. h := hosts[i]
  681. if StringSliceContains(h.Nodes, id) {
  682. return &h
  683. }
  684. }
  685. return nil
  686. }
  687. // ConvHostPassToHash - converts password to md5 hash
  688. func ConvHostPassToHash(hostPass string) string {
  689. return fmt.Sprintf("%x", md5.Sum([]byte(hostPass)))
  690. }
  691. // SortApiHosts - Sorts slice of ApiHosts by their ID alphabetically with numbers first
  692. func SortApiHosts(unsortedHosts []models.ApiHost) {
  693. sort.Slice(unsortedHosts, func(i, j int) bool {
  694. return unsortedHosts[i].ID < unsortedHosts[j].ID
  695. })
  696. }