COMPLETE: A relatively straightforward implementation of the Monologued server.

This is obviously a version 1.0, but it taught me a few things about Go's
networking and its batteries included philosophy.  I'll have a longer blog
post about it in a few days.  It still needs a configuration file and a
couple of other features found in traditional finger servers, but for now
this is a pretty good example.
This commit is contained in:
Elf M. Sternberg 2018-01-10 14:27:33 -08:00
parent b7513a86fa
commit 73b0f90c16
5 changed files with 206 additions and 60 deletions

43
dotplan/userpath.go Normal file
View File

@ -0,0 +1,43 @@
package dotplan
import (
"errors"
"os"
"os/user"
"io/ioutil"
"path"
)
func GetUserpath(username *string) (error, *string) {
User, err := user.Lookup(*username)
if err != nil {
return err, nil
}
return nil, &User.HomeDir
}
func GetUserplan(username *string) (error, *[]byte) {
err, Path := GetUserpath(username)
if err != nil {
return err, nil
}
PlanPath := path.Join(*Path, ".plan")
Plan, err := os.Stat(PlanPath)
if err != nil {
return err, nil
}
if Plan.IsDir() {
return errors.New("Not a readable file"), nil
}
Data, err := ioutil.ReadFile(PlanPath)
if err != nil {
return err, nil
}
return nil, &Data
}

77
main.go Normal file
View File

@ -0,0 +1,77 @@
package main
import (
"fmt"
"net"
"time"
"bufio"
"strconv"
"monologued/rfc1288"
"monologued/dotplan"
)
const PORT = 2003
func main() {
server, err := net.Listen("tcp", ":" + strconv.Itoa(PORT))
if server == nil {
if (err != nil) {
panic("couldn't start listening: " + err.Error())
}
panic("Couldn't start listening. Error undefined")
}
for {
client, err := server.Accept()
if client == nil {
fmt.Printf("Connection request failed: %s\n", err.Error())
continue
}
go Response(client)
}
}
func Response(socket net.Conn) {
defer func() {
socket.Close()
}()
timeout := time.Second * 15
socket.SetDeadline(time.Now().Add(timeout))
buffer := bufio.NewReader(socket)
line, _, err := buffer.ReadLine()
if err != nil {
socket.Write([]byte("400 Bad Request\r\n\r\n"))
return
}
err, Request := rfc1288.ParseRfc1288Request(string(line))
if err != nil {
socket.Write([]byte("400 Bad Request\r\n\r\n"))
return
}
if Request.Type == rfc1288.Remote {
socket.Write([]byte("403 Forbidden - This server does not support remote requests\r\n\r\n"))
return
}
if Request.Type == rfc1288.UserList {
socket.Write([]byte("403 Forbidden - This server does not support user lists\r\n\r\n"))
return
}
err, Data := dotplan.GetUserplan(Request.User)
if err != nil {
socket.Write([]byte(fmt.Sprintf("404 Not Found - No information for user '%s' found\r\n\r\n", *Request.User)))
return
}
socket.Write(*Data)
}

View File

@ -1,7 +0,0 @@
package main
import "fmt"
func main() {
fmt.Println("What the fuck, over?", extraNonsense())
}

View File

