コード例 #1
0
ファイル: server.go プロジェクト: mijo-sjx/lantern
// InitServerCert initializes a PK + cert for use by a server proxy, signed by
// the CA certificate.  We always generate a new certificate just in case.
func (ctx *CertContext) InitServerCert(host string) (err error) {
	if ctx.PK, err = keyman.LoadPKFromFile(ctx.PKFile); err != nil {
		if os.IsNotExist(err) {
			log.Debugf("Creating new PK at: %s", ctx.PKFile)
			if ctx.PK, err = keyman.GeneratePK(2048); err != nil {
				return
			}
			if err = ctx.PK.WriteToFile(ctx.PKFile); err != nil {
				return fmt.Errorf("Unable to save private key: %s", err)
			}
		} else {
			return fmt.Errorf("Unable to read private key, even though it exists: %s", err)
		}
	}

	log.Debugf("Creating new server cert at: %s", ctx.ServerCertFile)
	ctx.ServerCert, err = ctx.PK.TLSCertificateFor("Lantern", host, TEN_YEARS_FROM_TODAY, true, nil)
	if err != nil {
		return
	}
	err = ctx.ServerCert.WriteToFile(ctx.ServerCertFile)
	if err != nil {
		return
	}
	return nil
}
コード例 #2
0
ファイル: statreporter.go プロジェクト: mijo-sjx/lantern
func postStats(accumulators map[string]map[string]int64) error {
	report := map[string]interface{}{
		"dims": map[string]string{
			"country": country,
		},
	}

	for category, accum := range accumulators {
		report[category] = accum
	}

	jsonBytes, err := json.Marshal(report)
	if err != nil {
		return fmt.Errorf("Unable to marshal json for stats: %s", err)
	}

	url := fmt.Sprintf(StatshubUrlTemplate, addr, id)
	resp, err := http.Post(url, "application/json", bytes.NewReader(jsonBytes))
	if err != nil {
		return fmt.Errorf("Unable to post stats to statshub: %s", err)
	}
	defer resp.Body.Close()
	if resp.StatusCode != 200 {
		return fmt.Errorf("Unexpected response status posting stats to statshub: %d", resp.StatusCode)
	}

	log.Debugf("Reported %s to statshub", string(jsonBytes))
	return nil
}
コード例 #3
0
ファイル: config.go プロジェクト: mijo-sjx/lantern
// UpdatedFrom creates a new Config by merging the given yaml into this Config.
// Any servers in the updated yaml replace ones in the original Config and any
// masquerade sets in the updated yaml replace ones in the original Config.
func (orig *Config) UpdatedFrom(updateBytes []byte) (*Config, error) {
	origCopy := orig.deepCopy()
	updated := orig.deepCopy()
	err := yaml.Unmarshal(updateBytes, updated)
	if err != nil {
		return nil, fmt.Errorf("Unable to unmarshal YAML for update: %s", err)
	}
	// Need to de-duplicate servers, since yaml appends them
	servers := make(map[string]*client.ServerInfo)
	for _, server := range updated.Client.Servers {
		servers[server.Host] = server
	}
	updated.Client.Servers = make([]*client.ServerInfo, len(servers))
	i := 0
	for _, server := range servers {
		updated.Client.Servers[i] = server
		i = i + 1
	}
	origCopy.Client.SortServers()
	updated.Client.SortServers()
	if !reflect.DeepEqual(origCopy, updated) {
		log.Debugf("Saving updated")
		err = updated.SaveToDisk()
		if err != nil {
			return nil, err
		}
	}
	return updated, nil
}
コード例 #4
0
ファイル: client.go プロジェクト: mijo-sjx/lantern
// ServeHTTP implements the method from interface http.Handler
func (client *Client) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
	server := client.randomServer(req)
	log.Debugf("Using server %s to handle request for %s", server.info.Host, req.RequestURI)
	if req.Method == CONNECT {
		server.enproxyConfig.Intercept(resp, req)
	} else {
		server.reverseProxy.ServeHTTP(resp, req)
	}
}
コード例 #5
0
ファイル: statreporter.go プロジェクト: mijo-sjx/lantern
// Start runs a goroutine that periodically coalesces the collected statistics
// and reports them to statshub via HTTP post
func Start(reportingPeriod time.Duration, statshubAddr string, instanceId string, countryCode string) {
	alreadyStarted := !atomic.CompareAndSwapInt32(&started, 0, 1)
	if alreadyStarted {
		log.Debugf("statreporter already started, not starting again")
		return
	}
	period = reportingPeriod
	addr = statshubAddr
	id = instanceId
	country = strings.ToLower(countryCode)
	// We buffer the updates channel to be able to continue accepting updates while we're posting a report
	updatesCh = make(chan *update, 10000)
	accumulators = make(map[string]map[string]int64)

	timer := time.NewTimer(timeToNextReport())
	for {
		select {
		case update := <-updatesCh:
			// Coalesce
			accum := accumulators[update.category]
			if accum == nil {
				accum = make(map[string]int64)
				accumulators[update.category] = accum
			}
			switch update.action {
			case set:
				accum[update.key] = update.val
			case add:
				accum[update.key] = accum[update.key] + update.val
			}
		case <-timer.C:
			if len(accumulators) == 0 {
				log.Debugf("No stats to report")
			} else {
				err := postStats(accumulators)
				if err != nil {
					log.Errorf("Error on posting stats: %s", err)
				}
				accumulators = make(map[string]map[string]int64)
			}
			timer.Reset(timeToNextReport())
		}
	}
}
コード例 #6
0
ファイル: server.go プロジェクト: mijo-sjx/lantern
func (server *Server) startServingStatsIfNecessary() bool {
	if server.StatServer != nil {
		log.Debugf("Serving stats at address: %s", server.StatServer.Addr)
		go server.StatServer.ListenAndServe()
		return true
	} else {
		log.Debug("Not serving stats (no statsaddr specified)")
		return false
	}
}
コード例 #7
0
ファイル: client.go プロジェクト: mijo-sjx/lantern
// ListenAndServe makes the client listen for HTTP connections
func (client *Client) ListenAndServe() error {
	httpServer := &http.Server{
		Addr:         client.Addr,
		ReadTimeout:  client.ReadTimeout,
		WriteTimeout: client.WriteTimeout,
		Handler:      client,
	}

	log.Debugf("About to start client (http) proxy at %s", client.Addr)
	return httpServer.ListenAndServe()
}
コード例 #8
0
// On windows, make sure that flashlight stops running if its parent
// process has stopped.  This is necessary on Windows, where child processes
// don't tend to get terminated it the parent process dies unexpectedly.
func init() {
	go func() {
		if *parentPID == 0 {
			log.Errorf("No parent PID specified, not terminating when orphaned")
		}
		parent, _ := os.FindProcess(*parentPID)
		if parent == nil {
			log.Errorf("No parent, not terminating when orphaned")
			return
		}
		log.Debugf("Waiting for parent %d to terminate", *parentPID)
		parent.Wait()
		log.Debug("Parent no longer running, terminating")
		os.Exit(0)
	}()
}
コード例 #9
0
ファイル: masquerade.go プロジェクト: mijo-sjx/lantern
// doVerify does the verification and returns a boolean indicating whether or
// not to continue processing more verifications.
func (vms *verifiedMasqueradeSet) doVerify(masquerade *Masquerade) bool {
	errCh := make(chan error, 2)
	go func() {
		// Limit amount of time we'll wait for a response
		time.Sleep(30 * time.Second)
		errCh <- fmt.Errorf("Timed out verifying %s", masquerade.Domain)
	}()
	go func() {
		start := time.Now()
		httpClient := HttpClient(vms.testServer, masquerade)
		req, _ := http.NewRequest("HEAD", "http://www.google.com/humans.txt", nil)
		resp, err := httpClient.Do(req)
		if err != nil {
			errCh <- fmt.Errorf("HTTP ERROR FOR MASQUERADE %v: %v", masquerade.Domain, err)
			return
		} else {
			body, err := ioutil.ReadAll(resp.Body)
			defer resp.Body.Close()
			if err != nil {
				errCh <- fmt.Errorf("HTTP Body Error: %s", body)
			} else {
				delta := time.Now().Sub(start)
				log.Debugf("SUCCESSFUL CHECK FOR: %s IN %s, %s", masquerade.Domain, delta, body)
				errCh <- nil
			}
		}
	}()
	err := <-errCh
	if err != nil {
		log.Error(err)
		return true
	}
	if vms.incrementVerifiedCount() {
		vms.verifiedCh <- masquerade
		return true
	}
	return false
}
コード例 #10
0
ファイル: config.go プロジェクト: mijo-sjx/lantern
// LoadFromDisk loads a Config from flashlight.yaml inside the configured
// configDir with default values populated as necessary. If the file couldn't be
// loaded for some reason, this function returns a new default Config. This
// function assumes that flag.Parse() has already been called.
func LoadFromDisk() (*Config, error) {
	filename := InConfigDir("flashlight.yaml")
	log.Debugf("Loading config from: %s", filename)

	cfg := &Config{filename: filename}
	fileInfo, err := os.Stat(filename)
	if err != nil {
		err = fmt.Errorf("Unable to stat config file %s: %s", filename, err)
	} else {
		cfg.lastFileInfo = fileInfo
		bytes, err := ioutil.ReadFile(filename)
		if err != nil {
			err = fmt.Errorf("Error reading config from %s: %s", filename, err)
		} else {
			err = yaml.Unmarshal(bytes, cfg)
			if err != nil {
				err = fmt.Errorf("Error unmarshaling config yaml from file %s: %s", filename, err)
			}
		}
	}
	cfg.applyDefaults()
	return cfg, err
}
コード例 #11
0
ファイル: server.go プロジェクト: mijo-sjx/lantern
func (server *Server) ListenAndServe() error {
	err := server.CertContext.InitServerCert(strings.Split(server.Addr, ":")[0])
	if err != nil {
		return fmt.Errorf("Unable to init server cert: %s", err)
	}

	// Set up an enproxy Proxy
	proxy := &enproxy.Proxy{
		Dial: server.dialDestination,
		Host: server.Host,
	}

	if server.Host != "" {
		log.Debugf("Running as host %s", server.Host)
	}

	// Hook into stats reporting if necessary
	servingStats := server.startServingStatsIfNecessary()

	// Add callbacks to track bytes given
	proxy.OnBytesReceived = func(ip string, bytes int64) {
		statreporter.OnBytesGiven(ip, bytes)
		if servingStats {
			server.StatServer.OnBytesReceived(ip, bytes)
		}
	}
	proxy.OnBytesSent = func(ip string, bytes int64) {
		statreporter.OnBytesGiven(ip, bytes)
		if servingStats {
			server.StatServer.OnBytesSent(ip, bytes)
		}
	}

	proxy.Start()

	httpServer := &http.Server{
		Handler:      proxy,
		ReadTimeout:  server.ReadTimeout,
		WriteTimeout: server.WriteTimeout,
	}

	log.Debugf("About to start server (https) proxy at %s", server.Addr)

	tlsConfig := server.TLSConfig
	if server.TLSConfig == nil {
		tlsConfig = DEFAULT_TLS_SERVER_CONFIG
	}
	cert, err := tls.LoadX509KeyPair(server.CertContext.ServerCertFile, server.CertContext.PKFile)
	if err != nil {
		return fmt.Errorf("Unable to load certificate and key from %s and %s: %s", server.CertContext.ServerCertFile, server.CertContext.PKFile, err)
	}
	tlsConfig.Certificates = []tls.Certificate{cert}

	listener, err := tls.Listen("tcp", server.Addr, tlsConfig)
	if err != nil {
		return fmt.Errorf("Unable to listen for tls connections at %s: %s", server.Addr, err)
	}

	// We use an idle timing listener to time out idle HTTP connections, since
	// the CDNs seem to like keeping lots of connections open indefinitely.
	idleTimingListener := idletiming.Listener(listener, httpIdleTimeout, nil)
	return httpServer.Serve(idleTimingListener)
}
コード例 #12
0
ファイル: common.go プロジェクト: mijo-sjx/lantern
// DumpHeaders logs the given headers (request or response).
func DumpHeaders(category string, headers *http.Header) {
	log.Debugf("%s Headers\n%s\n%s\n%s\n\n", category, HR, spew.Sdump(headers), HR)
}
コード例 #13
0
ファイル: client.go プロジェクト: mijo-sjx/lantern
// Configure updates the client's configuration.  Configure can be called
// before or after ListenAndServe, and can be called multiple times.  The
// optional enproxyConfigs parameter allows explicitly specifying enproxy
// configurations for the servers in ClientConfig in lieu of building them based
// on the ServerInfo in ClientConfig (mostly useful for testing).
func (client *Client) Configure(cfg *ClientConfig, enproxyConfigs []*enproxy.Config) {
	client.cfgMutex.Lock()
	defer client.cfgMutex.Unlock()

	log.Debug("Configure() called")
	if client.cfg != nil {
		if reflect.DeepEqual(client.cfg, cfg) {
			log.Debugf("Client configuration unchanged")
			return
		} else {
			log.Debugf("Client configuration changed")
		}
	} else {
		log.Debugf("Client configuration initialized")
	}

	client.cfg = cfg

	if client.verifiedSets != nil {
		// Stop old verifications
		for _, verifiedSet := range client.verifiedSets {
			go verifiedSet.stop()
		}
	}

	// Set up new verified masquerade sets
	client.verifiedSets = make(map[string]*verifiedMasqueradeSet)

	for key, masqueradeSet := range cfg.MasqueradeSets {
		testServer := cfg.highestQosServer(key)
		if testServer != nil {
			client.verifiedSets[key] = newVerifiedMasqueradeSet(testServer, masqueradeSet)
		}
	}

	// Close existing servers
	if client.servers != nil {
		for _, server := range client.servers {
			server.close()
		}
	}

	// Configure servers
	client.servers = make([]*server, len(cfg.Servers))
	i := 0
	for _, serverInfo := range cfg.Servers {
		var enproxyConfig *enproxy.Config
		if enproxyConfigs != nil {
			enproxyConfig = enproxyConfigs[i]
		}
		client.servers[i] = serverInfo.buildServer(
			cfg.DumpHeaders,
			client.verifiedSets[serverInfo.MasqueradeSet],
			enproxyConfig)
		i = i + 1
	}

	// Calculate total server weights
	client.totalServerWeights = 0
	for _, server := range client.servers {
		client.totalServerWeights = client.totalServerWeights + server.info.Weight
	}
}
コード例 #14
0
ファイル: server.go プロジェクト: mijo-sjx/lantern
func (serverInfo *ServerInfo) dialerFor(masqueradeSource func() *Masquerade) func() (net.Conn, error) {
	dialTimeout := time.Duration(serverInfo.DialTimeoutMillis) * time.Millisecond
	if dialTimeout == 0 {
		dialTimeout = 20 * time.Second
	}

	// Note - we need to suppress the sending of the ServerName in the client
	// handshake to make host-spoofing work with Fastly.  If the client Hello
	// includes a server name, Fastly checks to make sure that this matches the
	// Host header in the HTTP request and if they don't match, it returns
	// a 400 Bad Request error.
	sendServerNameExtension := false

	return func() (net.Conn, error) {
		masquerade := masqueradeSource()
		cwt, err := tlsdialer.DialForTimings(
			&net.Dialer{
				Timeout: dialTimeout,
			},
			"tcp",
			serverInfo.addressForServer(masquerade),
			sendServerNameExtension,
			serverInfo.tlsConfig(masquerade))

		domain := ""
		if masquerade != nil {
			domain = masquerade.Domain
		}

		resultAddr := ""
		if err == nil {
			resultAddr = cwt.Conn.RemoteAddr().String()
		}

		if cwt.ResolutionTime > 0 {
			serverInfo.recordTiming("DNSLookup", cwt.ResolutionTime)
			if cwt.ResolutionTime > 1*time.Second {
				log.Debugf("DNS lookup for %s (%s) took %s", domain, resultAddr, cwt.ResolutionTime)
			}
		}

		if cwt.ConnectTime > 0 {
			serverInfo.recordTiming("TCPConnect", cwt.ConnectTime)
			if cwt.ConnectTime > 5*time.Second {
				log.Debugf("TCP connecting to %s (%s) took %s", domain, resultAddr, cwt.ConnectTime)
			}
		}

		if cwt.HandshakeTime > 0 {
			serverInfo.recordTiming("TLSHandshake", cwt.HandshakeTime)
			if cwt.HandshakeTime > 5*time.Second {
				log.Debugf("TLS handshake to %s (%s) took %s", domain, resultAddr, cwt.HandshakeTime)
			}
		}

		if err != nil && masquerade != nil {
			err = fmt.Errorf("Unable to dial masquerade %s: %s", masquerade.Domain, err)
		}
		return cwt.Conn, err
	}
}