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:
parent
b7513a86fa
commit
73b0f90c16
|
@ -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
|
||||||
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,7 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import "fmt"
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
fmt.Println("What the fuck, over?", extraNonsense())
|
|
||||||
}
|
|
|
@ -1,8 +1,9 @@
|
||||||
package rfc1288
|
package rfc1288
|
||||||
|
|
||||||
import(
|
import(
|
||||||
|
"errors"
|
||||||
|
"strings"
|
||||||
"bytes"
|
"bytes"
|
||||||
// "fmt"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func is_unix_conventional(c byte) bool {
|
func is_unix_conventional(c byte) bool {
|
||||||
|
@ -37,19 +38,23 @@ type Rfc1288Request struct {
|
||||||
Host* string
|
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)
|
Buflen := len(Buffer)
|
||||||
|
|
||||||
if Buflen < 2 {
|
if Buflen < 2 {
|
||||||
return BadProtocol, nil
|
return errors.New("Protocol not recognized"), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if Buffer[0] != '/' || (Buffer[1] != 'W' && Buffer[1] != 'w') {
|
if Buffer[0] != '/' || (Buffer[1] != 'W' && Buffer[1] != 'w') {
|
||||||
return BadProtocol, nil
|
return errors.New("Protocol not recognized"), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(Buffer) == 2 {
|
if len(Buffer) == 2 {
|
||||||
return Ok, &Rfc1288Request{UserList, nil, nil}
|
return nil, &Rfc1288Request{UserList, nil, nil}
|
||||||
}
|
}
|
||||||
|
|
||||||
index := 2
|
index := 2
|
||||||
|
@ -58,7 +63,7 @@ func parse_rfc1288_request(Buffer string) (Rfc1288ErrorCode, *Rfc1288Request) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if Buflen == index {
|
if Buflen == index {
|
||||||
return Ok, &Rfc1288Request{Type: UserList, User: nil, Host: nil}
|
return nil, &Rfc1288Request{Type: UserList, User: nil, Host: nil}
|
||||||
}
|
}
|
||||||
|
|
||||||
user := bytes.NewBufferString("")
|
user := bytes.NewBufferString("")
|
||||||
|
@ -71,11 +76,11 @@ func parse_rfc1288_request(Buffer string) (Rfc1288ErrorCode, *Rfc1288Request) {
|
||||||
|
|
||||||
if index == Buflen || (index < Buflen && Buffer[index] == ' ') {
|
if index == Buflen || (index < Buflen && Buffer[index] == ' ') {
|
||||||
ruser := user.String()
|
ruser := user.String()
|
||||||
return Ok, &Rfc1288Request{Type: User, User: &ruser, Host: nil}
|
return nil, &Rfc1288Request{Type: User, User: &ruser, Host: nil}
|
||||||
}
|
}
|
||||||
|
|
||||||
if Buffer[index] != '@' {
|
if Buffer[index] != '@' {
|
||||||
return BadRequest, nil
|
return errors.New("Protocol does not meet specification"), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
index += 1
|
index += 1
|
||||||
|
@ -86,5 +91,5 @@ func parse_rfc1288_request(Buffer string) (Rfc1288ErrorCode, *Rfc1288Request) {
|
||||||
|
|
||||||
ruser := user.String()
|
ruser := user.String()
|
||||||
rhost := host.String()
|
rhost := host.String()
|
||||||
return Ok, &Rfc1288Request{Type: Remote, User: &ruser, Host: &rhost}
|
return nil, &Rfc1288Request{Type: Remote, User: &ruser, Host: &rhost}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,77 +1,105 @@
|
||||||
package rfc1288
|
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) {
|
func TestGood_List(t *testing.T) {
|
||||||
res, req := parse_rfc1288_request("/W")
|
res, req := parse_rfc1288_request("/W")
|
||||||
if res != Ok {
|
assert(t, res == nil, "Expected result to be nil.")
|
||||||
t.Error("Expected a good return")
|
assert(t, req.Type == UserList, "Expected type to be Userlist")
|
||||||
}
|
|
||||||
if req.Type != UserList {
|
|
||||||
t.Error("Expected UserList as a return type")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGood_ListWSpaces(t *testing.T) {
|
func TestGood_ListWSpaces(t *testing.T) {
|
||||||
res, req := parse_rfc1288_request("/W ");
|
res, req := parse_rfc1288_request("/W ");
|
||||||
if res != Ok {
|
assert(t, res == nil, "Expected result to be nil.")
|
||||||
t.Error("Expected a good return")
|
assert(t, req.Type == UserList, "Expected type to be Userlist")
|
||||||
}
|
|
||||||
if req.Type != UserList {
|
|
||||||
t.Error("Expected UserList as a return type")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBad_Start(t *testing.T) {
|
func TestBad_Start(t *testing.T) {
|
||||||
res, _ := parse_rfc1288_request("")
|
res, _ := parse_rfc1288_request("")
|
||||||
if res != BadProtocol {
|
assert(t, res != nil, "Expected result to be BadProtocol.")
|
||||||
t.Error("Expected BadProtocol")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBad_Start1(t *testing.T) {
|
func TestBad_Start1(t *testing.T) {
|
||||||
res, _ := parse_rfc1288_request("/")
|
res, _ := parse_rfc1288_request("/")
|
||||||
if res != BadProtocol {
|
assert(t, res != nil, "Expected result to be BadProtocol.")
|
||||||
t.Error("Expected BadProtocol")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBad_Start2(t *testing.T) {
|
func TestBad_Start2(t *testing.T) {
|
||||||
res, _ := parse_rfc1288_request("/X")
|
res, _ := parse_rfc1288_request("/X")
|
||||||
if res != BadProtocol {
|
assert(t, res != nil, "Expected result to be BadProtocol.")
|
||||||
t.Error("Expected BadProtocol, got", res)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGood_Name(t *testing.T) {
|
func TestGood_Name(t *testing.T) {
|
||||||
res, req := parse_rfc1288_request("/W foozle")
|
res, req := parse_rfc1288_request("/W foozle")
|
||||||
if res != Ok {
|
assert(t, res == nil, "Expected a good return")
|
||||||
t.Error("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.")
|
||||||
}
|
}
|
||||||
if req.Type != User {
|
|
||||||
t.Error("Expected User as a return type")
|
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.")
|
||||||
}
|
}
|
||||||
if *req.User != "foozle" {
|
|
||||||
t.Error("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) {
|
func TestGood_NameExtraSpace(t *testing.T) {
|
||||||
res, req := parse_rfc1288_request("/W foozle ")
|
res, req := parse_rfc1288_request("/W foozle ")
|
||||||
if res != Ok {
|
assert(t, res == nil, "Expected result to be nil.")
|
||||||
t.Error("Expected a good return")
|
assert(t, req.Type == User, "Expected type to be User")
|
||||||
}
|
assert(t, *req.User == "foozle", "User name returned did not match")
|
||||||
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.")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGood_NameWHost(t *testing.T) {
|
func TestGood_NameWHost(t *testing.T) {
|
||||||
res, req := parse_rfc1288_request("/W foozle@localhost")
|
res, req := parse_rfc1288_request("/W foozle@localhost")
|
||||||
if res != Ok {
|
if res != nil {
|
||||||
t.Error("Expected a good return")
|
t.Error("Expected a good return")
|
||||||
}
|
}
|
||||||
if req.Type != Remote {
|
if req.Type != Remote {
|
||||||
|
@ -87,7 +115,7 @@ func TestGood_NameWHost(t *testing.T) {
|
||||||
|
|
||||||
func TestGood_NameWHostAndSpaces(t *testing.T) {
|
func TestGood_NameWHostAndSpaces(t *testing.T) {
|
||||||
res, req := parse_rfc1288_request("/W foozle@localhost ")
|
res, req := parse_rfc1288_request("/W foozle@localhost ")
|
||||||
if res != Ok {
|
if res != nil {
|
||||||
t.Error("Expected a good return")
|
t.Error("Expected a good return")
|
||||||
}
|
}
|
||||||
if req.Type != Remote {
|
if req.Type != Remote {
|
||||||
|
@ -103,7 +131,7 @@ func TestGood_NameWHostAndSpaces(t *testing.T) {
|
||||||
|
|
||||||
func TestGood_NameWHostAndSpacesAndLowerW(t *testing.T) {
|
func TestGood_NameWHostAndSpacesAndLowerW(t *testing.T) {
|
||||||
res, req := parse_rfc1288_request("/w foozle@localhost ")
|
res, req := parse_rfc1288_request("/w foozle@localhost ")
|
||||||
if res != Ok {
|
if res != nil {
|
||||||
t.Error("Expected a good return")
|
t.Error("Expected a good return")
|
||||||
}
|
}
|
||||||
if req.Type != Remote {
|
if req.Type != Remote {
|
||||||
|
@ -119,7 +147,7 @@ func TestGood_NameWHostAndSpacesAndLowerW(t *testing.T) {
|
||||||
|
|
||||||
func TestBad_Name(t *testing.T) {
|
func TestBad_Name(t *testing.T) {
|
||||||
res, _ := parse_rfc1288_request("/W foozle.. ")
|
res, _ := parse_rfc1288_request("/W foozle.. ")
|
||||||
if res != BadRequest {
|
if res == nil {
|
||||||
t.Error("Expected BadRequest")
|
t.Error("Expected BadRequest")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue