Example #1
0
// ProxyFor will take a URL, run it through the PAC logic and produce a PAC result string
func (p *PacSandbox) ProxyFor(u string) (string, error) {
	parsedURL := earl.Parse(u)

	key := fmt.Sprintf("%s-%s-%s-result", parsedURL.Scheme, parsedURL.Host, parsedURL.Port)
	if val, ok := p.resultCache.Get(key); ok {
		log.WithFields(log.Fields{"key": key}).Debug("PacSandbox result cache hit")
		return val, nil
	}

	js := fmt.Sprintf(
		"FindProxyForURL(%#v, %#v);",
		u,
		parsedURL.Host,
	)

	vm := p.vm.Copy()
	result, err := p.ottoRetString(
		vm.Run(js),
	)

	if err == nil {
		p.resultCache.Set(key, result)
	}

	log.WithFields(log.Fields{"result": result, "url": u}).Debug("PAC result")

	return result, err
}
Example #2
0
// Run is the entry point for pacyak. It will initialize pacyak and start listening.
func Run(opts *PacYakOpts) {

	log.SetLevel(opts.LogLevel)
	reader := readly.New()

	// We need to explicitly set HTTP client to prevent it trying to use ENV vars for proxy
	// pacyak listen addr is expected to be set as HTTP_PROXY / HTTPS_PROXY but it isn't started yet!
	// This level of control also means a lib like hashicorp/go-getter is not suitable :(
	reader.Client = &http.Client{
		Transport: &http.Transport{
			Proxy: func(req *http.Request) (*url.URL, error) {
				if opts.PacProxy != "" {
					return url.Parse(opts.PacProxy)
				}

				return nil, nil
			},
			DialContext: (&net.Dialer{
				Timeout:   5 * time.Second,
				KeepAlive: 5 * time.Second,
			}).DialContext,
			IdleConnTimeout: 5 * time.Second,
		},
	}

	app := &PacYakApplication{
		opts:         opts,
		pacFile:      earl.Parse(opts.PacFile),
		factory:      proxyfactory.New(),
		sandboxIndex: DIRECT_SANDBOX,
		sandboxes:    []pacInterpreter{&directPac{}, &directPac{}},
		listenAddr:   opts.ListenAddr,
		Reader:       reader,
	}

	go app.monitorPingAvailability()
	go app.monitorNetworkInterfaces()

	// FIXME - graceful handler; server 502 on error and keep going
	log.Fatal(http.ListenAndServe(app.opts.ListenAddr, app))
}
Example #3
0
func main() {
	app := cli.NewApp()
	app.Name = "pacyak"
	app.Version = "1.0"

	cli.AppHelpTemplate = `{{.Name}} version {{.Version}} - For the unfortunate souls stuck behind corporate proxies

{{.HelpName}} [options] <pac location>

OPTIONS:
   {{range .VisibleFlags}}{{.}}
   {{end}}
`

	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name:  "listen",
			Usage: "Pacyak will listen for requests to this address",
			Value: "127.0.0.1:8080",
		},
		cli.StringFlag{
			Name:  "ping-host",
			Usage: "Host only accessible from within your proxy. Required if PAC location is a file. (default: Host of PAC location)",
		},
		cli.StringFlag{
			Name:  "pac-proxy",
			Usage: "Proxy for pac file. (Only necessary if your PAC location requires a proxy to be set)",
		},
		cli.StringFlag{
			Name:  "log-level",
			Usage: "Log level (debug, info, warn, error)",
			Value: "info",
		},
	}

	app.Action = func(c *cli.Context) error {
		opts := &PacYakOpts{}

		tmp, err := logrus.ParseLevel(c.String("log-level"))
		if err != nil {
			return cli.NewExitError(fmt.Sprintf("Invalid log level '%s'. Valid levels are: debug, info, warn, error", tmp), 1)
		}
		opts.LogLevel = tmp

		if c.NArg() < 1 {
			cli.ShowAppHelp(c)
			os.Exit(0)
		}

		opts.PacFile = c.Args().Get(0)

		url := earl.Parse(opts.PacFile)
		if url.Scheme == "" || url.Scheme == "file" {
			if _, err := os.Stat(opts.PacFile); os.IsNotExist(err) {
				return cli.NewExitError("PAC location is not a valid URL and file does not exist. If it is a URL, please specify a protocol (e.g. http://)", 1)
			}
		}

		opts.PingCheckHost = earl.Parse(c.String("ping-host")).Host
		if c.String("ping-host") == "" {
			url = earl.Parse(opts.PacFile)
			if url.Scheme == "" || url.Scheme == "file" {
				return cli.NewExitError("--ping-host is required if PAC location is not a URL", 1)
			}
			opts.PingCheckHost = url.Host
		}

		opts.PacProxy = c.String("pac-proxy")
		opts.ListenAddr = c.String("listen")

		Run(opts)
		return nil
	}

	app.Run(os.Args)
}