commit ee11d4c8187a2ec638390e0047e9a380098aa935 Author: Aurélie DELHAIE Date: Sat Aug 23 14:31:15 2025 +0200 first commit diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..daeab24 --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module test + +go 1.25 \ No newline at end of file diff --git a/main.go b/main.go new file mode 100644 index 0000000..4049747 --- /dev/null +++ b/main.go @@ -0,0 +1,12 @@ +package main + +import ( + "test/pkg/client" +) + +func main() { + cli := client.New("172.0.0.42", 80) + if err := cli.SendSMS("0782092003", "salut toi comment tu vas ?"); err != nil { + panic(err) + } +} diff --git a/pkg/client/client.go b/pkg/client/client.go new file mode 100644 index 0000000..0a4c6f2 --- /dev/null +++ b/pkg/client/client.go @@ -0,0 +1,156 @@ +package client + +import ( + "crypto/md5" + "encoding/hex" + "fmt" + "io" + "net" + "strings" +) + +type ( + Client struct { + addr string + } + + Payload interface { + WriteTo(server io.Writer) (int64, error) + Len() int64 + } + + PayloadStrict struct { + data string + } + + PayloadCloser struct { + data string + closer byte + } + + Command byte +) + +const ( + Send Command = 1 +) + +func (p PayloadCloser) WriteTo(server io.Writer) (int64, error) { + n1, err := server.Write([]byte(p.data)) + if err != nil { + return int64(n1), err + } + n2, err := server.Write([]byte{p.closer}) + if err != nil { + return int64(n1 + n2), err + } + return int64(n1 + n2), nil +} + +func (p PayloadCloser) Len() int64 { + return int64(len(p.data) + 1) +} + +func (p PayloadStrict) WriteTo(server io.Writer) (int64, error) { + n, err := server.Write([]byte(p.data)) + return int64(n), err +} + +func (p PayloadStrict) Len() int64 { + return int64(len(p.data)) +} + +func New(ip string, port int) *Client { + return &Client{ + addr: fmt.Sprintf("%s:%d", ip, port), + } +} + +func (c *Client) SendSMS(phoneNumber, msg string) error { + if len(msg) > 140 { + return fmt.Errorf("message too long") + } + conn, err := net.Dial("tcp", c.addr) + if err != nil { + return fmt.Errorf("failed to connect: %w", err) + } + defer conn.Close() + + m := md5.New() + m.Write([]byte(msg)) + hash := hex.EncodeToString(m.Sum(nil)) + + if err := c.Send(conn, Send, PayloadStrict{phoneNumber}, PayloadStrict{hash}, PayloadCloser{msg, 1}); err != nil { + return fmt.Errorf("failed to send data: %w", err) + } + + resp, err := c.ReadAll(conn) + if err != nil { + return err + } + + if len(resp) == 0 { + return fmt.Errorf("empty message: the server stop responding") + } + fmt.Println(resp) + + return nil +} + +func (c *Client) Send(server net.Conn, cmd Command, payload ...Payload) error { + if err := c.SendCommand(server, cmd); err != nil { + return err + } + + for _, data := range payload { + n, err := data.WriteTo(server) + if err != nil { + return fmt.Errorf("failed to send phone number: %w", err) + } + + if n != data.Len() { + return fmt.Errorf("network error: broken pipe") + } + } + + return nil +} + +func (c *Client) SendCommand(server net.Conn, cmd Command) error { + n, err := server.Write([]byte{byte(cmd)}) + if err != nil { + return fmt.Errorf("failed to send command: %w", err) + } + + if n != 1 { + return fmt.Errorf("failed to send command: broken pipe") + } + return nil +} + +func (c *Client) ReadAll(server net.Conn) (string, error) { + var err error + var data []byte + var message string + waiting := true + for { + data, err = io.ReadAll(server) + if err != nil { + return "", fmt.Errorf("failed to received response: %w", err) + } + if len(data) == 0 { + if !waiting { + break + } + } else { + waiting = false + message += string(data) + } + } + + if strings.HasPrefix(message, "error:") { + return "", fmt.Errorf("%s", message) + } + + return message, nil +}