2016-03-14 11:18:24 +08:00
// Copyright 2016 fatedier, fatedier@gmail.com
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
2016-02-18 18:24:48 +08:00
package server
2016-01-27 21:24:36 +08:00
import (
"fmt"
"strconv"
2016-04-18 15:16:40 +08:00
"strings"
2016-06-03 17:51:45 +08:00
"sync"
2016-01-27 21:24:36 +08:00
ini "github.com/vaughan0/go-ini"
2016-04-18 15:16:40 +08:00
2016-08-11 16:10:44 +08:00
"github.com/fatedier/frp/src/models/consts"
"github.com/fatedier/frp/src/models/metric"
"github.com/fatedier/frp/src/utils/log"
"github.com/fatedier/frp/src/utils/vhost"
2016-01-27 21:24:36 +08:00
)
// common config
var (
2016-08-13 22:32:11 +08:00
ConfigFile string = "./frps.ini"
BindAddr string = "0.0.0.0"
BindPort int64 = 7000
VhostHttpPort int64 = 0 // if VhostHttpPort equals 0, don't listen a public port for http protocol
VhostHttpsPort int64 = 0 // if VhostHttpsPort equals 0, don't listen a public port for https protocol
DashboardPort int64 = 0 // if DashboardPort equals 0, dashboard is not available
DashboardUsername string = "admin"
DashboardPassword string = "admin"
AssetsDir string = ""
LogFile string = "console"
LogWay string = "console" // console or file
LogLevel string = "info"
LogMaxDays int64 = 3
PrivilegeMode bool = false
PrivilegeToken string = ""
2016-12-21 01:01:44 +08:00
AuthTimeout int64 = 900
SubDomainHost string = ""
2016-07-31 02:17:10 +08:00
// if PrivilegeAllowPorts is not nil, tcp proxies which remote port exist in this map can be connected
PrivilegeAllowPorts map [ int64 ] struct { }
MaxPoolCount int64 = 100
2017-01-04 23:09:28 +08:00
HeartBeatTimeout int64 = 30
2016-07-31 02:17:10 +08:00
UserConnTimeout int64 = 10
2016-04-18 15:16:40 +08:00
2016-06-13 22:19:24 +08:00
VhostHttpMuxer * vhost . HttpMuxer
VhostHttpsMuxer * vhost . HttpsMuxer
2016-06-03 17:51:45 +08:00
ProxyServers map [ string ] * ProxyServer = make ( map [ string ] * ProxyServer ) // all proxy servers info and resources
ProxyServersMutex sync . RWMutex
2016-01-27 21:24:36 +08:00
)
func LoadConf ( confFile string ) ( err error ) {
2016-06-03 17:51:45 +08:00
err = loadCommonConf ( confFile )
if err != nil {
return err
}
// load all proxy server's configure and initialize
// and set ProxyServers map
newProxyServers , err := loadProxyConf ( confFile )
if err != nil {
return err
}
for _ , proxyServer := range newProxyServers {
proxyServer . Init ( )
}
ProxyServersMutex . Lock ( )
ProxyServers = newProxyServers
ProxyServersMutex . Unlock ( )
return nil
}
func loadCommonConf ( confFile string ) error {
2016-01-27 21:24:36 +08:00
var tmpStr string
var ok bool
conf , err := ini . LoadFile ( confFile )
if err != nil {
return err
}
// common
tmpStr , ok = conf . Get ( "common" , "bind_addr" )
if ok {
BindAddr = tmpStr
}
tmpStr , ok = conf . Get ( "common" , "bind_port" )
if ok {
2016-06-24 13:12:34 +08:00
v , err := strconv . ParseInt ( tmpStr , 10 , 64 )
if err == nil {
BindPort = v
}
2016-01-27 21:24:36 +08:00
}
2016-04-18 15:16:40 +08:00
tmpStr , ok = conf . Get ( "common" , "vhost_http_port" )
if ok {
VhostHttpPort , _ = strconv . ParseInt ( tmpStr , 10 , 64 )
} else {
VhostHttpPort = 0
}
2016-06-13 22:19:24 +08:00
tmpStr , ok = conf . Get ( "common" , "vhost_https_port" )
if ok {
VhostHttpsPort , _ = strconv . ParseInt ( tmpStr , 10 , 64 )
} else {
VhostHttpsPort = 0
}
2016-05-19 00:04:19 +08:00
tmpStr , ok = conf . Get ( "common" , "dashboard_port" )
if ok {
DashboardPort , _ = strconv . ParseInt ( tmpStr , 10 , 64 )
} else {
DashboardPort = 0
}
2016-12-21 01:01:44 +08:00
tmpStr , ok = conf . Get ( "common" , "dashboard_user" )
2016-08-13 22:32:11 +08:00
if ok {
DashboardUsername = tmpStr
}
2016-12-21 01:01:44 +08:00
tmpStr , ok = conf . Get ( "common" , "dashboard_pwd" )
2016-08-13 22:32:11 +08:00
if ok {
DashboardPassword = tmpStr
}
2016-08-10 20:18:36 +08:00
tmpStr , ok = conf . Get ( "common" , "assets_dir" )
if ok {
AssetsDir = tmpStr
}
2016-01-27 21:24:36 +08:00
tmpStr , ok = conf . Get ( "common" , "log_file" )
if ok {
LogFile = tmpStr
2016-03-14 00:21:40 +08:00
if LogFile == "console" {
LogWay = "console"
} else {
LogWay = "file"
}
2016-01-27 21:24:36 +08:00
}
tmpStr , ok = conf . Get ( "common" , "log_level" )
if ok {
LogLevel = tmpStr
}
2016-05-03 21:41:57 +08:00
tmpStr , ok = conf . Get ( "common" , "log_max_days" )
if ok {
2016-06-24 13:12:34 +08:00
v , err := strconv . ParseInt ( tmpStr , 10 , 64 )
if err == nil {
LogMaxDays = v
}
2016-05-03 21:41:57 +08:00
}
2016-06-26 22:36:07 +08:00
tmpStr , ok = conf . Get ( "common" , "privilege_mode" )
if ok {
if tmpStr == "true" {
PrivilegeMode = true
}
}
if PrivilegeMode == true {
2016-06-28 00:21:13 +08:00
tmpStr , ok = conf . Get ( "common" , "privilege_token" )
2016-06-26 22:36:07 +08:00
if ok {
2016-06-28 00:21:13 +08:00
if tmpStr == "" {
return fmt . Errorf ( "Parse conf error: privilege_token can not be null" )
}
PrivilegeToken = tmpStr
2016-06-26 22:36:07 +08:00
} else {
2016-06-28 00:21:13 +08:00
return fmt . Errorf ( "Parse conf error: privilege_token must be set if privilege_mode is enabled" )
2016-06-26 22:36:07 +08:00
}
2016-07-31 02:17:10 +08:00
PrivilegeAllowPorts = make ( map [ int64 ] struct { } )
tmpStr , ok = conf . Get ( "common" , "privilege_allow_ports" )
if ok {
// for example: 1000-2000,2001,2002,3000-4000
portRanges := strings . Split ( tmpStr , "," )
for _ , portRangeStr := range portRanges {
// 1000-2000 or 2001
portArray := strings . Split ( portRangeStr , "-" )
// lenght: only 1 or 2 is correct
rangeType := len ( portArray )
if rangeType == 1 {
singlePort , err := strconv . ParseInt ( portArray [ 0 ] , 10 , 64 )
if err != nil {
return fmt . Errorf ( "Parse conf error: privilege_allow_ports is incorrect, %v" , err )
}
PrivilegeAllowPorts [ singlePort ] = struct { } { }
} else if rangeType == 2 {
min , err := strconv . ParseInt ( portArray [ 0 ] , 10 , 64 )
if err != nil {
return fmt . Errorf ( "Parse conf error: privilege_allow_ports is incorrect, %v" , err )
}
max , err := strconv . ParseInt ( portArray [ 1 ] , 10 , 64 )
if err != nil {
return fmt . Errorf ( "Parse conf error: privilege_allow_ports is incorrect, %v" , err )
}
if max < min {
return fmt . Errorf ( "Parse conf error: privilege_allow_ports range incorrect" )
}
for i := min ; i <= max ; i ++ {
PrivilegeAllowPorts [ i ] = struct { } { }
}
} else {
return fmt . Errorf ( "Parse conf error: privilege_allow_ports is incorrect" )
}
}
}
2016-06-26 22:36:07 +08:00
}
2016-07-29 23:08:00 +08:00
tmpStr , ok = conf . Get ( "common" , "max_pool_count" )
if ok {
v , err := strconv . ParseInt ( tmpStr , 10 , 64 )
if err == nil && v >= 0 {
MaxPoolCount = v
}
}
2016-11-08 13:40:40 +08:00
tmpStr , ok = conf . Get ( "common" , "authentication_timeout" )
2016-11-05 14:15:16 +08:00
if ok {
v , err := strconv . ParseInt ( tmpStr , 10 , 64 )
if err != nil {
2016-11-08 13:40:40 +08:00
return fmt . Errorf ( "Parse conf error: authentication_timeout is incorrect" )
2016-11-05 14:15:16 +08:00
} else {
2016-11-08 13:40:40 +08:00
AuthTimeout = v
2016-11-05 14:15:16 +08:00
}
}
2016-12-21 01:01:44 +08:00
SubDomainHost , ok = conf . Get ( "common" , "subdomain_host" )
if ok {
SubDomainHost = strings . ToLower ( strings . TrimSpace ( SubDomainHost ) )
}
2016-12-30 16:16:07 +08:00
tmpStr , ok = conf . Get ( "common" , "heartbeat_timeout" )
if ok {
v , err := strconv . ParseInt ( tmpStr , 10 , 64 )
if err != nil {
return fmt . Errorf ( "Parse conf error: heartbeat_timeout is incorrect" )
} else {
HeartBeatTimeout = v
}
}
2016-06-03 17:51:45 +08:00
return nil
}
2016-05-03 21:41:57 +08:00
2016-06-03 17:51:45 +08:00
func loadProxyConf ( confFile string ) ( proxyServers map [ string ] * ProxyServer , err error ) {
var ok bool
proxyServers = make ( map [ string ] * ProxyServer )
conf , err := ini . LoadFile ( confFile )
if err != nil {
return proxyServers , err
}
2016-01-27 21:24:36 +08:00
// servers
for name , section := range conf {
if name != "common" {
2016-06-03 17:51:45 +08:00
proxyServer := NewProxyServer ( )
2016-01-27 21:24:36 +08:00
proxyServer . Name = name
2016-04-18 15:16:40 +08:00
proxyServer . Type , ok = section [ "type" ]
if ok {
2016-12-19 01:22:21 +08:00
if proxyServer . Type != "tcp" && proxyServer . Type != "http" && proxyServer . Type != "https" && proxyServer . Type != "udp" {
2016-06-03 17:51:45 +08:00
return proxyServers , fmt . Errorf ( "Parse conf error: proxy [%s] type error" , proxyServer . Name )
2016-04-18 15:16:40 +08:00
}
} else {
proxyServer . Type = "tcp"
}
2016-04-05 17:18:21 +08:00
proxyServer . AuthToken , ok = section [ "auth_token" ]
2016-01-27 21:24:36 +08:00
if ! ok {
2016-06-03 17:51:45 +08:00
return proxyServers , fmt . Errorf ( "Parse conf error: proxy [%s] no auth_token found" , proxyServer . Name )
2016-01-27 21:24:36 +08:00
}
2016-12-19 01:22:21 +08:00
// for tcp and udp
if proxyServer . Type == "tcp" || proxyServer . Type == "udp" {
2016-04-18 15:16:40 +08:00
proxyServer . BindAddr , ok = section [ "bind_addr" ]
if ! ok {
proxyServer . BindAddr = "0.0.0.0"
}
2016-01-27 21:24:36 +08:00
2016-04-18 15:16:40 +08:00
portStr , ok := section [ "listen_port" ]
if ok {
proxyServer . ListenPort , err = strconv . ParseInt ( portStr , 10 , 64 )
if err != nil {
2016-06-03 17:51:45 +08:00
return proxyServers , fmt . Errorf ( "Parse conf error: proxy [%s] listen_port error" , proxyServer . Name )
2016-04-18 15:16:40 +08:00
}
} else {
2016-06-03 17:51:45 +08:00
return proxyServers , fmt . Errorf ( "Parse conf error: proxy [%s] listen_port not found" , proxyServer . Name )
2016-04-18 15:16:40 +08:00
}
} else if proxyServer . Type == "http" {
// for http
2016-08-10 20:18:36 +08:00
proxyServer . ListenPort = VhostHttpPort
2016-04-18 15:16:40 +08:00
domainStr , ok := section [ "custom_domains" ]
if ok {
proxyServer . CustomDomains = strings . Split ( domainStr , "," )
for i , domain := range proxyServer . CustomDomains {
2016-12-21 01:01:44 +08:00
domain = strings . ToLower ( strings . TrimSpace ( domain ) )
// custom domain should not belong to subdomain_host
if SubDomainHost != "" && strings . Contains ( domain , SubDomainHost ) {
return proxyServers , fmt . Errorf ( "Parse conf error: proxy [%s] custom domain should not belong to subdomain_host" , proxyServer . Name )
}
proxyServer . CustomDomains [ i ] = domain
2016-04-18 15:16:40 +08:00
}
2017-01-13 02:21:17 +08:00
}
// subdomain
subdomainStr , ok := section [ "subdomain" ]
if ok {
if strings . Contains ( subdomainStr , "." ) || strings . Contains ( subdomainStr , "*" ) {
return proxyServers , fmt . Errorf ( "Parse conf error: proxy [%s] '.' and '*' is not supported in subdomain" , proxyServer . Name )
}
if SubDomainHost == "" {
return proxyServers , fmt . Errorf ( "Parse conf error: proxy [%s] subdomain is not supported because subdomain_host is empty" , proxyServer . Name )
}
proxyServer . SubDomain = subdomainStr + "." + SubDomainHost
}
if len ( proxyServer . CustomDomains ) == 0 && proxyServer . SubDomain == "" {
return proxyServers , fmt . Errorf ( "Parse conf error: proxy [%s] custom_domains and subdomain should set at least one of them when type is http" , proxyServer . Name )
2016-06-13 22:19:24 +08:00
}
2016-12-18 22:40:58 +08:00
2016-12-25 01:53:23 +08:00
// locations
locations , ok := section [ "locations" ]
if ok {
proxyServer . Locations = strings . Split ( locations , "," )
2016-12-19 10:34:37 +08:00
} else {
proxyServer . Locations = [ ] string { "" }
2016-12-18 22:40:58 +08:00
}
2016-06-13 22:19:24 +08:00
} else if proxyServer . Type == "https" {
// for https
2016-08-10 20:18:36 +08:00
proxyServer . ListenPort = VhostHttpsPort
2016-06-13 22:19:24 +08:00
domainStr , ok := section [ "custom_domains" ]
if ok {
proxyServer . CustomDomains = strings . Split ( domainStr , "," )
for i , domain := range proxyServer . CustomDomains {
2016-12-21 01:01:44 +08:00
domain = strings . ToLower ( strings . TrimSpace ( domain ) )
if SubDomainHost != "" && strings . Contains ( domain , SubDomainHost ) {
return proxyServers , fmt . Errorf ( "Parse conf error: proxy [%s] custom domain should not belong to subdomain_host" , proxyServer . Name )
}
proxyServer . CustomDomains [ i ] = domain
2016-06-13 22:19:24 +08:00
}
2017-01-13 02:21:17 +08:00
}
// subdomain
subdomainStr , ok := section [ "subdomain" ]
if ok {
if strings . Contains ( subdomainStr , "." ) || strings . Contains ( subdomainStr , "*" ) {
return proxyServers , fmt . Errorf ( "Parse conf error: proxy [%s] '.' and '*' is not supported in subdomain" , proxyServer . Name )
}
if SubDomainHost == "" {
return proxyServers , fmt . Errorf ( "Parse conf error: proxy [%s] subdomain is not supported because subdomain_host is empty" , proxyServer . Name )
}
proxyServer . SubDomain = subdomainStr + "." + SubDomainHost
}
if len ( proxyServer . CustomDomains ) == 0 && proxyServer . SubDomain == "" {
return proxyServers , fmt . Errorf ( "Parse conf error: proxy [%s] custom_domains and subdomain should set at least one of them when type is https" , proxyServer . Name )
2016-01-27 21:24:36 +08:00
}
}
2016-06-03 17:51:45 +08:00
proxyServers [ proxyServer . Name ] = proxyServer
}
}
2016-07-17 21:42:21 +08:00
// set metric statistics of all proxies
2016-12-25 01:53:23 +08:00
for name , p := range proxyServers {
metric . SetProxyInfo ( name , p . Type , p . BindAddr , p . UseEncryption , p . UseGzip ,
2016-12-25 02:09:01 +08:00
p . PrivilegeMode , p . CustomDomains , p . Locations , p . ListenPort )
2016-07-17 21:42:21 +08:00
}
2016-06-03 17:51:45 +08:00
return proxyServers , nil
}
2016-01-27 21:24:36 +08:00
2016-06-03 17:51:45 +08:00
// the function can only reload proxy configures
// common section won't be changed
func ReloadConf ( confFile string ) ( err error ) {
loadProxyServers , err := loadProxyConf ( confFile )
if err != nil {
return err
}
ProxyServersMutex . Lock ( )
for name , proxyServer := range loadProxyServers {
oldProxyServer , ok := ProxyServers [ name ]
if ok {
if ! oldProxyServer . Compare ( proxyServer ) {
oldProxyServer . Close ( )
proxyServer . Init ( )
ProxyServers [ name ] = proxyServer
log . Info ( "ProxyName [%s] configure change, restart" , name )
}
} else {
2016-01-27 21:24:36 +08:00
proxyServer . Init ( )
2016-06-03 17:51:45 +08:00
ProxyServers [ name ] = proxyServer
log . Info ( "ProxyName [%s] is new, init it" , name )
2016-01-27 21:24:36 +08:00
}
}
2016-06-26 22:36:07 +08:00
// proxies created by PrivilegeMode won't be deleted
2016-06-03 17:51:45 +08:00
for name , oldProxyServer := range ProxyServers {
_ , ok := loadProxyServers [ name ]
if ! ok {
2016-06-26 22:36:07 +08:00
if ! oldProxyServer . PrivilegeMode {
oldProxyServer . Close ( )
delete ( ProxyServers , name )
log . Info ( "ProxyName [%s] deleted, close it" , name )
} else {
log . Info ( "ProxyName [%s] created by PrivilegeMode, won't be closed" , name )
}
2016-06-03 17:51:45 +08:00
}
2016-01-27 21:24:36 +08:00
}
2016-06-03 17:51:45 +08:00
ProxyServersMutex . Unlock ( )
2016-01-27 21:24:36 +08:00
return nil
}
2016-06-26 22:36:07 +08:00
func CreateProxy ( s * ProxyServer ) error {
ProxyServersMutex . Lock ( )
defer ProxyServersMutex . Unlock ( )
oldServer , ok := ProxyServers [ s . Name ]
if ok {
if oldServer . Status == consts . Working {
return fmt . Errorf ( "this proxy is already working now" )
}
2016-12-28 00:56:55 +08:00
oldServer . Lock ( )
2016-12-27 01:19:19 +08:00
oldServer . Release ( )
2016-12-28 00:56:55 +08:00
oldServer . Unlock ( )
2016-06-26 22:36:07 +08:00
if oldServer . PrivilegeMode {
delete ( ProxyServers , s . Name )
}
}
ProxyServers [ s . Name ] = s
2016-12-25 01:53:23 +08:00
metric . SetProxyInfo ( s . Name , s . Type , s . BindAddr , s . UseEncryption , s . UseGzip ,
2016-12-25 02:09:01 +08:00
s . PrivilegeMode , s . CustomDomains , s . Locations , s . ListenPort )
2016-06-26 22:36:07 +08:00
return nil
}
2016-06-28 00:55:30 +08:00
func DeleteProxy ( proxyName string ) {
ProxyServersMutex . Lock ( )
defer ProxyServersMutex . Unlock ( )
delete ( ProxyServers , proxyName )
}
2016-12-20 01:30:10 +08:00
func GetProxyServer ( proxyName string ) ( p * ProxyServer , ok bool ) {
ProxyServersMutex . RLock ( )
defer ProxyServersMutex . RUnlock ( )
p , ok = ProxyServers [ proxyName ]
return
}