storage.go 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  1. /*
  2. GNU GENERAL PUBLIC LICENSE
  3. Version 3, 29 June 2007
  4. kyoketsu, a Client-To-Client Network Enumeration System
  5. Copyright (C) 2024 Russell Hrubesky, ChiralWorks Software LLC
  6. Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
  7. Everyone is permitted to copy and distribute verbatim copies
  8. of this license document, but changing it is not allowed.
  9. This program is free software: you can redistribute it and/or modify
  10. it under the terms of the GNU General Public License as published by
  11. the Free Software Foundation, either version 3 of the License,
  12. or (at your option) any later version.
  13. This program is distributed in the hope that it will be useful,
  14. but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  16. See the GNU General Public License for more details.
  17. You should have received a copy of the GNU General Public License
  18. along with this program. If not, see <http://www.gnu.org/licenses/>.
  19. */
  20. package kyoketsu
  21. import (
  22. "database/sql"
  23. "errors"
  24. "github.com/mattn/go-sqlite3"
  25. )
  26. type TopologyDatabaseIO interface {
  27. /*
  28. This interface defines the Input and output methods that will be necessary
  29. for an appropriate implementation of the data storage that the distributed system will use.
  30. When I get around to implementing the client-to-client format of this, it could be anything.
  31. */
  32. Migrate() error
  33. Create(host Host) (*Host, error)
  34. All() ([]Host, error)
  35. GetByIP(ip string) (*Host, error)
  36. Update(id int64, updated Host) (*Host, error)
  37. Delete(id int64) error
  38. }
  39. var (
  40. ErrDuplicate = errors.New("record already exists")
  41. ErrNotExists = errors.New("row not exists")
  42. ErrUpdateFailed = errors.New("update failed")
  43. ErrDeleteFailed = errors.New("delete failed")
  44. )
  45. type SQLiteRepo struct {
  46. db *sql.DB
  47. }
  48. // Instantiate a new SQLiteRepo struct
  49. func NewSQLiteRepo(db *sql.DB) *SQLiteRepo {
  50. return &SQLiteRepo{
  51. db: db,
  52. }
  53. }
  54. // Creates a new SQL table with necessary data
  55. func (r *SQLiteRepo) Migrate() error {
  56. query := `
  57. CREATE TABLE IF NOT EXISTS hosts(
  58. id INTEGER PRIMARY KEY AUTOINCREMENT,
  59. fqdn TEXT NOT NULL,
  60. ipv4_address TEXT NOT NULL UNIQUE,
  61. listening_port INTEGER[] NOT NULL
  62. );
  63. `
  64. _, err := r.db.Exec(query)
  65. return err
  66. }
  67. /*
  68. Create an entry in the hosts table
  69. :param host: a Host entry from a port scan
  70. */
  71. func (r *SQLiteRepo) Create(host Host) (*Host, error) {
  72. res, err := r.db.Exec("INSERT INTO hosts(fqdn, ipv4_address, listening_port) values(?,?,?)", host.Fqdn, host.IpAddress, host.ListeningPorts)
  73. if err != nil {
  74. var sqliteErr sqlite3.Error
  75. if errors.As(err, &sqliteErr) {
  76. if errors.Is(sqliteErr.ExtendedCode, sqlite3.ErrConstraintUnique) {
  77. return nil, ErrDuplicate
  78. }
  79. }
  80. return nil, err
  81. }
  82. id, err := res.LastInsertId()
  83. if err != nil {
  84. return nil, err
  85. }
  86. host.Id = id
  87. return &host, nil
  88. }
  89. // Get all Hosts from the host table
  90. func (r *SQLiteRepo) All() ([]Host, error) {
  91. rows, err := r.db.Query("SELECT * FROM hosts")
  92. if err != nil {
  93. return nil, err
  94. }
  95. defer rows.Close()
  96. var all []Host
  97. for rows.Next() {
  98. var host Host
  99. if err := rows.Scan(&host.Id, &host.Fqdn, &host.IpAddress, &host.ListeningPorts); err != nil {
  100. return nil, err
  101. }
  102. all = append(all, host)
  103. }
  104. return all, nil
  105. }
  106. // Get a record by its FQDN
  107. func (r *SQLiteRepo) GetByIP(ip string) (*Host, error) {
  108. row := r.db.QueryRow("SELECT * FROM hosts WHERE ipv4_address = ?", ip)
  109. var host Host
  110. if err := row.Scan(&host.Id, &host.Fqdn, &host.IpAddress, &host.ListeningPorts); err != nil {
  111. if errors.Is(err, sql.ErrNoRows) {
  112. return nil, ErrNotExists
  113. }
  114. return nil, err
  115. }
  116. return &host, nil
  117. }
  118. // Update a record by its ID
  119. func (r *SQLiteRepo) Update(id int64, updated Host) (*Host, error) {
  120. if id == 0 {
  121. return nil, errors.New("invalid updated ID")
  122. }
  123. res, err := r.db.Exec("UPDATE hosts SET fqdn = ?, ipv4_address = ?, listening_port = ? WHERE id = ?", updated.Fqdn, updated.IpAddress, updated.ListeningPorts, id)
  124. if err != nil {
  125. return nil, err
  126. }
  127. rowsAffected, err := res.RowsAffected()
  128. if err != nil {
  129. return nil, err
  130. }
  131. if rowsAffected == 0 {
  132. return nil, ErrUpdateFailed
  133. }
  134. return &updated, nil
  135. }
  136. // Delete a record by its ID
  137. func (r *SQLiteRepo) Delete(id int64) error {
  138. res, err := r.db.Exec("DELETE FROM hosts WHERE id = ?", id)
  139. if err != nil {
  140. return err
  141. }
  142. rowsAffected, err := res.RowsAffected()
  143. if err != nil {
  144. return err
  145. }
  146. if rowsAffected == 0 {
  147. return ErrDeleteFailed
  148. }
  149. return err
  150. }