func ConfReload() { // Do not panic! defer func() { if r := recover(); r != nil { log.Errorf("paniced when reloading config %s, recovered.", config_file) log.Errorf("panic: %s", r) } }() log.Warn("Reloading config") content, err := ioutil.ReadFile(config_file) if err != nil { log.Errorf("unable to reload config %s: %s", config_file, err.Error()) return } prev_listen := config.Listen_addr config_lock.Lock() err = yaml.Unmarshal(content, &config) if err != nil { log.Errorf("error when parsing config file %s: %s", config_file, err.Error()) return } validateConfig() config_lock.Unlock() log.Info("config reloaded.") if config.Listen_addr != prev_listen { log.Warnf("config reload will not reopen server socket, thus no effect on listen address") } log.Infof("%d upstream server(s) found", len(config.Upstream)) }
func RejectHandler(conn *WrapedSocket, initial_pkt *mcproto.MCHandShake, e *mcchat.ChatMsg) { if initial_pkt.NextState == 1 { conn.Infof("ping packet") pkt, err := mcproto.ReadPacket(conn) if err != nil { conn.Errorf("Error when reading status request: %s", err.Error()) conn.Close() return } if !pkt.IsStatusRequest() { conn.Errorf("Invalid protocol: no status request.") conn.Close() return } conn.Debugf("status: request") resp := new(mcproto.MCStatusResponse) resp.Description = e resp.Version.Name = "minegate" resp.Version.Protocol = 0 resp_pkt, err := resp.ToRawPacket() if err != nil { conn.Errorf("Unable to make packet: %s", err.Error()) conn.Close() return } _, err = conn.Write(resp_pkt.ToBytes()) if err != nil { log.Errorf("Unable to write response: %s", err.Error()) conn.Close() return } pkt, err = mcproto.ReadPacket(conn) if err != nil { if err != io.EOF { log.Errorf("Unable to read packet: %s", err.Error()) } conn.Close() return } if !pkt.IsStatusPing() { log.Error("Invalid protocol: no status ping.") conn.Close() return } conn.Write(pkt.ToBytes()) // Don't care now. } else { log.Info("login packet") kick_pkt := (*mcproto.MCKick)(e) raw_pkt, err := kick_pkt.ToRawPacket() if err != nil { log.Errorf("Unable to make packet: %s", err.Error()) conn.Close() return } // Don't care now conn.Write(raw_pkt.ToBytes()) } conn.Close() return }
func Run() { PreLoadConfig() confInit() PostLoadConfig() runtime.GOMAXPROCS(runtime.NumCPU()) log.Infof("MineGate %s started.", version_full) go ServerSocket() sig := make(chan os.Signal, 1) signal.Notify(sig, syscall.SIGHUP, syscall.SIGUSR1) for { cur := <-sig switch cur { case syscall.SIGHUP: log.Warn("SIGHUP caught, reloading config...") PreLoadConfig() ConfReload() PostLoadConfig() case syscall.SIGUSR1: log.Warn("SIGUSR1 caught, rotating log...") log.Rotate() default: log.Errorf("Trapped unexpected signal: %s", cur.String()) continue } } }
func (upstream *Upstream) Validate() (valid bool) { var host, port string host, port, err := net.SplitHostPort(upstream.Server) if err != nil { server := upstream.Server + ":25565" if host, port, err = net.SplitHostPort(server); err != nil { log.Error("Invalid upstream server: " + upstream.Server) return false } log.Infof("no port information found in %s, assume 25565", upstream.Server) upstream.Server = server } p, err := strconv.ParseUint(port, 10, 16) if err != nil { log.Errorf("Invalid port %s: %s", port, err.Error()) return false } host = strings.ToLower(host) if !CheckHost(host) { log.Error("Invalid upstream host: " + host) return false } upstream.Server = net.JoinHostPort(host, fmt.Sprintf("%d", p)) upstream.Pattern = strings.ToLower(upstream.Pattern) if !CheckPattern(upstream.Pattern) { log.Error("Invalid pattern: " + upstream.Pattern) return false } if upstream.ErrorMsg.Text == "" { log.Warnf("Empty error text for %s, use default string", upstream.Server) upstream.ErrorMsg.Text = "Connection failed to " + upstream.Server } upstream.ChatMsg = ToChatMsg(&upstream.ErrorMsg) return true }
func (upstream *Upstream) GetExtra(path string) (val interface{}, err error) { defer func() { if r := recover(); r != nil { log.Errorf("Recovered panic: upstream.GetExtra(%s), err=%+v", path, r) log.Debugf("Upstream: %+v", upstream) val = nil err = errors.New("panic when getting config.") return } }() paths := strings.Split(path, ".") cur := reflect.ValueOf(upstream.Extras) // ROOT can't be an array, so assume no config path starts with # for _, path := range paths { index := strings.Split(path, "#") prefix, index := index[0], index[1:] if cur.Kind() != reflect.Map { log.Warnf("upstream.GetExtra(%s): unable to fetch key %s, not a map.", path, prefix) return nil, fmt.Errorf("index key on non-map type") } cur = reflect.ValueOf(cur.MapIndex(reflect.ValueOf(prefix)).Interface()) for _, idx := range index { i, err := strconv.ParseInt(idx, 0, 0) if err != nil { log.Errorf("upstream.GetExtra(%s): unable to parse %s: %s", path, idx, err.Error()) return nil, fmt.Errorf("Unable to parse %s: %s", idx, err.Error()) } if cur.Kind() != reflect.Slice { log.Warnf("upstream.GetExtra(%s): unable to index value, not a slice", path) return nil, errors.New("Unable to index value, not a slice.") } if int(i) >= cur.Len() { log.Warnf("upstream.GetExtra(%s): index %d out of range", path, i) return nil, errors.New("Index out of range.") } cur = reflect.ValueOf(cur.Index(int(i)).Interface()) } } return cur.Interface(), nil }
func OnDisconnect(handle DisconnectFunc, priority int) (err error) { if priority < 0 || priority > 39 { log.Errorf("Invalid priority %d: not in range [0, 39]", priority) return fmt.Errorf("priority check failure: %d not in range [0, 39]", priority) } if handle == nil { log.Error("Attempt to register nil handler") return errors.New("Nil handler!") } if disconnectHandlers[priority] == nil { disconnectHandlers[priority] = make(disconnectHandler, 0, 16) } disconnectHandlers[priority] = append(disconnectHandlers[priority], handle) log.Infof("Registered disconnect handler at priority %d", priority) return nil }
func OnPreStatusResponse(handle PreStatusResponseFunc, priority int) (err error) { if priority < 0 || priority > 39 { log.Errorf("Invalid priority %d: not in range [0, 39]", priority) return fmt.Errorf("priority check failure: %d not in range [0, 39]", priority) } if handle == nil { log.Error("Attempt to register nil handler") return errors.New("Nil handler!") } if preStatusResponseHandlers[priority] == nil { preStatusResponseHandlers[priority] = make(preStatusResponseHandler, 0, 16) } preStatusResponseHandlers[priority] = append(preStatusResponseHandlers[priority], handle) log.Infof("Registered preStatusResponse handler at priority %d", priority) return nil }
func OnStartProxy(handle StartProxyFunc, priority int) (err error) { if priority < 0 || priority > 39 { log.Errorf("Invalid priority %d: not in range [0, 39]", priority) return fmt.Errorf("priority check failure: %d not in range [0, 39]", priority) } if handle == nil { log.Error("Attempt to register nil handler") return errors.New("Nil handler!") } if startProxyHandlers[priority] == nil { startProxyHandlers[priority] = make(startProxyHandler, 0, 16) } startProxyHandlers[priority] = append(startProxyHandlers[priority], handle) log.Infof("Registered startProxy handler at priority %d", priority) return nil }
func OnLoginRequest(handle LoginRequestFunc, priority int) (err error) { if priority < 0 || priority > 39 { log.Errorf("Invalid priority %d: not in range [0, 39]", priority) return fmt.Errorf("priority check failure: %d not in range [0, 39]", priority) } if handle == nil { log.Error("Attempt to register nil handler") return errors.New("Nil handler!") } if loginRequestHandlers[priority] == nil { loginRequestHandlers[priority] = make(loginRequestHandler, 0, 16) } loginRequestHandlers[priority] = append(loginRequestHandlers[priority], handle) log.Infof("Registered pingRequest handler at priority %d", priority) return nil }
func userLogout(event *minegate.DisconnectEvent) { defer func() { if r := recover(); r != nil { log.Errorf("[conntrack] Recovered from panic. Programming error inside plugin. Please contact author ASAP!") } }() connID := event.GetConnID() ollock.Lock() defer ollock.Unlock() info, ok := conn_in[connID] if !ok { return } delete(conn_in, connID) online_list[info.Server].Remove(info.User) event.Infof("[conntrack] User %s logout from server %s.", info.User, info.Server) event.Infof("[conntrack] Upstream online user: %d. Total online user: %d", online_list[info.Server].Cardinality(), len(conn_in)) }
func validateConfig() { invalid_upstreams := make([]int, 0, len(config.Upstream)) for idx, upstream := range config.Upstream { if !upstream.Validate() { log.Errorf("Upstream %s is not activated.", upstream.Server) invalid_upstreams = append(invalid_upstreams, idx) } } for delta, idx := range invalid_upstreams { idx -= delta config.Upstream[idx] = nil config.Upstream = append(config.Upstream[:idx], config.Upstream[idx+1:]...) } if config.NotFound.Text == "" { log.Warn("Empty error text for not found error, use default string") config.NotFound.Text = "No such host." } config.chatNotFound = ToChatMsg(&config.NotFound) }
func confInit() { content, err := ioutil.ReadFile(config_file) if err != nil { log.Fatalf("unable to load config %s: %s", config_file, err.Error()) } err = yaml.Unmarshal(content, &config) if err != nil { log.Fatalf("error when parsing config file %s: %s", config_file, err.Error()) } validateConfig() if config.Log.Target != "" && config.Log.Target != "-" { config.Log.Target, _ = filepath.Abs(config.Log.Target) log.Info("log path: " + config.Log.Target) } log.Stop() if config.Daemonize { Daemonize() } log.Start() if config.Log.Level != "" { level := log.ToLevel(config.Log.Level) if level == log.INVALID { log.Errorf("Invalid log level %s", config.Log.Level) } else { log.SetLogLevel(level) } } if config.Log.Target != "" && config.Log.Target != "-" { err := log.Open(config.Log.Target) if err != nil { log.Fatalf("Unable to open log %s: %s", config.Log.Target, err.Error()) } } log.Info("config loaded.") log.Info("server listen on: " + config.Listen_addr) log.Infof("%d upstream server(s) found", len(config.Upstream)) }
func (event *NetworkEvent) Errorf(format string, v ...interface{}) { if event.log_prefix == "" { event.log_prefix = fmt.Sprintf("[#%d %s]", event.connID, event.RemoteAddr) } log.Errorf(event.log_prefix+format, v...) }
func (ws *WrapedSocket) Errorf(format string, v ...interface{}) { log.Errorf(ws.log_prefix+format, v...) }
func proxy(conn *WrapedSocket, upstream *Upstream, initial_pkt *mcproto.MCHandShake, ne *PostAcceptEvent) { addr, perr := net.ResolveTCPAddr("tcp", upstream.Server) var err error var upsock *net.TCPConn if perr == nil { upsock, err = net.DialTCP("tcp", nil, addr) } if err != nil || perr != nil { if err == nil { err = perr } log.Errorf("Unable to connect to upstream %s", upstream.Server) RejectHandler(conn, initial_pkt, upstream.ChatMsg) return } upconn := WrapUpstreamSocket(upsock, conn) if initial_pkt.NextState == 1 { // Handle ping here. conn.Debugf("ping proxy") pre := new(PingRequestEvent) pre.NetworkEvent = ne.NetworkEvent pre.Packet = initial_pkt pre.Upstream = upstream PingRequest(pre) if pre.Rejected() { if pre.reason == "" { conn.Warnf("Ping request was rejected.") pre.reason = "Request was rejected by plugin." } else { conn.Warnf("Ping request was rejected: %s", pre.reason) } e := mcchat.NewMsg(pre.reason) e.SetColor(mcchat.RED) e.SetBold(true) RejectHandler(conn, initial_pkt, e) return } init_raw, err := initial_pkt.ToRawPacket() if err != nil { log.Errorf("Unable to encode initial packet: %s", err.Error()) conn.Close() upconn.Close() return } pkt, err := mcproto.ReadPacket(conn) if err != nil { conn.Errorf("Error when reading status request: %s", err.Error()) conn.Close() upconn.Close() return } if !pkt.IsStatusRequest() { conn.Errorf("Invalid protocol: no status request.") conn.Close() upconn.Close() return } _, err = upconn.Write(init_raw.ToBytes()) if err == nil { _, err = upconn.Write(pkt.ToBytes()) } if err != nil { upconn.Errorf("write error: %s", err.Error()) conn.Close() upconn.Close() return } resp_pkt, err := mcproto.ReadPacket(upconn) if err != nil { upconn.Errorf("invalid packet: %s", err.Error()) conn.Close() upconn.Close() return } resp, err := resp_pkt.ToStatusResponse() if err != nil { upconn.Errorf("invalid packet: %s", err.Error()) conn.Close() upconn.Close() return } psre := new(PreStatusResponseEvent) psre.NetworkEvent = ne.NetworkEvent psre.Packet = resp psre.Upstream = upstream PreStatusResponse(psre) resp_pkt, err = resp.ToRawPacket() if err != nil { conn.Errorf("invalid packet: %s", err.Error()) conn.Close() upconn.Close() return } // We can handle ping request, close upstream upconn.Close() _, err = conn.Write(resp_pkt.ToBytes()) if err != nil { conn.Errorf("write error: %s", err.Error()) conn.Close() return } ping_pkt, err := mcproto.ReadPacket(conn) if err != nil || !ping_pkt.IsStatusPing() { if err == nil { err = errors.New("packet is not ping") } conn.Errorf("invalid packet: %s", err.Error()) conn.Close() return } _, err = conn.Write(ping_pkt.ToBytes()) conn.Close() } else { // Handle login here. conn.Debugf("login proxy") login_raw, err := mcproto.ReadPacket(conn) if err != nil { conn.Errorf("Read login packet: %s", err.Error()) conn.Close() return } login_pkt, err := login_raw.ToLogin() if err != nil { conn.Errorf("invalid packet: %s", err.Error()) conn.Close() return } lre := new(LoginRequestEvent) lre.NetworkEvent = ne.NetworkEvent lre.InitPacket = initial_pkt lre.LoginPacket = login_pkt lre.Upstream = upstream LoginRequest(lre) if lre.Rejected() { if lre.reason == "" { conn.Warnf("Ping request was rejected.") lre.reason = "Request was rejected by plugin." } else { conn.Warnf("Ping request was rejected: %s", lre.reason) } e := mcchat.NewMsg(lre.reason) e.SetColor(mcchat.RED) e.SetBold(true) RejectHandler(conn, initial_pkt, e) return } init_raw, err := initial_pkt.ToRawPacket() if err != nil { log.Errorf("Unable to encode initial packet: %s", err.Error()) conn.Close() return } login_raw, err = login_pkt.ToRawPacket() if err != nil { log.Errorf("Unable to encode login packet: %s", err.Error()) conn.Close() return } _, err = upconn.Write(init_raw.ToBytes()) if err == nil { upconn.Write(login_raw.ToBytes()) } if err != nil { upconn.Errorf("write error: %s", err.Error()) } spe := new(StartProxyEvent) spe.Upstream = upstream spe.NetworkEvent = ne.NetworkEvent spe.InitPacket = initial_pkt spe.LoginPacket = login_pkt StartProxy(spe) go PipeIt(conn, upconn) go PipeIt(upconn, conn) } }