瀏覽代碼

Initial setup of go door.

Steve Thielemann 3 年之前
當前提交
7953de7b80
共有 7 個文件被更改,包括 645 次插入0 次删除
  1. 10 0
      Makefile
  2. 425 0
      door/door.go
  3. 3 0
      door/go.mod
  4. 173 0
      door32.c
  5. 4 0
      run_4044
  6. 7 0
      testdoor/go.mod
  7. 23 0
      testdoor/testdoor.go

+ 10 - 0
Makefile

@@ -0,0 +1,10 @@
+
+all: door32 testdoor/testdoor
+
+
+door32: door32.c
+	gcc -o door32 door32.c
+
+testdoor/testdoor: testdoor/testdoor.go
+	cd testdoor; go build testdoor.go
+

+ 425 - 0
door/door.go

@@ -0,0 +1,425 @@
+package door
+
+import (
+	"bufio"
+	"flag"
+	"fmt"
+	"os"
+	"strconv"
+	"strings"
+	"syscall"
+	"time"
+)
+
+/*
+door32.sys:
+
+0                            Line 1 : Comm type (0=local, 1=serial, 2=telnet)
+0                            Line 2 : Comm or socket handle
+38400                        Line 3 : Baud rate
+Mystic 1.07                  Line 4 : BBSID (software name and version)
+1                            Line 5 : User record position (1-based)
+James Coyle                  Line 6 : User's real name
+g00r00                       Line 7 : User's handle/alias
+255                          Line 8 : User's security level
+58                           Line 9 : User's time left (in minutes)
+1                            Line 10: Emulation *See Below
+1                            Line 11: Current node number
+*/
+
+func Color(arg ...int) string {
+	var result string = "\x1b["
+	for i := range arg {
+		result += fmt.Sprintf("%d;", arg[i])
+	}
+	result += "m"
+	return result
+}
+
+func ColorText(color string) string {
+	// split on spaces, uppercase, match first 3 letter
+	var result []int
+	var bg bool
+
+	result = append(result, 0)
+
+	parts := strings.Fields(strings.ToUpper(color))
+	for _, part := range parts {
+		switch part {
+		case "BLACK", "BLA":
+			if bg {
+				result = append(result, 40)
+			} else {
+				result = append(result, 30)
+			}
+
+		case "RED":
+			if bg {
+				result = append(result, 41)
+			} else {
+				result = append(result, 31)
+			}
+
+		case "GREEN", "GRE":
+			if bg {
+				result = append(result, 42)
+			} else {
+				result = append(result, 32)
+			}
+
+		case "BROWN", "BRO":
+			if bg {
+				result = append(result, 43)
+			} else {
+				result = append(result, 33)
+			}
+
+		case "YELLOW", "YEL":
+			if bg {
+				result = append(result, 43)
+			} else {
+				result = append(result, 33)
+			}
+
+		case "BLUE", "BLU":
+			if bg {
+				result = append(result, 44)
+			} else {
+				result = append(result, 34)
+			}
+
+		case "MAGENTA", "MAG":
+			if bg {
+				result = append(result, 45)
+			} else {
+				result = append(result, 35)
+			}
+
+		case "CYAN", "CYA":
+			if bg {
+				result = append(result, 46)
+			} else {
+				result = append(result, 36)
+			}
+
+		case "WHITE", "WHI":
+			if bg {
+				result = append(result, 47)
+			} else {
+				result = append(result, 37)
+			}
+
+		case "BOLD", "BOL", "BRIGHT", "BRI":
+			result = append(result, 1)
+
+		case "ON":
+			bg = true
+
+		case "BLINK", "BLI":
+			result = append(result, 5)
+
+		case "INVERT", "INVERSE", "INV":
+			result = append(result, 7)
+		default:
+			fmt.Println("ColorText Unknown:", part)
+		}
+
+	}
+	// fmt.Println("ColorText:", result)
+	return Color(result...)
+}
+
+var Reset string = Color(0)
+var READFD int
+var WRITEFD int
+
+type DropfileConfig struct {
+	comm_type      int
+	comm_handle    int
+	baudrate       int
+	BBSID          string
+	user_number    int
+	real_name      string
+	handle         string
+	security_level int
+	time_left      int
+	emulation      int
+	node_number    int
+}
+
+type Door struct {
+	config  DropfileConfig
+	READFD  int
+	WRITEFD int
+}
+
+func (d *Door) ReadDropfile(filename string) {
+	file, err := os.Open(filename)
+	if err != nil {
+		fmt.Printf("Open(%s): %s\n", filename, err)
+		os.Exit(2)
+	}
+
+	defer file.Close()
+
+	var lines []string
+	// read line by line
+
+	// The scanner handles DOS and linux file endings.
+
+	scanner := bufio.NewScanner(file)
+	for scanner.Scan() {
+		line := scanner.Text()
+		lines = append(lines, line)
+		// fmt.Printf("[%s]\n", line)
+	}
+
+	d.config.comm_type, err = strconv.Atoi(lines[0])
+	d.config.comm_handle, err = strconv.Atoi(lines[1])
+	d.config.baudrate, err = strconv.Atoi(lines[2])
+	d.config.BBSID = lines[3]
+	d.config.user_number, err = strconv.Atoi(lines[4])
+	d.config.real_name = lines[5]
+	d.config.handle = lines[6]
+	d.config.security_level, err = strconv.Atoi(lines[7])
+	d.config.time_left, err = strconv.Atoi(lines[8])
+	d.config.emulation, err = strconv.Atoi(lines[9])
+	d.config.node_number, err = strconv.Atoi(lines[10])
+	d.READFD = d.config.comm_handle
+	//if d.READFD == 0 {
+	//	d.WRITEFD = 1
+	//} else {
+	d.WRITEFD = d.config.comm_handle
+	//}
+}
+
+func (d *Door) HasKey() bool {
+	var fdsetRead = syscall.FdSet{}
+	clearAll(&fdsetRead)
+	set(&fdsetRead, d.READFD)
+	timeout := syscall.Timeval{Sec: 0, Usec: 1}
+	v, _ := syscall.Select(d.READFD+1, &fdsetRead, nil, nil, &timeout)
+	if v == -1 {
+		return false
+	}
+	if v == 0 {
+		return false
+	}
+	return true
+}
+
+func (d *Door) detect() {
+	// if d.config.comm_handle == 0 {
+	// d.Write("\377\375\042\377\373\001") // fix telnet client
+	// }
+	d.Write("\x1b[0;30;40m\x1b[2J\x1b[H") // black on black, clrscr, go home
+	d.Write("\x03\x04\x1b[6n")            // hearts and diamonds does CP437 work?
+
+	d.Write("\n\r" + "\u2615\x1b[6n")
+	d.Write("\x1b[999C\x1b[999B\x1b[6n" + Reset + "\x1b[2J\x1b[H") // goto end of screen + cursor pos
+	// time.Sleep(50 * time.Millisecond)
+	time.Sleep(250 * time.Millisecond)
+	// read everything
+	// telnet term isn't in RAW mode, so keys are buffer until <CR>
+
+	if true { // d.HasKey() {
+		buffer := make([]byte, 100)
+		r, err := syscall.Read(d.READFD, buffer)
+		results := string(buffer[:r])
+		results = strings.Replace(results, "\x1b", "^", -1)
+		fmt.Println("DETECT:", r, err, results)
+	} else {
+		// local telnet echos the reply  :()
+		fmt.Println("DETECT: Nothing received.")
+	}
+
+}
+
+func (d *Door) Init() {
+	var dropfile string
+
+	flag.StringVar(&dropfile, "d", "", "Path to dropfile")
+	flag.Parse()
+	if len(dropfile) == 0 {
+		flag.PrintDefaults()
+		os.Exit(2)
+	}
+	fmt.Printf("Loading: %s\n", dropfile)
+
+	d.ReadDropfile(dropfile)
+
+	fmt.Printf("BBS %s, User %s / Handle %s / File %d\n", d.config.BBSID, d.config.real_name, d.config.handle, d.config.comm_handle)
+	// putting the linux terminal into raw mode ...
+	// requires golang.org/x/sys/unix  unix.Ioctlgetermios, etc.
+	d.detect()
+}
+
+func (d *Door) Write(output string) {
+	buffer := []byte(output)
+	n, err := syscall.Write(d.WRITEFD, buffer)
+	if err != nil {
+		fmt.Println("Write error/HANGUP?", n)
+	}
+	// No, this isn't it.  The # of bytes in buffer == bytes written.
+	if n != len(buffer) {
+		fmt.Printf("Write fail: %d != %d\n", len(buffer), n)
+	}
+}
+
+/*
+func write(output string, config *DropfileConfig) {
+	buffer := []byte(output)
+	n, err := syscall.Write(config.comm_handle, buffer)
+	if err != nil {
+		fmt.Println("Write error/HANGUP?", n)
+	}
+}
+*/
+
+// from: https://github.com/yubo/dea_ng
+// https://github.com/yubo/dea_ng/blob/master/go/src/directoryserver/streaming.go
+
+func set(fdSetPtr *syscall.FdSet, fd int) {
+	(*fdSetPtr).Bits[fd/64] |= 1 << uint64(fd%64)
+}
+
+func isSet(fdSetPtr *syscall.FdSet, fd int) bool {
+	return ((*fdSetPtr).Bits[fd/64] & (1 << uint64(fd%64))) != 0
+}
+
+func clearAll(fdSetPtr *syscall.FdSet) {
+	for index, _ := range (*fdSetPtr).Bits {
+		(*fdSetPtr).Bits[index] = 0
+	}
+}
+
+func (d *Door) SleepKey(sleep int64) int {
+	// var fdsetRead, fdsetWrite, fdsete syscall.FdSet
+	var fdsetRead syscall.FdSet
+	// fdsetWrite := syscall.FdSet
+	clearAll(&fdsetRead)
+	// clearAll(&fdsetWrite)
+	// clearAll(&fdsete)
+	set(&fdsetRead, d.READFD)
+	// timeout := syscall.Timeval{Sec: 0, Usec: 100}
+	timeout := syscall.Timeval{Sec: sleep, Usec: 0}
+	v, err := syscall.Select(d.READFD+1, &fdsetRead, nil, nil, &timeout)
+	if v == -1 {
+		fmt.Println("-1 : ", err)
+		// hangup ?!
+		return -2
+	}
+	if v == 0 {
+		// timeout
+		return -1
+	}
+	// var buffer []byte   -- 0 byte buffer.  doh!
+	buffer := make([]byte, 1)
+	r, err := syscall.Read(d.READFD, buffer)
+	if r != 1 {
+		fmt.Printf("Read said ready, but didn't read a character %d %v.", r, err)
+		// hangup
+		return -2
+	}
+	return int(buffer[0])
+}
+
+func (d *Door) Getch() int {
+	// var fdsetRead, fdsetWrite, fdsete syscall.FdSet
+	var fdsetRead syscall.FdSet
+	// fdsetWrite := syscall.FdSet
+	clearAll(&fdsetRead)
+	// clearAll(&fdsetWrite)
+	// clearAll(&fdsete)
+	set(&fdsetRead, d.READFD)
+	// timeout := syscall.Timeval{Sec: 0, Usec: 100}
+	timeout := syscall.Timeval{Sec: 120, Usec: 0}
+	v, err := syscall.Select(d.READFD+1, &fdsetRead, nil, nil, &timeout)
+	if v == -1 {
+		fmt.Println("-1 : ", err)
+		// hangup ?!
+		return -2
+	}
+	if v == 0 {
+		// timeout
+		return -1
+	}
+	// var buffer []byte   -- 0 byte buffer.  doh!
+	buffer := make([]byte, 1)
+	r, err := syscall.Read(d.READFD, buffer)
+	if r != 1 {
+		fmt.Printf("Read said ready, but didn't read a character %d %v.", r, err)
+		// hangup
+		return -2
+	}
+	return int(buffer[0])
+}
+
+func sleep_key(config *DropfileConfig, secs int) int {
+	// var fdsetRead, fdsetWrite, fdsete syscall.FdSet
+	var fdsetRead = syscall.FdSet{}
+	// fdsetWrite := syscall.FdSet
+	clearAll(&fdsetRead)
+	// clearAll(&fdsetWrite)
+	// clearAll(&fdsete)
+	set(&fdsetRead, config.comm_handle)
+	timeout := syscall.Timeval{Sec: int64(secs), Usec: 0}
+	// v, err := syscall.Select(config.comm_handle+1, &fdsetRead, &fdsetWrite, &fdsete, &timeout)
+	v, err := syscall.Select(config.comm_handle+1, &fdsetRead, nil, nil, &timeout)
+	fmt.Println("v:", v, "err:", err)
+	if v == -1 {
+		fmt.Println("-1 : ", err)
+		// hangup ?!
+		return -2
+	}
+	if v == 0 {
+		// timeout
+		return -1
+	}
+	// var buffer []byte
+	buffer := make([]byte, 1)
+	// var buffer [1]byte
+
+	r, err := syscall.Read(config.comm_handle, buffer)
+	if r != 1 {
+		fmt.Printf("Read said ready, but didn't read a character %d %v ?\n", r, err)
+		// hangup
+		return -2
+	}
+	return int(buffer[0])
+}
+
+/*
+func main() {
+	fmt.Println("doorgo")
+	var dropfile string
+
+	flag.StringVar(&dropfile, "dropfile", "", "Dropfile to use")
+	flag.Parse()
+
+	if len(dropfile) == 0 {
+		flag.PrintDefaults()
+		os.Exit(2)
+	}
+	fmt.Printf("Loading: %s\n", dropfile)
+
+	var config DropfileConfig
+	read_dropfile(dropfile, &config)
+
+	fmt.Printf("BBS %s, User %s / Handle %s\n", config.BBSID, config.real_name, config.handle)
+	message := "Welcome BBS User!\n\r"
+	// buffer := []byte(message)
+	// n, err := syscall.Write(config.comm_handle, buffer)
+	write(message, &config)
+
+	write("Press a key...", &config)
+	key := sleep_key(&config, 20)
+	write("\n\r", &config)
+	message = fmt.Sprintf("Key %d / %x\n\r", key, key)
+	write(message, &config)
+
+	write("\n\rReturning to BBS.\n\r", &config)
+	fmt.Println("Done.")
+	// fmt.Println(n, err)
+}
+*/

