server_common.go 12.3 KB
Newer Older
F
fatedier 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
// 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.

package config

import (
	"fmt"
	"strconv"
	"strings"

	ini "github.com/vaughan0/go-ini"
23 24

	"github.com/fatedier/frp/utils/util"
F
fatedier 已提交
25 26
)

27 28 29
// ServerCommonConf contains information for a server service. It is
// recommended to use GetDefaultServerConf instead of creating this object
// directly, so that all unspecified fields have reasonable default values.
F
fatedier 已提交
30
type ServerCommonConf struct {
31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
	// BindAddr specifies the address that the server binds to. By default,
	// this value is "0.0.0.0".
	BindAddr string `json:"bind_addr"`
	// BindPort specifies the port that the server listens on. By default, this
	// value is 7000.
	BindPort int `json:"bind_port"`
	// BindUdpPort specifies the UDP port that the server listens on. If this
	// value is 0, the server will not listen for UDP connections. By default,
	// this value is 0
	BindUdpPort int `json:"bind_udp_port"`
	// BindKcpPort specifies the KCP port that the server listens on. If this
	// value is 0, the server will not listen for KCP connections. By default,
	// this value is 0.
	KcpBindPort int `json:"kcp_bind_port"`
	// ProxyBindAddr specifies the address that the proxy binds to. This value
	// may be the same as BindAddr. By default, this value is "0.0.0.0".
F
fatedier 已提交
47
	ProxyBindAddr string `json:"proxy_bind_addr"`
F
fatedier 已提交
48

49 50 51
	// VhostHttpPort specifies the port that the server listens for HTTP Vhost
	// requests. If this value is 0, the server will not listen for HTTP
	// requests. By default, this value is 0.
F
fatedier 已提交
52
	VhostHttpPort int `json:"vhost_http_port"`
F
fatedier 已提交
53

54 55 56
	// VhostHttpsPort specifies the port that the server listens for HTTPS
	// Vhost requests. If this value is 0, the server will not listen for HTTPS
	// requests. By default, this value is 0.
F
go vet  
fatedier 已提交
57
	VhostHttpsPort int `json:"vhost_https_port"`
F
fatedier 已提交
58

59 60
	// VhostHttpTimeout specifies the response header timeout for the Vhost
	// HTTP server, in seconds. By default, this value is 60.
F
fatedier 已提交
61 62
	VhostHttpTimeout int64 `json:"vhost_http_timeout"`

63 64
	// DashboardAddr specifies the address that the dashboard binds to. By
	// default, this value is "0.0.0.0".
F
fatedier 已提交
65
	DashboardAddr string `json:"dashboard_addr"`
F
fatedier 已提交
66

67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128
	// DashboardPort specifies the port that the dashboard listens on. If this
	// value is 0, the dashboard will not be started. By default, this value is
	// 0.
	DashboardPort int `json:"dashboard_port"`
	// DashboardUser specifies the username that the dashboard will use for
	// login. By default, this value is "admin".
	DashboardUser string `json:"dashboard_user"`
	// DashboardUser specifies the password that the dashboard will use for
	// login. By default, this value is "admin".
	DashboardPwd string `json:"dashboard_pwd"`
	// AssetsDir specifies the local directory that the dashboard will load
	// resources from. If this value is "", assets will be loaded from the
	// bundled executable using statik. By default, this value is "".
	AssetsDir string `json:"asserts_dir"`
	// LogFile specifies a file where logs will be written to. This value will
	// only be used if LogWay is set appropriately. By default, this value is
	// "console".
	LogFile string `json:"log_file"`
	// LogWay specifies the way logging is managed. Valid values are "console"
	// or "file". If "console" is used, logs will be printed to stdout. If
	// "file" is used, logs will be printed to LogFile. By default, this value
	// is "console".
	LogWay string `json:"log_way"`
	// LogLevel specifies the minimum log level. Valid values are "trace",
	// "debug", "info", "warn", and "error". By default, this value is "info".
	LogLevel string `json:"log_level"`
	// LogMaxDays specifies the maximum number of days to store log information
	// before deletion. This is only used if LogWay == "file". By default, this
	// value is 0.
	LogMaxDays int64 `json:"log_max_days"`
	// DisableLogColor disables log colors when LogWay == "console" when set to
	// true. By default, this value is false.
	DisableLogColor bool `json:"disable_log_color"`
	// Token specifies the authorization token used to authenticate keys
	// received from clients. Clients must have a matching token to be
	// authorized to use the server. By default, this value is "".
	Token string `json:"token"`
	// SubDomainHost specifies the domain that will be attached to sub-domains
	// requested by the client when using Vhost proxying. For example, if this
	// value is set to "frps.com" and the client requested the subdomain
	// "test", the resulting URL would be "test.frps.com". By default, this
	// value is "".
	SubDomainHost string `json:"subdomain_host"`
	// TcpMux toggles TCP stream multiplexing. This allows multiple requests
	// from a client to share a single TCP connection. By default, this value
	// is true.
	TcpMux bool `json:"tcp_mux"`
	// Custom404Page specifies a path to a custom 404 page to display. If this
	// value is "", a default page will be displayed. By default, this value is
	// "".
	Custom404Page string `json:"custom_404_page"`

	// AllowPorts specifies a set of ports that clients are able to proxy to.
	// If the length of this value is 0, all ports are allowed. By default,
	// this value is an empty set.
	AllowPorts map[int]struct{}
	// MaxPoolCount specifies the maximum pool size for each proxy. By default,
	// this value is 5.
	MaxPoolCount int64 `json:"max_pool_count"`
	// MaxPortsPerClient specifies the maximum number of ports a single client
	// may proxy to. If this value is 0, no limit will be applied. By default,
	// this value is 0.
F
fatedier 已提交
129
	MaxPortsPerClient int64 `json:"max_ports_per_client"`
130 131 132 133 134 135 136
	// HeartBeatTimeout specifies the maximum time to wait for a heartbeat
	// before terminating the connection. It is not recommended to change this
	// value. By default, this value is 90.
	HeartBeatTimeout int64 `json:"heart_beat_timeout"`
	// UserConnTimeout specifies the maximum time to wait for a work
	// connection. By default, this value is 10.
	UserConnTimeout int64 `json:"user_conn_timeout"`
F
fatedier 已提交
137 138
}

