package syscon import ( "bufio" "bytes" "config" "datax" "errors" "fmt" "net" "os" "path/filepath" "regexp" "strconv" "strings" "syslog" "time" ) func StartNewService(conf *config.Config) { if err := datax.SQLInit(conf); err != nil { panic(syslog.BigError{Why: err, Cod: 1}) } defer datax.DBClose() lisfunc, isthere := networkListenType[conf.GetConf("listener_type", 0)] if !isthere { panic(syslog.BigError{Why: errors.New("invalid listener_type in config file, should be either inet or unix"), Cod: 1}) } l := lisfunc(conf) defer l.Close() syslog.InformGreen("connection to sql server established") syn := regexp.MustCompile(`(^sasl_username=((.*[^\s].*)|))$`) for { conn, err := l.Accept() if err != nil { syslog.InformError(err) continue } go handlePostfixConn(conn, syn) } } func handlePostfixConn(conn net.Conn, syn *regexp.Regexp) { defer conn.Close() scanner := bufio.NewScanner(conn) for scanner.Scan() { if find := syn.MatchString(scanner.Text()); find { pureName := strings.ReplaceAll(scanner.Text(), "sasl_username=", "") if pureName == "" { if _, err := conn.Write([]byte("action=OK\n\n")); err != nil { syslog.InformError(err) return } continue } userAcc, err := datax.GetUserFromDatabase(&pureName) if err != nil { syslog.InformError(err) _, err := conn.Write([]byte("action=REJECT fixrate error: internal server error <1>, contact administrator\n\n")) if err != nil { syslog.InformError(err) return } continue } if userAcc.LastReset.Add(time.Duration(userAcc.Reset) * time.Second).Before(time.Now()) { if err := userAcc.UpdateUserLastReset(time.Now()); err != nil { syslog.InformError(err) _, err := conn.Write([]byte("action=REJECT fixrate error: internal server error <2>, contact administrator\n\n")) if err != nil { syslog.InformError(err) return } continue } if err := userAcc.UpdateUserCounter(0); err != nil { syslog.InformError(err) _, err := conn.Write([]byte("action=REJECT fixrate error: internal server error <3>, contact administrator\n\n")) if err != nil { syslog.InformError(err) return } continue } userAcc.Counter = 0 } if userAcc.Limit <= userAcc.Counter { diff := userAcc.LastReset.Add(time.Duration(userAcc.Reset) * time.Second).Sub(time.Now()) _, err := conn.Write([]byte(fmt.Sprintf("action=REJECT fixrate: sending limit exceeded, you can't send anything until next %v\n\n", diff.Round(time.Second)))) if err != nil { syslog.InformError(err) return } continue } if err := userAcc.UpdateUserCounter(userAcc.Counter + 1); err != nil { syslog.InformError(err) _, err := conn.Write([]byte("action=REJECT fixrate error: internal server error <4>, contact administrator\n\n")) if err != nil { syslog.InformError(err) return } continue } if _, err := conn.Write([]byte("action=OK\n\n")); err != nil { syslog.InformError(err) return } } } if err := scanner.Err(); err != nil { panic(syslog.BigError{Why: err, Cod: 1}) } } func checkUinixSocket(socketAddr string) (string, bool) { unix, err := os.Open("/proc/net/unix") if err != nil { panic(syslog.BigError{Why: err, Cod: 1}) } defer unix.Close() u := bufio.NewScanner(unix) for u.Scan() { if bytes.Contains(u.Bytes(), []byte(socketAddr)) { pid := findPid(strings.Fields(u.Text())[6]) return pid, true } } if err := u.Err(); err != nil { panic(syslog.BigError{Why: err, Cod: 1}) } return "", false } var networkListenType = map[string]func(conf *config.Config) net.Listener{ "unix": func(conf *config.Config) net.Listener { socketAddr := conf.GetConf("socket_path", 0) if pid, exists := checkUinixSocket(socketAddr); exists { panic(syslog.BigError{Why: fmt.Errorf("unix socket %v is held by another process (pid: %v), is another fixrate daemon runing?", socketAddr, pid), Cod: 1}) } if err := os.RemoveAll(socketAddr); err != nil { panic(syslog.BigError{Why: err, Cod: 1}) } SockPerm, err := strconv.Atoi(conf.GetConf("socket_perm", 0)) if err != nil { panic(syslog.BigError{Why: errors.New("socket_path should hold an integer"), Cod: 1}) } l, err := net.Listen("unix", socketAddr) if err != nil { panic(syslog.BigError{Why: err, Cod: 1}) } mod := os.FileMode(SockPerm) if err = os.Chmod(socketAddr, mod); err != nil { l.Close() panic(syslog.BigError{Why: err, Cod: 1}) } syslog.InformGreen("start listening on unix socket", socketAddr) return l }, "inet": func(conf *config.Config) net.Listener { listenAddr := conf.GetConf("listen_addr", 0) l, err := net.Listen("tcp", listenAddr) if err != nil { panic(syslog.BigError{Why: err, Cod: 1}) } syslog.InformGreen("start listening on tcp address", listenAddr) return l }, } func findPid(inode string) string { type sysInodes struct { path string link string } var pid string fd, err := filepath.Glob("/proc/[0-9]*/fd/[0-9]*") if err != nil { return pid } inodes := make([]sysInodes, len(fd)) mx := make(chan sysInodes, len(fd)) go func(fd *[]string, outchan chan<- sysInodes) { for _, item := range *fd { link, _ := os.Readlink(item) outchan <- sysInodes{item, link} } }(&fd, mx) for range fd { inodes = append(inodes, <-mx) } re := regexp.MustCompile(inode) for _, item := range inodes { out := re.FindString(item.link) if len(out) != 0 { pid = strings.Split(item.path, "/")[2] } } return pid }