aboutsummaryrefslogblamecommitdiff
path: root/vendor/syscon/syscon.go
blob: 14c0afca46acd2920f09aaf497c362e10ef5ec37 (plain) (tree)






























                                                                                                                                      
                                                                     


















                                                                                            






                                                                                              























































































































































                                                                                                                                                                                              
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
}

Snix LLC Git Repository Holder Copyright(C) 2022 All Rights Reserved Email To Snix.IR