| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210 |
- package main
- import (
- "bytes"
- "database/sql"
- "fmt"
- "io"
- "io/ioutil"
- "os"
- "path"
- "strings"
- "text/template"
- _ "github.com/go-sql-driver/mysql"
- "github.com/pin/tftp"
- "github.com/namsral/flag"
- )
- var (
- workdir, sepTeplateFilePath, freepbxDb string
- sepTeplateFile []byte
- db *sql.DB
- )
- //PhoneSetting struct
- type PhoneSetting struct {
- DisplayName, PhonePassword string
- PhoneNumber string
- }
- //Getting phone setting from freepbx database
- func getPhoneSetting(filename string) (*PhoneSetting, error) {
- var ps PhoneSetting
- filename = strings.TrimSuffix(filename, ".cnf.xml")
- query := `
- SELECT userman_users.displayname, sip.data, userman_users.default_extension
- FROM userman_users
- LEFT JOIN sip
- ON userman_users.default_extension=sip.id AND sip.keyword='secret'
- WHERE userman_users.fax=?
- ORDER BY userman_users.default_extension
- LIMIT 1
- `
- err := db.QueryRow(query, filename).Scan(&ps.DisplayName, &ps.PhonePassword, &ps.PhoneNumber)
- if err != nil {
- return &ps, err
- }
- return &ps, nil
- }
- //Send file to client
- func sendFile(file *bytes.Buffer, rf io.ReaderFrom) error {
- n, err := rf.ReadFrom(file)
- if err != nil {
- fmt.Fprintf(os.Stderr, "%v\n", err)
- return err
- }
- fmt.Printf("%d bytes sent\n", n)
- return nil
- }
- //Reading file in tftp dir. If file not found, returning empty file
- func readFile(filename string, rf io.ReaderFrom) error {
- var err error
- buf := new(bytes.Buffer)
- file, err := os.Open(path.Join(workdir, filename))
- if err != nil {
- fmt.Fprintf(os.Stderr, "%v\n", err)
- } else {
- buf.ReadFrom(file)
- }
- defer file.Close()
- err = sendFile(buf, rf)
- if err != nil {
- fmt.Fprintf(os.Stderr, "%v\n", err)
- return err
- }
- return nil
- }
- //Generating phone settings file use go-template
- func genFile(filename string, rf io.ReaderFrom) error {
- var (
- tpl bytes.Buffer
- err error
- )
- p, err := getPhoneSetting(filename)
- if err != nil {
- fmt.Fprintf(os.Stderr, "%v\n", err)
- return err
- }
- t := template.Must(template.New("sepTeplateFile").Parse(string(sepTeplateFile)))
- err = t.Execute(&tpl, p)
- if err != nil {
- fmt.Fprintf(os.Stderr, "%v\n", err)
- return err
- }
- err = sendFile(&tpl, rf)
- if err != nil {
- fmt.Fprintf(os.Stderr, "%v\n", err)
- return err
- }
- return nil
- }
- func genEmptyFile(filename string, rf io.ReaderFrom) error {
- var (
- buff bytes.Buffer
- err error
- )
- err = sendFile(&buff, rf)
- if err != nil {
- fmt.Fprintf(os.Stderr, "%v\n", err)
- return err
- }
- return nil
- }
- //Processing file request from a tftp client
- func readHandler(filename string, rf io.ReaderFrom) error {
- raddr := rf.(tftp.OutgoingTransfer).RemoteAddr()
- laddr := rf.(tftp.RequestPacketInfo).LocalIP()
- fmt.Println("RRQ from:", raddr.String(), "To:", laddr.String(), "File:", filename)
- sepFile, err := path.Match("SEP*.cnf.xml", filename)
- if err != nil {
- fmt.Fprintf(os.Stderr, "%v\n", err)
- return err
- }
- ctlFile, err := path.Match("CTLSEP*.tlv", filename)
- if err != nil {
- fmt.Fprintf(os.Stderr, "%v\n", err)
- return err
- }
- itlFile, err := path.Match("ITLSEP*.tlv", filename)
- if err != nil {
- fmt.Fprintf(os.Stderr, "%v\n", err)
- return err
- }
- if sepFile {
- genFile(filename, rf)
- } else if ctlFile {
- genEmptyFile(filename, rf)
- } else if itlFile {
- genEmptyFile(filename, rf)
- } else {
- readFile(filename, rf)
- }
- return nil
- }
- //Declaring cli flags
- func init() {
- flag.StringVar(&workdir, "workdir", "/tftpboot", "Set working directory")
- flag.StringVar(&sepTeplateFilePath, "sep_template_file", "./sep-cisco.cnf.xml.tpl", "Set path to sep template file")
- flag.StringVar(&freepbxDb, "freepbx_db", "asterisk:password@tcp(127.0.0.1)/asterisk", "Set freepbx db connection string")
- }
- func main() {
- var err error
- flag.Parse()
- sepTeplateFile, err = ioutil.ReadFile(sepTeplateFilePath)
- if err != nil {
- fmt.Fprintf(os.Stderr, "%v\n", err)
- os.Exit(1)
- }
- //Connicting to db
- db, err = sql.Open("mysql", freepbxDb)
- if err != nil {
- fmt.Fprintf(os.Stderr, "Error on initializing database connection: %s\n", err)
- os.Exit(1)
- }
- //Checking db connection
- err = db.Ping()
- if err != nil {
- fmt.Fprintf(os.Stderr, "Error on database connection: %s\n", err)
- os.Exit(1)
- }
- db.SetMaxIdleConns(10)
- fmt.Println("Starting freepbx tftp server")
- s := tftp.NewServer(readHandler, nil)
- err = s.ListenAndServe(":69")
- if err != nil {
- fmt.Fprintf(os.Stderr, "server: %v\n", err)
- os.Exit(1)
- }
- }
|