139 140
// GetDefaultServerConf returns a server configuration with reasonable
// defaults.
141 142
func GetDefaultServerConf() ServerCommonConf {
	return ServerCommonConf{
F
fatedier 已提交
143 144 145 146 147 148 149
		BindAddr:          "0.0.0.0",
		BindPort:          7000,
		BindUdpPort:       0,
		KcpBindPort:       0,
		ProxyBindAddr:     "0.0.0.0",
		VhostHttpPort:     0,
		VhostHttpsPort:    0,
F
fatedier 已提交
150
		VhostHttpTimeout:  60,
F
fatedier 已提交
151 152 153 154 155 156 157 158 159
		DashboardAddr:     "0.0.0.0",
		DashboardPort:     0,
		DashboardUser:     "admin",
		DashboardPwd:      "admin",
		AssetsDir:         "",
		LogFile:           "console",
		LogWay:            "console",
		LogLevel:          "info",
		LogMaxDays:        3,
160
		DisableLogColor:   false,
F
fatedier 已提交
161 162 163 164 165 166 167 168
		Token:             "",
		SubDomainHost:     "",
		TcpMux:            true,
		AllowPorts:        make(map[int]struct{}),
		MaxPoolCount:      5,
		MaxPortsPerClient: 0,
		HeartBeatTimeout:  90,
		UserConnTimeout:   10,
F
fatedier 已提交
169
		Custom404Page:     "",
F
fatedier 已提交
170 171 172
	}
}

