package main import ( "context" "crypto/rand" "encoding/base64" "flag" "fmt" "io" "log" "net/http" "net/http/cookiejar" "net/url" "strings" ) var ( flagAddress string flagUsername string flagPassword string ) const ( sessionKey = "SESSION_ID_VIGOR" ) func makeAddress(page string) string { u, err := url.Parse(flagAddress) if err != nil { panic(fmt.Sprintf("Invalid address (%q): %v", flagAddress, err)) } u = u.JoinPath(page) return u.String() } type client struct { random string jar *cookiejar.Jar } func (c *client) doRequest(ctx context.Context, body io.Reader, method, url, referer string) (*http.Response, error) { req, err := http.NewRequestWithContext(ctx, method, url, body) if err != nil { return nil, err } req.Header.Add("Content-Type", "application/x-www-form-urlencoded") req.Header.Add("Referer", referer) req.Header.Add("Origin", makeAddress("")) client := &http.Client{ Jar: c.jar, } return client.Do(req) } func (c *client) getSession(ctx context.Context) error { // Get session. c.random = rand.Text()[:15] form := url.Values{} form.Add("aa", base64.StdEncoding.EncodeToString([]byte(flagUsername))) form.Add("ab", base64.StdEncoding.EncodeToString([]byte(flagPassword))) form.Add("sFormAuthStr", c.random) res, err := c.doRequest(ctx, strings.NewReader(form.Encode()), "POST", makeAddress("cgi-bin/wlogin.cgi"), makeAddress("weblogin.htm")) if err != nil { return err } defer res.Body.Close() body, _ := io.ReadAll(res.Body) if strings.Contains(string(body), "Authorization Error") { return fmt.Errorf("authorization error on wlogin.cgi") } // Check that auth works. res, err = c.doRequest(ctx, nil, "GET", makeAddress("cgi-bin/cgidashboard.cgi")+fmt.Sprintf("?sFormAuthStr=%s&fid=1", c.random), makeAddress("")) if err != nil { return err } defer res.Body.Close() body, _ = io.ReadAll(res.Body) if strings.Contains(string(body), "Authorization Error") { return fmt.Errorf("authorization error on cgidashboard.cgi") } return nil } func (c *client) reboot(ctx context.Context) error { form := url.Values{} form.Add("sReboot", "Current") form.Add("submitbnt", "Reboot Now") form.Add("nSch1", "0") form.Add("nSch2", "0") form.Add("nSch3", "0") form.Add("nSch4", "0") form.Add("fid", "0") form.Add("webchange", "0") form.Add("sFormAuthStr", c.random) res, err := c.doRequest(ctx, strings.NewReader(form.Encode()), "POST", makeAddress("cgi-bin/reboot.cgi"), makeAddress("doc/rebootsys.sht")) if err != nil { return err } defer res.Body.Close() body, _ := io.ReadAll(res.Body) if strings.Contains(string(body), "Authorization Error") { return fmt.Errorf("authorization error on reboot.cgi") } return nil } func main() { flag.StringVar(&flagAddress, "address", "http://192.168.1.1/", "Address of Draytek admin interface") flag.StringVar(&flagUsername, "username", "admin", "Username for authenticating to the Draytek admin interface") flag.StringVar(&flagPassword, "password", "", "Password for authenticating to the Draytek admin interface") flag.Parse() if flagPassword == "" { log.Fatalf("-password must be specified") } ctx := context.Background() jar, _ := cookiejar.New(nil) c := client{ jar: jar, } err := c.getSession(ctx) if err != nil { log.Fatalf("Could not get session: %v", err) } if err := c.reboot(ctx); err != nil { log.Fatalf("Reboot failed: %v", err) } log.Printf("Rebooted!") }