[PD1] Structured client.

Still need to create crypto object and use it to encrypt messages
Need to create TLS still
This commit is contained in:
Afonso Franco 2024-04-17 15:44:38 +01:00
parent 278b8e1a73
commit cdaae8fb7e
Signed by: afonso
SSH key fingerprint: SHA256:aiLbdlPwXKJS5wMnghdtod0SPy8imZjlVvCyUX9DJNk
9 changed files with 252 additions and 110 deletions

View file

@ -1,7 +1,60 @@
package client package client
import "fmt" import (
"PD1/internal/protocol"
"PD1/internal/utils/networking"
"flag"
"fmt"
)
func Run() { func Run() {
fmt.Println("Client is running...") var userFile string
flag.StringVar(&userFile, "user", "userdata.p12", "Specify user data file")
flag.Parse()
if flag.NArg() == 0 {
panic("No command provided. Use 'help' for instructions.")
}
command := flag.Arg(0)
switch command {
case "send":
if flag.NArg() < 3 {
panic("Insufficient arguments for 'send' command. Usage: send <UID> <SUBJECT>")
}
uid := flag.Arg(1)
subject := flag.Arg(2)
// Read message content from stdin
messageContent := readMessageContent()
cl := networking.NewClient[protocol.Packet]()
defer cl.Connection.Conn.Close()
// TODO: cipherSubject := CHAMAR CRYPTO
// TODO: cipherBody := CHAMAR CRYPTO
submitMessage(cl,uid,cipherSubject,cipherBody)
case "askqueue":
cl = networking.NewClient[protocol.Packet]()
defer cl.Connection.Conn.Close()
case "getmsg":
if flag.NArg() < 2 {
panic("Insufficient arguments for 'getmsg' command. Usage: getmsg <NUM>")
}
num := flag.Arg(1)
cl = networking.NewClient[protocol.Packet]()
defer cl.Connection.Conn.Close()
case "help":
showHelp()
default:
fmt.Println("Invalid command. Use 'help' for instructions.")
}
}
func submitMessage(cl networking.Client[protocol.Packet],uid string,subject []byte,body []byte) {
pack := protocol.NewSubmitMessage(uid, subject, body)
cl.Connection.Send(pack)
} }

View file

@ -1 +1,23 @@
package client package client
import (
"bufio"
"fmt"
"os"
)
func readMessageContent() string {
fmt.Println("Enter message content (limited to 1000 bytes):")
scanner := bufio.NewScanner(os.Stdin)
scanner.Scan()
return scanner.Text()
}
func showHelp() {
fmt.Println("Comandos da aplicação cliente:")
fmt.Println("-user <FNAME>: Especifica o ficheiro com dados do utilizador. Por omissão, será assumido que esse ficheiro é userdata.p12.")
fmt.Println("send <UID> <SUBJECT>: Envia uma mensagem com assunto <SUBJECT> destinada ao utilizador com identificador <UID>. O conteúdo da mensagem será lido do stdin, e o tamanho deve ser limitado a 1000 bytes.")
fmt.Println("askqueue: Solicita ao servidor que lhe envie a lista de mensagens não lidas da queue do utilizador.")
fmt.Println("getmsg <NUM>: Solicita ao servidor o envio da mensagem da sua queue com número <NUM>.")
fmt.Println("help: Imprime instruções de uso do programa.")
}

View file