173 174
// UnmarshalServerConfFromIni parses the contents of a server configuration ini
// file and returns the resulting server configuration.
175 176
func UnmarshalServerConfFromIni(content string) (cfg ServerCommonConf, err error) {
	cfg = GetDefaultServerConf()
F
fatedier 已提交
177 178 179 180

	conf, err := ini.Load(strings.NewReader(content))
	if err != nil {
		err = fmt.Errorf("parse ini conf file error: %v", err)
181
		return ServerCommonConf{}, err
F
fatedier 已提交
182 183
	}

F
fatedier 已提交
184 185 186 187 188
	var (
		tmpStr string
		ok     bool
		v      int64
	)
F
fatedier 已提交
189
	if tmpStr, ok = conf.Get("common", "bind_addr"); ok {
F
fatedier 已提交
190 191 192
		cfg.BindAddr = tmpStr
	}

F
fatedier 已提交
193
	if tmpStr, ok = conf.Get("common", "bind_port"); ok {
194 195 196 197 198
		if v, err = strconv.ParseInt(tmpStr, 10, 64); err != nil {
			err = fmt.Errorf("Parse conf error: invalid bind_port")
			return
		} else {
			cfg.BindPort = int(v)
F
fatedier 已提交
199 200 201
		}
	}

F
fatedier 已提交
202
	if tmpStr, ok = conf.Get("common", "bind_udp_port"); ok {
203 204 205 206 207
		if v, err = strconv.ParseInt(tmpStr, 10, 64); err != nil {
			err = fmt.Errorf("Parse conf error: invalid bind_udp_port")
			return
		} else {
			cfg.BindUdpPort = int(v)
F
fatedier 已提交
208 209 210
		}
	}

F
fatedier 已提交
211
	if tmpStr, ok = conf.Get("common", "kcp_bind_port"); ok {
212 213 214 215 216
		if v, err = strconv.ParseInt(tmpStr, 10, 64); err != nil {
			err = fmt.Errorf("Parse conf error: invalid kcp_bind_port")
			return
		} else {
			cfg.KcpBindPort = int(v)
F
fatedier 已提交
217 218 219
		}
	}

F
fatedier 已提交
220
	if tmpStr, ok = conf.Get("common", "proxy_bind_addr"); ok {
F
fatedier 已提交
221 222 223 224 225
		cfg.ProxyBindAddr = tmpStr
	} else {
		cfg.ProxyBindAddr = cfg.BindAddr
	}

F
fatedier 已提交
226
	if tmpStr, ok = conf.Get("common", "vhost_http_port"); ok {
227 228
		if v, err = strconv.ParseInt(tmpStr, 10, 64); err != nil {
			err = fmt.Errorf("Parse conf error: invalid vhost_http_port")
F
fatedier 已提交
229
			return
230 231
		} else {
			cfg.VhostHttpPort = int(v)
F
fatedier 已提交
232 233 234 235 236
		}
	} else {
		cfg.VhostHttpPort = 0
	}

F
fatedier 已提交
237
	if tmpStr, ok = conf.Get("common", "vhost_https_port"); ok {
238 239
		if v, err = strconv.ParseInt(tmpStr, 10, 64); err != nil {
			err = fmt.Errorf("Parse conf error: invalid vhost_https_port")
F
fatedier 已提交
240
			return
241 242
		} else {
			cfg.VhostHttpsPort = int(v)
F
fatedier 已提交
243 244 245 246 247
		}
	} else {
		cfg.VhostHttpsPort = 0
	}

F
fatedier 已提交
248 249 250 251 252 253 254 255 256 257
	if tmpStr, ok = conf.Get("common", "vhost_http_timeout"); ok {
		v, errRet := strconv.ParseInt(tmpStr, 10, 64)
		if errRet != nil || v < 0 {
			err = fmt.Errorf("Parse conf error: invalid vhost_http_timeout")
			return
		} else {
			cfg.VhostHttpTimeout = v
		}
	}

F
fatedier 已提交
258
	if tmpStr, ok = conf.Get("common", "dashboard_addr"); ok {
T
timerever 已提交
259 260 261 262 263
		cfg.DashboardAddr = tmpStr
	} else {
		cfg.DashboardAddr = cfg.BindAddr
	}

F
fatedier 已提交
264
	if tmpStr, ok = conf.Get("common", "dashboard_port"); ok {
265 266
		if v, err = strconv.ParseInt(tmpStr, 10, 64); err != nil {
			err = fmt.Errorf("Parse conf error: invalid dashboard_port")
F
fatedier 已提交
267
			return
268 269
		} else {
			cfg.DashboardPort = int(v)
F
fatedier 已提交
270 271 272 273 274
		}
	} else {
		cfg.DashboardPort = 0
	}

F
fatedier 已提交
275
	if tmpStr, ok = conf.Get("common", "dashboard_user"); ok {
F
fatedier 已提交
276 277 278
		cfg.DashboardUser = tmpStr
	}

F
fatedier 已提交
279
	if tmpStr, ok = conf.Get("common", "dashboard_pwd"); ok {
F
fatedier 已提交
280 281 282
		cfg.DashboardPwd = tmpStr
	}

F
fatedier 已提交
283
	if tmpStr, ok = conf.Get("common", "assets_dir"); ok {
F
fatedier 已提交
284 285 286
		cfg.AssetsDir = tmpStr
	}

F
fatedier 已提交
287
	if tmpStr, ok = conf.Get("common", "log_file"); ok {
F
fatedier 已提交
288 289 290 291 292 293 294 295
		cfg.LogFile = tmpStr
		if cfg.LogFile == "console" {
			cfg.LogWay = "console"
		} else {
			cfg.LogWay = "file"
		}
	}

F
fatedier 已提交
296
	if tmpStr, ok = conf.Get("common", "log_level"); ok {
F
fatedier 已提交
297 298 299
		cfg.LogLevel = tmpStr
	}

F
fatedier 已提交
300
	if tmpStr, ok = conf.Get("common", "log_max_days"); ok {
F
fatedier 已提交
301 302 303 304 305 306
		v, err = strconv.ParseInt(tmpStr, 10, 64)
		if err == nil {
			cfg.LogMaxDays = v
		}
	}

307 308 309 310
	if tmpStr, ok = conf.Get("common", "disable_log_color"); ok && tmpStr == "true" {
		cfg.DisableLogColor = true
	}

F
fatedier 已提交
311
	cfg.Token, _ = conf.Get("common", "token")
F
fatedier 已提交
312

F
fatedier 已提交
313
	if allowPortsStr, ok := conf.Get("common", "allow_ports"); ok {
F
fatedier 已提交
314 315 316
		// e.g. 1000-2000,2001,2002,3000-4000
		ports, errRet := util.ParseRangeNumbers(allowPortsStr)
		if errRet != nil {
F
fatedier 已提交
317
			err = fmt.Errorf("Parse conf error: allow_ports: %v", errRet)
F
fatedier 已提交
318 319
			return
		}
320

F
fatedier 已提交
321
		for _, port := range ports {
F
fatedier 已提交
322
			cfg.AllowPorts[int(port)] = struct{}{}
F
fatedier 已提交
323 324 325
		}
	}

F
fatedier 已提交
326
	if tmpStr, ok = conf.Get("common", "max_pool_count"); ok {
F
fatedier 已提交
327 328 329 330 331 332 333 334
		if v, err = strconv.ParseInt(tmpStr, 10, 64); err != nil {
			err = fmt.Errorf("Parse conf error: invalid max_pool_count")
			return
		} else {
			if v < 0 {
				err = fmt.Errorf("Parse conf error: invalid max_pool_count")
				return
			}
F
fatedier 已提交
335 336 337 338
			cfg.MaxPoolCount = v
		}
	}

F
fatedier 已提交
339
	if tmpStr, ok = conf.Get("common", "max_ports_per_client"); ok {
F
fatedier 已提交
340 341 342 343 344 345 346 347 348 349 350 351
		if v, err = strconv.ParseInt(tmpStr, 10, 64); err != nil {
			err = fmt.Errorf("Parse conf error: invalid max_ports_per_client")
			return
		} else {
			if v < 0 {
				err = fmt.Errorf("Parse conf error: invalid max_ports_per_client")
				return
			}
			cfg.MaxPortsPerClient = v
		}
	}

F
fatedier 已提交
352
	if tmpStr, ok = conf.Get("common", "subdomain_host"); ok {
F
fatedier 已提交
353 354 355
		cfg.SubDomainHost = strings.ToLower(strings.TrimSpace(tmpStr))
	}

F
fatedier 已提交
356
	if tmpStr, ok = conf.Get("common", "tcp_mux"); ok && tmpStr == "false" {
357 358 359 360 361
		cfg.TcpMux = false
	} else {
		cfg.TcpMux = true
	}

F
fatedier 已提交
362 363 364 365
	if tmpStr, ok = conf.Get("common", "custom_404_page"); ok {
		cfg.Custom404Page = tmpStr
	}

F
fatedier 已提交
366
	if tmpStr, ok = conf.Get("common", "heartbeat_timeout"); ok {
F
fatedier 已提交
367 368 369 370 371 372 373 374 375 376
		v, errRet := strconv.ParseInt(tmpStr, 10, 64)
		if errRet != nil {
			err = fmt.Errorf("Parse conf error: heartbeat_timeout is incorrect")
			return
		} else {
			cfg.HeartBeatTimeout = v
		}
	}
	return
}
F
fatedier 已提交
377 378 379 380

func (cfg *ServerCommonConf) Check() (err error) {
	return
}