+ 3 - 0
door/go.mod

@@ -0,0 +1,3 @@
+module red-green/door
+
+go 1.17

+ 173 - 0
door32.c

@@ -0,0 +1,173 @@
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <netdb.h>
+#include <netinet/in.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include <string.h>
+
+void doprocessing (int sock);
+char * cmds[10];
+
+int main( int argc, char *argv[] ) {
+   int sockfd, newsockfd, portno, clilen;
+   char buffer[256];
+   struct sockaddr_in serv_addr, cli_addr;
+   int n, pid;
+   
+   /* First call to socket() function */
+   sockfd = socket(AF_INET, SOCK_STREAM, 0);
+   
+   if (sockfd < 0) {
+      perror("ERROR opening socket");
+      exit(1);
+   }
+
+   if (argc != 3) {
+	printf("I need a listening port number, and command/door to call.\n");
+        exit(2);
+   }
+   
+   int listen_port = atoi(argv[1]);
+   char * command = strdup(argv[2]);
+   char * cp;
+   int cmd_count;
+   cp = command;
+   cmds[cmd_count] = cp;
+   cmd_count++;
+   while (*cp != 0) {
+      if (*cp == ' ') {
+	*cp = 0;
+	cp++;
+	cmds[cmd_count] = cp;
+        cmd_count++;
+	continue;
+      }
+      cp++;
+   }
+   cmds[cmd_count] = (char *)NULL;
+
+   if (listen_port ==0) {
+      printf("I need a listening port number as a parameter.\n");
+      exit(2);
+   }
+
+   /* Initialize socket structure */
+   bzero((char *) &serv_addr, sizeof(serv_addr));
+   portno = listen_port;
+   
+   serv_addr.sin_family = AF_INET;
+   serv_addr.sin_addr.s_addr = INADDR_ANY;
+   serv_addr.sin_port = htons(portno);
+
+   int flag = 1;  
+   if (-1 == setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(flag))) {  
+      perror("setsockopt fail");  
+   }  
+
+   /* Now bind the host address using bind() call.*/
+   if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) {
+      perror("ERROR on binding");
+      exit(1);
+   }
+   
+   /* Now start listening for the clients, here
+      * process will go in sleep mode and will wait
+      * for the incoming connection
+   */
+   
+   listen(sockfd,5);
+   clilen = sizeof(cli_addr);
+   
+   while (1) {
+      newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen);
+		
+      if (newsockfd < 0) {
+         perror("ERROR on accept");
+         exit(1);
+      }
+      
+      /* Create child process */
+      pid = fork();
+		
+      if (pid < 0) {
+         perror("ERROR on fork");
+         exit(1);
+      }
+      
+      if (pid == 0) {
+         /* This is the client process */
+         close(sockfd);
+         doprocessing(newsockfd);
+         exit(0);
+      }
+      else {
+         close(newsockfd);
+      }
+		
+   } /* end of while */
+   free((void*)command);
+}
+
+void doprocessing (int sock) {
+   int n;
+   char buffer[256];
+   // raw mode
+   struct termios tio_raw;
+   tcgetattr(sock, &tio_raw);
+   cfmakeraw(&tio_raw);
+   tcsetattr(sock, TCSANOW, &tio_raw);
+   // telnet client into character mode
+   // sprintf(buffer, "\377\375\042\377\373\001Welcome socket %d\n\r", sock);
+   sprintf(buffer, "Welcome socket %d\n\r", sock);
+   write(sock, buffer, strlen(buffer) );
+
+   // can I read the buffer until empty, maybe?
+
+   FILE * fp;
+
+   fp = fopen("door32.sys", "w");
+   if ( fp == NULL) {
+      return;
+   }
+
+   fprintf(fp, "%d\n", 2);
+   fprintf(fp, "%d\n", sock);
+   fprintf(fp, "38400\n");
+   fprintf(fp, "Fake Door32 BBS\n");
+   fprintf(fp, "1\n");
+   fprintf(fp, "%s\n%s\n", "Bugz Laundry", "Bugz");
+   fprintf(fp, "%d\n", 100);
+   fprintf(fp, "%d\n", 120);
+   fprintf(fp, "1\n1\n");
+   fclose(fp);
+
+   // This resets the termios ??  
+   // execl("./testdoor", "testdoor", "-d", "door32.sys", (const char *)NULL);
+   execv(cmds[0], cmds);
+
+   // step1:  write out door32.sys file
+   // exec / replace this with our door
+
+   /*
+   bzero(buffer,256);
+   
+   n = read(sock,buffer,255);
+   
+   if (n < 0) {
+      perror("ERROR reading from socket");
+      exit(1);
+   }
+   
+   printf("Here is the message: %s\n",buffer);
+   n = write(sock,"I got your message",18);
+   
+   if (n < 0) {
+      perror("ERROR writing to socket");
+      exit(1);
+   }
+   */
+
+}

+ 4 - 0
run_4044

@@ -0,0 +1,4 @@
+#!/bin/bash
+
+./door32 4044 "testdoor/testdoor -d door32.sys"
+

+ 7 - 0
testdoor/go.mod

@@ -0,0 +1,7 @@
+module red-green/testdoor
+
+go 1.17
+
+replace red-green/door => ../door
+
+require red-green/door v0.0.0-00010101000000-000000000000

+ 23 - 0
testdoor/testdoor.go

@@ -0,0 +1,23 @@
+package main
+
+import (
+	"fmt"
+	"red-green/door"
+)
+
+func main() {
+	fmt.Println("Starting testdoor.go")
+	d := door.Door{}
+
+	d.Init()
+	reset := door.Reset
+	bold := door.Color(1, 37, 40)
+	bolder := door.ColorText("BLI BOLD YEL ON BLUE")
+	d.Write("Welcome to " + bolder + "door32.sys" + reset + "\n\r...\n\r")
+	key := d.Getch()
+	message := fmt.Sprintf("Key %s%d / %x%s\n\r", bold, key, key, reset)
+	d.Write(message)
+	d.Write("Returning you to the BBS...\n\r")
+	d.SleepKey(3)
+	fmt.Println("Ending testdoor.go")
+}