main.go 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. package main
  2. import (
  3. "bytes"
  4. "database/sql"
  5. "fmt"
  6. "io"
  7. "io/ioutil"
  8. "os"
  9. "path"
  10. "regexp"
  11. "strings"
  12. "text/template"
  13. _ "github.com/go-sql-driver/mysql"
  14. "github.com/pin/tftp"
  15. "github.com/namsral/flag"
  16. )
  17. var (
  18. workdir, sepTeplateFilePath, freepbxDb string
  19. sepTeplateFile []byte
  20. db *sql.DB
  21. )
  22. //PhoneSetting struct
  23. type PhoneSetting struct {
  24. DisplayName, PhonePassword string
  25. PhoneNumber string
  26. }
  27. //Getting phone setting from freepbx database
  28. func getPhoneSetting(filename string) (*PhoneSetting, error) {
  29. var ps PhoneSetting
  30. filename = strings.TrimSuffix(filename, ".cnf.xml")
  31. query := `
  32. SELECT userman_users.displayname, sip.data, userman_users.default_extension
  33. FROM userman_users
  34. LEFT JOIN sip
  35. ON userman_users.default_extension=sip.id AND sip.keyword='secret'
  36. WHERE userman_users.fax=?
  37. ORDER BY userman_users.default_extension
  38. LIMIT 1
  39. `
  40. err := db.QueryRow(query, filename).Scan(&ps.DisplayName, &ps.PhonePassword, &ps.PhoneNumber)
  41. if err != nil {
  42. return &ps, err
  43. }
  44. return &ps, nil
  45. }
  46. //Send file to client
  47. func sendFile(file *bytes.Buffer, rf io.ReaderFrom) error {
  48. n, err := rf.ReadFrom(file)
  49. if err != nil {
  50. fmt.Fprintf(os.Stderr, "%v\n", err)
  51. return err
  52. }
  53. fmt.Printf("%d bytes sent\n", n)
  54. return nil
  55. }
  56. //Reading file in tftp dir. If file not found, returning empty file
  57. func readFile(filename string, rf io.ReaderFrom) error {
  58. var err error
  59. buf := new(bytes.Buffer)
  60. file, err := os.Open(path.Join(workdir, filename))
  61. if err != nil {
  62. fmt.Fprintf(os.Stderr, "%v\n", err)
  63. } else {
  64. buf.ReadFrom(file)
  65. }
  66. defer file.Close()
  67. err = sendFile(buf, rf)
  68. if err != nil {
  69. fmt.Fprintf(os.Stderr, "%v\n", err)
  70. return err
  71. }
  72. return nil
  73. }
  74. //Generating phone settings file use go-template
  75. func genFile(filename string, rf io.ReaderFrom) error {
  76. var (
  77. tpl bytes.Buffer
  78. err error
  79. )
  80. p, err := getPhoneSetting(filename)
  81. if err != nil {
  82. fmt.Fprintf(os.Stderr, "%v\n", err)
  83. return err
  84. }
  85. t := template.Must(template.New("sepTeplateFile").Parse(string(sepTeplateFile)))
  86. err = t.Execute(&tpl, p)
  87. if err != nil {
  88. fmt.Fprintf(os.Stderr, "%v\n", err)
  89. return err
  90. }
  91. err = sendFile(&tpl, rf)
  92. if err != nil {
  93. fmt.Fprintf(os.Stderr, "%v\n", err)
  94. return err
  95. }
  96. return nil
  97. }
  98. //Processing file request from a tftp client
  99. func readHandler(filename string, rf io.ReaderFrom) error {
  100. raddr := rf.(tftp.OutgoingTransfer).RemoteAddr()
  101. laddr := rf.(tftp.RequestPacketInfo).LocalIP()
  102. fmt.Println("RRQ from:", raddr.String(), "To:", laddr.String(), "File:", filename)
  103. sepFile, err := path.Match("SEP*.cnf.xml", filename)
  104. if err != nil {
  105. fmt.Fprintf(os.Stderr, "%v\n", err)
  106. return err
  107. }
  108. if sepFile {
  109. genFile(filename, rf)
  110. } else {
  111. readFile(filename, rf)
  112. }
  113. return nil
  114. }
  115. //Declaring cli flags
  116. func init() {
  117. flag.StringVar(&workdir, "workdir", "/tftpboot", "Set working directory")
  118. flag.StringVar(&sepTeplateFilePath, "sep_template_file", "./sep-cisco.cnf.xml.tpl", "Set path to sep template file")
  119. flag.StringVar(&freepbxDb, "freepbx_db", "asterisk:password@tcp(127.0.0.1)/asterisk", "Set freepbx db connection string")
  120. }
  121. func main() {
  122. var err error
  123. flag.Parse()
  124. sepTeplateFile, err = ioutil.ReadFile(sepTeplateFilePath)
  125. if err != nil {
  126. fmt.Fprintf(os.Stderr, "%v\n", err)
  127. os.Exit(1)
  128. }
  129. //Connicting to db
  130. db, err = sql.Open("mysql", freepbxDb)
  131. if err != nil {
  132. fmt.Fprintf(os.Stderr, "Error on initializing database connection: %s\n", err)
  133. os.Exit(1)
  134. }
  135. //Checking db connection
  136. err = db.Ping()
  137. if err != nil {
  138. fmt.Fprintf(os.Stderr, "Error on database connection: %s\n", err)
  139. os.Exit(1)
  140. }
  141. db.SetMaxIdleConns(10)
  142. fmt.Println("Starting freepbx tftp server")
  143. s := tftp.NewServer(readHandler, nil)
  144. err = s.ListenAndServe(":69")
  145. if err != nil {
  146. fmt.Fprintf(os.Stderr, "server: %v\n", err)
  147. os.Exit(1)
  148. }
  149. }