@ -1,8 +1,9 @@
package rfc1288
import(
"errors"
"strings"
"bytes"
// "fmt"
)
func is_unix_conventional(c byte) bool {
@ -37,19 +38,23 @@ type Rfc1288Request struct {
Host* string
}
func parse_rfc1288_request(Buffer string) (Rfc1288ErrorCode, *Rfc1288Request) {
func ParseRfc1288Request(Buffer string) (error, *Rfc1288Request) {
if pos := strings.IndexAny(Buffer, "\r\n"); pos > 0 {
Buffer = Buffer[:pos]
}
Buflen := len(Buffer)
if Buflen < 2 {
return BadProtocol, nil
return errors.New("Protocol not recognized"), nil
}
if Buffer[0] != '/' || (Buffer[1] != 'W' && Buffer[1] != 'w') {
return BadProtocol, nil
return errors.New("Protocol not recognized"), nil
}
if len(Buffer) == 2 {
return Ok, &Rfc1288Request{UserList, nil, nil}
return nil, &Rfc1288Request{UserList, nil, nil}
}
index := 2
@ -58,7 +63,7 @@ func parse_rfc1288_request(Buffer string) (Rfc1288ErrorCode, *Rfc1288Request) {
}
if Buflen == index {
return Ok, &Rfc1288Request{Type: UserList, User: nil, Host: nil}
return nil, &Rfc1288Request{Type: UserList, User: nil, Host: nil}
}
user := bytes.NewBufferString("")
@ -71,11 +76,11 @@ func parse_rfc1288_request(Buffer string) (Rfc1288ErrorCode, *Rfc1288Request) {
if index == Buflen || (index < Buflen && Buffer[index] == ' ') {
ruser := user.String()
return Ok, &Rfc1288Request{Type: User, User: &ruser, Host: nil}
return nil, &Rfc1288Request{Type: User, User: &ruser, Host: nil}
}
if Buffer[index] != '@' {
return BadRequest, nil
return errors.New("Protocol does not meet specification"), nil
}
index += 1
@ -86,5 +91,5 @@ func parse_rfc1288_request(Buffer string) (Rfc1288ErrorCode, *Rfc1288Request) {
ruser := user.String()
rhost := host.String()
return Ok, &Rfc1288Request{Type: Remote, User: &ruser, Host: &rhost}
return nil, &Rfc1288Request{Type: Remote, User: &ruser, Host: &rhost}
}

View File

@ -1,77 +1,105 @@
package rfc1288
import "testing"
import (
"fmt"
"path/filepath"
"runtime"
"reflect"
"testing"
)
// assert fails the test if the condition is false.
func assert(tb testing.TB, condition bool, msg string, v ...interface{}) {
if !condition {
_, file, line, _ := runtime.Caller(1)
fmt.Printf("%s:%d: "+msg+"\n\n", append([]interface{}{filepath.Base(file), line}, v...)...)
tb.FailNow()
}
}
// ok fails the test if an err is not nil.
func ok(tb testing.TB, err error) {
if err != nil {
_, file, line, _ := runtime.Caller(1)
fmt.Printf("%s:%d: unexpected error: %s\n\n", filepath.Base(file), line, err.Error())
tb.FailNow()
}
}
// equals fails the test if exp is not equal to act.
func equals(tb testing.TB, exp, act interface{}) {
if !reflect.DeepEqual(exp, act) {
_, file, line, _ := runtime.Caller(1)
fmt.Printf("%s:%d:\n\n\texp: %#v\n\n\tgot: %#v\n\n", filepath.Base(file), line, exp, act)
tb.FailNow()
}
}
func TestGood_List(t *testing.T) {
res, req := parse_rfc1288_request("/W")
if res != Ok {
t.Error("Expected a good return")
}
if req.Type != UserList {
t.Error("Expected UserList as a return type")
}
assert(t, res == nil, "Expected result to be nil.")
assert(t, req.Type == UserList, "Expected type to be Userlist")
}
func TestGood_ListWSpaces(t *testing.T) {
res, req := parse_rfc1288_request("/W ");
if res != Ok {
t.Error("Expected a good return")
}
if req.Type != UserList {
t.Error("Expected UserList as a return type")
}
assert(t, res == nil, "Expected result to be nil.")
assert(t, req.Type == UserList, "Expected type to be Userlist")
}
func TestBad_Start(t *testing.T) {
res, _ := parse_rfc1288_request("")
if res != BadProtocol {
t.Error("Expected BadProtocol")
}
assert(t, res != nil, "Expected result to be BadProtocol.")
}
func TestBad_Start1(t *testing.T) {
res, _ := parse_rfc1288_request("/")
if res != BadProtocol {
t.Error("Expected BadProtocol")
}
assert(t, res != nil, "Expected result to be BadProtocol.")
}
func TestBad_Start2(t *testing.T) {
res, _ := parse_rfc1288_request("/X")
if res != BadProtocol {
t.Error("Expected BadProtocol, got", res)
}
assert(t, res != nil, "Expected result to be BadProtocol.")
}
func TestGood_Name(t *testing.T) {
res, req := parse_rfc1288_request("/W foozle")
if res != Ok {
t.Error("Expected a good return")
}
if req.Type != User {
t.Error("Expected User as a return type")
}
if *req.User != "foozle" {
t.Error("The user name did not match passed in value.")
}
assert(t, res == nil, "Expected a good return")
assert(t, req.Type == User, "Expected User as a return type")
assert(t, *req.User == "foozle", "The user name did not match passed in value.")
}
func TestGood_NameLf(t *testing.T) {
res, req := parse_rfc1288_request("/W foozle\n")
assert(t, res == nil, "Expected a good return")
assert(t, req.Type == User, "Expected User as a return type")
assert(t, *req.User == "foozle", "The user name did not match passed in value.")
}
func TestGood_NameCr(t *testing.T) {
res, req := parse_rfc1288_request("/W foozle\r")
assert(t, res == nil, "Expected a good return")
assert(t, req.Type == User, "Expected User as a return type")
assert(t, *req.User == "foozle", "The user name did not match passed in value.")
}
func TestGood_NameCrLf(t *testing.T) {
res, req := parse_rfc1288_request("/W foozle\r\n")
assert(t, res == nil, "Expected a good return")
assert(t, req.Type == User, "Expected User as a return type")
assert(t, *req.User == "foozle", "The user name did not match passed in value.")
}
func TestGood_NameExtraSpace(t *testing.T) {
res, req := parse_rfc1288_request("/W foozle ")
if res != Ok {
t.Error("Expected a good return")
}
if req.Type != User {
t.Error("Expected User as a return type")
}
if *req.User != "foozle" {
t.Error("The user name did not match passed in value.")
}
assert(t, res == nil, "Expected result to be nil.")
assert(t, req.Type == User, "Expected type to be User")
assert(t, *req.User == "foozle", "User name returned did not match")
}
func TestGood_NameWHost(t *testing.T) {
res, req := parse_rfc1288_request("/W foozle@localhost")
if res != Ok {
if res != nil {
t.Error("Expected a good return")
}
if req.Type != Remote {
@ -87,7 +115,7 @@ func TestGood_NameWHost(t *testing.T) {
func TestGood_NameWHostAndSpaces(t *testing.T) {
res, req := parse_rfc1288_request("/W foozle@localhost ")
if res != Ok {
if res != nil {
t.Error("Expected a good return")
}
if req.Type != Remote {
@ -103,7 +131,7 @@ func TestGood_NameWHostAndSpaces(t *testing.T) {
func TestGood_NameWHostAndSpacesAndLowerW(t *testing.T) {
res, req := parse_rfc1288_request("/w foozle@localhost ")
if res != Ok {
if res != nil {
t.Error("Expected a good return")
}
if req.Type != Remote {
@ -119,7 +147,7 @@ func TestGood_NameWHostAndSpacesAndLowerW(t *testing.T) {
func TestBad_Name(t *testing.T) {
res, _ := parse_rfc1288_request("/W foozle.. ")
if res != BadRequest {
if res == nil {
t.Error("Expected BadRequest")
}
}