@ -1,60 +1,122 @@
package protocol package protocol
import ( import (
"time" "time"
) )
type PacketType int type PacketType int
const ( const (
ReqPK PacketType = iota ReqPK PacketType = iota
ReqAllMsg ReqAllMsg
ReqMsg ReqMsg
SubmitMsg SubmitMsg
SendPK SendPK
Msg Msg
) )
type PacketBody interface {} type PacketBody interface{}
type Packet struct { type Packet struct {
Flag PacketType Flag PacketType
Body PacketBody Body PacketBody
} }
// Client --> Server: Ask for a user's public key // Client --> Server: Ask for a user's public key
type RequestPubKey struct { type RequestPubKey struct {
FromUID string FromUID string
KeyUID string KeyUID string
}
func NewRequestPubKey(fromUID, keyUID string) Packet {
return Packet{
Flag: ReqPK,
Body: RequestPubKey{
FromUID: fromUID,
KeyUID: keyUID,
},
}
} }
// Client --> Server: Ask for all the client's messages in the queue // Client --> Server: Ask for all the client's messages in the queue
type RequestAllMsg struct { type RequestAllMsg struct {
FromUID string FromUID string
}
func NewRequestAllMsg(fromUID string) Packet {
return Packet{
Flag: ReqAllMsg,
Body: RequestAllMsg{
FromUID: fromUID,
},
}
} }
// Client --> Server: Ask for a specific message in the queue // Client --> Server: Ask for a specific message in the queue
type RequestMsg struct { type RequestMsg struct {
Num uint16 Num uint16
} }
// Client --> Server: Send message from client to server func NewRequestMsg(num uint16) Packet {
return Packet{
Flag: ReqMsg,
Body: RequestMsg{
Num: num,
},
}
}
// Client --> Server: Send message from client to server
type SubmitMessage struct { type SubmitMessage struct {
ToUID string ToUID string
Subject []byte Subject []byte
Body []byte Body []byte
}
func NewSubmitMessage(toUID string, subject, body []byte) Packet {
return Packet{
Flag: SubmitMsg,
Body: SubmitMessage{
ToUID: toUID,
Subject: subject,
Body: body,
},
}
} }
// Server --> Client: Send the client the requested public key // Server --> Client: Send the client the requested public key
type SendPubKey struct { type SendPubKey struct {
Key []byte Key []byte
}
func NewSendPubKey(key []byte) Packet {
return Packet{
Flag: SendPK,
Body: SendPubKey{
Key: key,
},
}
} }
// Server --> Client: Send the client a message // Server --> Client: Send the client a message
type Message struct { type Message struct {
FromUID string FromUID string
ToUID string ToUID string
Subject []byte Subject []byte
Body []byte Body []byte
Timestamp time.Time Timestamp time.Time
} }
func NewMessage(fromUID, toUID string, subject, body []byte, timestamp time.Time) Packet {
return Packet{
Flag: Msg,
Body: Message{
FromUID: fromUID,
ToUID: toUID,
Subject: subject,
Body: body,
Timestamp: timestamp,
},
}
}

View file

