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
|
||||
|
||||
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}
|
||||
}
|
||||
|
|
|
@ -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")
|
||||
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.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) {
|
||||
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")
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue