main.go 4.6 KB

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