@ -3,19 +3,14 @@ package server
import ( import (
"PD1/internal/protocol" "PD1/internal/protocol"
"PD1/internal/utils/networking" "PD1/internal/utils/networking"
"encoding/json"
"fmt" "fmt"
) )
func clientHandler(connection networking.Connection[protocol.Packet]) { func clientHandler(connection networking.Connection[protocol.Packet]) {
defer connection.Conn.Close() defer connection.Conn.Close()
jd := json.NewDecoder(connection.Conn)
for { for {
var pac protocol.Packet pac := connection.Receive()
jd.Decode(&pac)
switch pac.Flag { switch pac.Flag {
case protocol.ReqPK: case protocol.ReqPK:
fmt.Println("ReqPK") fmt.Println("ReqPK")
@ -24,7 +19,7 @@ func clientHandler(connection networking.Connection[protocol.Packet]) {
case protocol.ReqMsg: case protocol.ReqMsg:
fmt.Println("ReqMsg") fmt.Println("ReqMsg")
case protocol.SubmitMsg: case protocol.SubmitMsg:
fmt.Println("SubmitMsh") fmt.Println("SubmitMsg")
} }
} }
@ -35,7 +30,7 @@ func Run(port int) {
go server.ListenLoop() go server.ListenLoop()
for { for {
//Receive connection via channel //Receive Connection via channel
conn := <-server.C conn := <-server.C
//Launch client handler via clientHandler //Launch client handler via clientHandler
go clientHandler(conn) go clientHandler(conn)

View file

@ -0,0 +1,16 @@
package networking
import "net"
type Client[T any] struct {
Connection Connection[T]
}
func NewClient[T any]() Client[T] {
dialConn, err := net.Dial("tcp", "localhost:8080")
if err != nil {
panic("Could not open connection to server")
}
conn := NewConnection[T](dialConn)
return Client[T]{Connection: conn}
}

View file

@ -0,0 +1,35 @@
package networking
import (
"encoding/json"
"net"
)
type Connection[T any] struct {
Conn net.Conn
encoder *json.Encoder
decoder *json.Decoder
}
func NewConnection[T any](netConn net.Conn) Connection[T] {
return Connection[T]{
Conn: netConn,
encoder: json.NewEncoder(netConn),
decoder: json.NewDecoder(netConn),
}
}
func (jc Connection[T]) Send(obj T) {
if err := jc.encoder.Encode(&obj); err != nil {
panic("Failed encoding data or sending it to connection")
}
}
func (jc Connection[T]) Receive() T {
var obj T
if err := jc.decoder.Decode(&obj); err != nil {
panic("Failed decoding data or reading it from connection")
}
return obj
}

View file

@ -1,76 +0,0 @@
package networking
import (
"encoding/json"
"fmt"
"net"
)
type Server[T any] struct {
listener net.Listener
C chan Connection[T]
}
type Client[T any] struct {
conn net.Conn
}
type Connection[T any] struct {
Conn net.Conn
encoder *json.Encoder
decoder *json.Decoder
}
func NewServer[T any](port int) Server[T]{
listener, err := net.Listen("tcp", fmt.Sprintf("0.0.0.0:%d", port))
if err != nil {
panic("Server could not bind to address")
}
return Server[T]{
listener:listener,
C: make(chan Connection[T]),
}
}
func (s *Server[T]) ListenLoop() {
for {
listenerConn, err := s.listener.Accept()
if err != nil {
panic("Server could not accept connection")
}
conn := NewConnection[T](listenerConn)
s.C <- conn
}
}
func (c Client[T]) Connect() Connection[T] {
dialConn, err := net.Dial("tcp", "localhost:8080")
if err != nil {
panic("Could not open connection to server")
}
return NewConnection[T](dialConn)
}
func NewConnection[T any](netConn net.Conn) Connection[T] {
return Connection[T]{
Conn: netConn,
encoder: json.NewEncoder(netConn),
decoder: json.NewDecoder(netConn),
}
}
func (jc Connection[T]) Send(obj T) {
if err := jc.encoder.Encode(&obj); err != nil {
panic("Failed encoding data or sending it to connection")
}
}
func (jc Connection[T]) Receive() T {
var obj T
if err := jc.decoder.Decode(&obj); err != nil {
panic("Failed decoding data or reading it from connection")
}
return obj
}

View file

@ -0,0 +1,35 @@
package networking
import (
"fmt"
"net"
)
type Server[T any] struct {
listener net.Listener
C chan Connection[T]
}
func NewServer[T any](port int) Server[T]{
listener, err := net.Listen("tcp", fmt.Sprintf("0.0.0.0:%d", port))
if err != nil {
panic("Server could not bind to address")
}
return Server[T]{
listener:listener,
C: make(chan Connection[T]),
}
}
func (s *Server[T]) ListenLoop() {
for {
listenerConn, err := s.listener.Accept()
if err != nil {
panic("Server could not accept connection")
}
conn := NewConnection[T](listenerConn)
s.C <- conn
}
}

View file

@ -1,9 +1,9 @@
[targets.setup] [targets.setup]
wildcards=[["golang.org/x/tools/go/analysis/passes/shadow/cmd/shadow@latest","github.com/kisielk/errcheck@latest"]] wildcards=[["golang.org/x/tools/go/analysis/passes/shadow/cmd/shadow@latest"]]
cmd="go install @@" cmd="go install @@"
[targets.check] [targets.check]
wildcards=[["go vet ./...","shadow ./...","errcheck ./..."]] wildcards=[["go vet ./...","shadow ./..."]]
cmd="@@" cmd="@@"
[targets.build] [targets.build]