A More Secure Internet Connection for Your Home https://fen.gg
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.
 
 
 
 
 
 

292 lines
7.6 KiB

  1. package model
  2. //
  3. // Fengg Security Gateway Server Application
  4. // Copyright (C) 2020 Lukas Matt <support@fen.gg>
  5. //
  6. // This program is free software: you can redistribute it and/or modify
  7. // it under the terms of the GNU General Public License as published by
  8. // the Free Software Foundation, either version 3 of the License.
  9. //
  10. // This program is distributed in the hope that it will be useful,
  11. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. // GNU General Public License for more details.
  14. //
  15. // You should have received a copy of the GNU General Public License
  16. // along with this program. If not, see <https://www.gnu.org/licenses/>.
  17. //
  18. import (
  19. "net"
  20. "regexp"
  21. "fmt"
  22. "time"
  23. "github.com/jmoiron/sqlx"
  24. _ "github.com/lib/pq"
  25. )
  26. const (
  27. PatternQueryTmpl = `SELECT * FROM patterns %s;`
  28. PatternNamedUpdateTmpl = `
  29. UPDATE patterns
  30. SET updated_at=now()
  31. %s;`
  32. PatternNamedDeleteTmpl = `DELETE FROM patterns %s;`
  33. PatternNamedInsertTmpl = `
  34. INSERT INTO patterns (
  35. updated_at, raw, ip, url, url_path
  36. ) VALUES (
  37. now(), :raw, :ip, :url, :url_path
  38. ) RETURNING id;`
  39. PatternNamedBulkInsertTmpl = `
  40. with runalways as (select 1),
  41. inserted_pattern as (
  42. insert into patterns (
  43. updated_at, raw, ip, url, url_path
  44. ) select
  45. now(), :raw, :ip, :url, :url_path
  46. where not exists (
  47. select from patterns where raw = :raw
  48. ) returning id
  49. ),
  50. pattern as (
  51. select id from inserted_pattern
  52. union
  53. select id from patterns where raw = :raw
  54. ),
  55. parent as (
  56. select id from pattern_categories where name = :parent_name
  57. ),
  58. category_inserted as (
  59. insert into pattern_categories (
  60. updated_at, name, parent_id, count
  61. )
  62. select now(), :name, parent.id, 1 from runalways full join parent on 1 = 1
  63. where not exists (
  64. select from pattern_categories where name = :name
  65. ) returning id
  66. ),
  67. category_selected as (
  68. select id from pattern_categories where name = :name
  69. ),
  70. category as (
  71. select id from category_inserted
  72. union
  73. select id from category_selected
  74. )
  75. insert into pattern_category_lists (
  76. pattern_id, category_id
  77. )
  78. select pattern.id, category.id from pattern, category
  79. on conflict do nothing;`
  80. PatternCopyCSVQuery = `
  81. copy patterns
  82. from '/usr/share/fengg-pattern/patterns.csv'
  83. delimiter ',' csv header;`
  84. PatternCountTmpl = `select count(*) from patterns %s;`
  85. )
  86. type Pattern struct {
  87. ID uint `db:"id" json:"id"`
  88. UpdatedAt time.Time `db:"updated_at" json:"updatedAt"`
  89. Raw string `db:"raw" json:"raw"`
  90. IP bool `db:"ip" json:"ip"`
  91. URL bool `db:"url" json:"url"`
  92. URLPath bool `db:"url_path" json:"urlPath"`
  93. }
  94. type Patterns []*Pattern
  95. var (
  96. urlRegex = regexp.MustCompile(
  97. `^(?:https?:\/\/)?(?:[^@\/,]+@)?(?:www\.)?([^:\/,]{5,})(\/.*){0,1}$`)
  98. ipRegex = regexp.MustCompile(
  99. `^(?:https?:\/\/)?(?:[^@\/,]+@)?(?:www\.)?([\d\.]{7,})(\/.*){0,1}$`)
  100. )
  101. func NewPattern(address string) *Pattern {
  102. pattern := &Pattern{
  103. UpdatedAt: time.Now(),
  104. }
  105. pattern.Parse(address)
  106. return pattern
  107. }
  108. // PatternImport will bulk import all installed patterns and rules.
  109. // WARNING: This is a very hardware expensive job!
  110. func PatternImport() error {
  111. db, err := sqlx.Connect(dbDriver, dbConnect)
  112. if err != nil {
  113. return err
  114. }
  115. defer db.Close()
  116. tx := db.MustBegin()
  117. tx.MustExec(PatternCopyCSVQuery)
  118. tx.MustExec(PatternCategoryCopyCSVQuery)
  119. tx.MustExec(PatternCategoryListCopyCSVQuery)
  120. return tx.Commit()
  121. }
  122. // PatternURLCount will fetch the database count of the available URL pattern
  123. func PatternURLCount(count *int) error {
  124. db, err := sqlx.Connect(dbDriver, dbConnect)
  125. if err != nil {
  126. return err
  127. }
  128. defer db.Close()
  129. return db.Get(count, fmt.Sprintf(PatternCountTmpl, "WHERE url = true"))
  130. }
  131. // PatternIPCount will fetch the database count of the available IP pattern
  132. func PatternIPCount(count *int) error {
  133. db, err := sqlx.Connect(dbDriver, dbConnect)
  134. if err != nil {
  135. return err
  136. }
  137. defer db.Close()
  138. return db.Get(count, fmt.Sprintf(PatternCountTmpl, "WHERE ip = true"))
  139. }
  140. func (pattern *Pattern) Parse(address string) {
  141. pattern.parseIP(address)
  142. // urlRegex will detect ip addresses too
  143. if !pattern.IP {
  144. pattern.parseDNS(address)
  145. }
  146. }
  147. func (pattern *Pattern) parseIP(address string) {
  148. result := ipRegex.FindAllStringSubmatch(address, 1)
  149. if len(result) == 1 && len(result[0]) == 3 && (result[0][2] == "" || result[0][2] == "/") {
  150. pattern.IP = net.ParseIP(result[0][1]) != nil
  151. pattern.Raw = result[0][1]
  152. }
  153. pattern.URL = false
  154. pattern.URLPath = false
  155. }
  156. func (pattern *Pattern) parseDNS(address string) {
  157. result := urlRegex.FindAllStringSubmatch(address, 1)
  158. if len(result) == 1 && len(result[0]) == 3 {
  159. pattern.URL = result[0][1] != ""
  160. pattern.URLPath = result[0][2] != "" && result[0][2] != "/"
  161. pattern.Raw = result[0][1]
  162. }
  163. pattern.IP = false
  164. }
  165. func (pattern Pattern) Valid() bool {
  166. return ((pattern.IP && !pattern.URL && !pattern.URLPath) || (!pattern.IP && pattern.URL)) && pattern.Raw != ""
  167. }
  168. func (patterns *Patterns) FindByEnabled(enabled bool) error {
  169. db, err := sqlx.Connect(dbDriver, dbConnect)
  170. if err != nil {
  171. logger.Error().Err(err).Msg("cannot connect to database")
  172. return err
  173. }
  174. defer db.Close()
  175. return db.Select(patterns, fmt.Sprintf(PatternQueryTmpl, "WHERE enabled = $1"), enabled)
  176. }
  177. // Delete will delete the pattern and all its relation e.g. category, category lists
  178. // for more details see the postgres trigger function clean_up_after_pattern_delete
  179. func (patterns *Patterns) Delete() error {
  180. db, err := sqlx.Connect(dbDriver, dbConnect)
  181. if err != nil {
  182. return err
  183. }
  184. defer db.Close()
  185. tx := db.MustBegin()
  186. for _, pattern := range *patterns {
  187. query := fmt.Sprintf(PatternNamedDeleteTmpl, "WHERE id=:id OR raw=:raw")
  188. if _, err = tx.NamedExec(query, pattern); err != nil {
  189. return err
  190. }
  191. }
  192. return tx.Commit()
  193. }
  194. func (pattern *Pattern) FindByID(id uint) error {
  195. db, err := sqlx.Connect(dbDriver, dbConnect)
  196. if err != nil {
  197. logger.Error().Err(err).Msg("cannot connect to database")
  198. return err
  199. }
  200. defer db.Close()
  201. return db.Get(pattern, fmt.Sprintf(PatternQueryTmpl, "WHERE id = $1"), id)
  202. }
  203. func (pattern *Pattern) Exists() bool {
  204. db, err := sqlx.Connect(dbDriver, dbConnect)
  205. if err != nil {
  206. logger.Error().Err(err).Msg("cannot connect to database")
  207. return false
  208. }
  209. defer db.Close()
  210. return db.Get(pattern, fmt.Sprintf(
  211. PatternQueryTmpl, "WHERE id = $1 OR raw = $2"), pattern.ID, pattern.Raw) == nil
  212. }
  213. func (pattern *Pattern) Update() error {
  214. db, err := sqlx.Connect(dbDriver, dbConnect)
  215. if err != nil {
  216. return err
  217. }
  218. defer db.Close()
  219. _, err = db.NamedExec(fmt.Sprintf(
  220. PatternNamedUpdateTmpl, "WHERE id=:id OR raw=:raw"), pattern)
  221. return err
  222. }
  223. func (pattern *Pattern) Create() error {
  224. db, err := sqlx.Connect(dbDriver, dbConnect)
  225. if err != nil {
  226. return err
  227. }
  228. defer db.Close()
  229. rows, err := db.NamedQuery(PatternNamedInsertTmpl, pattern)
  230. if err != nil {
  231. return err
  232. }
  233. defer rows.Close()
  234. for rows.Next() {
  235. err = rows.Scan(&pattern.ID)
  236. if err != nil {
  237. return err
  238. }
  239. }
  240. return nil
  241. }
  242. // Delete will delete the pattern and all its relation e.g. category, category lists
  243. // for more details see the postgres trigger function clean_up_after_pattern_delete
  244. func (pattern *Pattern) Delete() error {
  245. db, err := sqlx.Connect(dbDriver, dbConnect)
  246. if err != nil {
  247. return err
  248. }
  249. defer db.Close()
  250. _, err = db.NamedExec(
  251. fmt.Sprintf(PatternNamedDeleteTmpl, "WHERE id=:id OR raw=:raw"), pattern)
  252. return err
  253. }