golang smtp get and parse wellcome message of server - go

I want to get welcome message of smtp server and check if contains specific keyword, this is my actual code:
package main
import (
"fmt"
"net"
"strings"
"time"
"net/smtp"
"errors"
)
type loginAuth struct {
username, password string
}
func (a *loginAuth) Start(server *smtp.ServerInfo) (string, []byte, error) {
return "LOGIN", []byte{}, nil
}
func (a *loginAuth) Next(fromServer []byte, more bool) ([]byte, error) {
if more {
switch string(fromServer) {
case "Username:":
return []byte(a.username), nil
case "Password:":
return []byte(a.password), nil
default:
return nil, errors.New("Unkown fromServer")
}
}
return nil, nil
}
func main() {
// attempt a connection
conn, err := net.DialTimeout("tcp", "88.198.24.108:25", 15 * time.Second)
defer conn.Close()
if err == nil {
buf := make([]byte, 32, 32)
conn.Read(buf)
if strings.Contains(string(buf), "Haraka") {
fmt.Println("this server not working with this application")
}
}
client, err := smtp.NewClient(conn, "88.198.24.108")
if err != nil {
fmt.Println("1>>", err)
return
}
err = client.Auth(&loginAuth{"info#example.com", "123456"})
if err != nil {
fmt.Println("2>>", err)
return
} else {
fmt.Println("auth successfull")
}
}
The code return this error : 1>> short response: 18 ready
where i wrong ?

Related

I only have *.pem and *.key files, how to make sure the http client is not attacked

I only got the two files *.pem and *.key through the certificate authority.
I am worried about man-in-the-middle attacks, which will replace the certificate and deceive the client, so I wrote the following code, the client directly reads the *.pem file and compares the certificate obtained by http to ensure that the connection is correct when they are the same.
I would like to know if my solution is correct and if there is a better solution to my problem?
server code
package main
import (
"fmt"
"net/http"
"time"
)
func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
_, _ = fmt.Fprintf(w, "hello %s", time.Now().String())
})
err := http.ListenAndServeTLS(":443", `xx.pem`, `xx.key`, nil)
if err != nil {
panic(err)
}
}
client code
package main
import (
"bytes"
"crypto/tls"
"crypto/x509"
"encoding/pem"
"errors"
"fmt"
"io"
"net/http"
"os"
)
func main() {
err := getPem()
if err != nil {
panic(err)
}
err = httpGet()
if err != nil {
panic(err)
}
}
var (
pemRawCerts [][]byte // xx.pem读取的原始数据
errCheckPem = errors.New("check xx.pem error")
)
func getPem() error {
certPEMBlock, err := os.ReadFile(`xxx.pem`)
if err != nil {
return err
}
var certDERBlock *pem.Block
for {
certDERBlock, certPEMBlock = pem.Decode(certPEMBlock)
if certDERBlock == nil {
break
}
if certDERBlock.Type == "CERTIFICATE" {
tmp := make([]byte, len(certDERBlock.Bytes))
copy(tmp, certDERBlock.Bytes)
pemRawCerts = append(pemRawCerts, tmp)
}
}
return nil
}
func httpGet() error {
c := http.Client{
Transport: &http.Transport{
TLSClientConfig: &tls.Config{
VerifyPeerCertificate: func(data [][]byte, verifiedChains [][]*x509.Certificate) error {
if len(data) == len(pemRawCerts) {
for i := len(data) - 1; i >= 0; i-- {
if !bytes.Equal(data[i], pemRawCerts[i]) {
return errCheckPem
}
}
// Only when every item of the certificate is correct can the current connection be determined to be ok
return nil
}
return errCheckPem
},
},
},
}
resp, err := c.Get("https://janbar.com")
if err != nil {
return err
}
defer resp.Body.Close()
n, err := io.Copy(io.Discard, resp.Body)
fmt.Println(n)
return err
}

(Go) Modify websocket body in reverse proxy

Hello Everyone, I'm new to Go.
I'm creating a reverse proxy server using Go.
My Server has websocket. I finally get it connected.
Now I want to change websocket message body.
Sorry If my code is weird to you. Forgive me, I'm new to Go 😢
I'm wraping resp.body to NewReadWriteBody() in which contains wrapper for Read, Write and Closer. And I'm modifying message body inside it.
Here is how I'm doing with it:
package rever
// https://blog.joshsoftware.com/2021/05/25/simple-and-powerful-reverseproxy-in-go/
// https://github.com/golang/go/blob/master/src/net/http/httputil/reverseproxy.go
import (
"bytes"
"crypto/tls"
"fmt"
"io"
"io/ioutil"
"log"
"net/http"
"net/http/httputil"
"net/url"
"strconv"
)
type ReadWriteBody struct {
originBody io.ReadWriteCloser
}
func NewReadWriteBody(body io.ReadCloser) *ReadWriteBody {
b := &ReadWriteBody{}
rw, ok := body.(io.ReadWriteCloser)
if !ok {
log.Println("29: error while casting body to ReadWriteCloser")
}
b.originBody = rw
return b
}
func (b *ReadWriteBody) Read(p []byte) (n int, err error) {
buf := make([]byte, len(p))
n, err = b.originBody.Read(buf)
if err != nil {
log.Println("43: ", err.Error())
return n, err
}
buf = bytes.ReplaceAll(buf, []byte("mm.remote"), []byte("mm.local"))
copy(p[:], buf)
return len(p), nil
}
func (b *ReadWriteBody) Write(p []byte) (n int, err error) {
buf := make([]byte, len(p))
n, err = b.originBody.Write(buf)
if err != nil {
log.Println(err.Error())
return n, err
}
buf = bytes.ReplaceAll(buf, []byte("mm.local"), []byte("mm.remote"))
copy(p[:], buf)
return len(p), nil
}
func (b *ReadWriteBody) Close() error {
return b.originBody.Close()
}
type transport struct {
http.RoundTripper
}
func (t *transport) RoundTrip(req *http.Request) (resp *http.Response, err error) {
resp, err = t.RoundTripper.RoundTrip(req)
if err != nil {
log.Println("99: ", err.Error())
return nil, err
}
if resp.StatusCode == http.StatusSwitchingProtocols {
resp.Body = NewReadWriteBody(resp.Body)
return resp, nil
}
b, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Println("114: ", err.Error())
return nil, err
}
err = resp.Body.Close()
if err != nil {
log.Println("119", err.Error())
return nil, err
}
b = bytes.ReplaceAll(b, []byte("mm.remote"), []byte("mm.local"))
body := ioutil.NopCloser(bytes.NewReader(b))
resp.Body = body
resp.ContentLength = int64(len(b))
resp.Header.Set("Content-Length", strconv.Itoa(len(b)))
return resp, nil
}
var _ http.RoundTripper = &transport{}
// NewProxy takes target host and creates a reverse proxy
func NewProxy(targetHost string) (*httputil.ReverseProxy, error) {
url, err := url.Parse(targetHost)
if err != nil {
log.Println("141: ", err.Error())
return nil, err
}
proxy := httputil.NewSingleHostReverseProxy(url)
originalDirector := proxy.Director
proxy.Director = func(req *http.Request) {
originalDirector(req)
modifyRequest(req)
}
proxy.ErrorHandler = errorHandler()
dt := http.DefaultTransport.(*http.Transport).Clone()
dt.TLSClientConfig = &tls.Config{}
dt.ForceAttemptHTTP2 = false
proxy.Transport = &transport{dt}
return proxy, nil
}
func modifyRequest(req *http.Request) {
req.Host = "mm.remote"
req.Header.Set("Accept-Encoding", "identity")
}
func errorHandler() func(http.ResponseWriter, *http.Request, error) {
return func(w http.ResponseWriter, req *http.Request, err error) {
// fmt.Printf("Got error while modifying response: %v \n", err)
}
}
// ProxyRequestHandler handles the http request using proxy
func ProxyRequestHandler(proxy *httputil.ReverseProxy) func(http.ResponseWriter, *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
proxy.ServeHTTP(w, r)
}
}
func Main() {
// initialize a reverse proxy and pass the actual backend server url here
proxy, err := NewProxy("https://mm.remote")
if err != nil {
log.Println(err.Error())
panic(err)
}
// handle all requests to your server using the proxy
http.HandleFunc("/", ProxyRequestHandler(proxy))
fmt.Println("Server started")
log.Fatal(http.ListenAndServe(":8008", nil))
}

How to terminate a console input request when a new input is requested

I need to terminate an existing console input request when a new one is requested. The following code is an attempt to close an existing request using a channel but it does not seem to terminate the input request.
package main
import (
"bufio"
"fmt"
"log"
"os"
"strings"
"time"
)
func main() {
go Confirm("you are a programmer, aint you?")
time.Sleep(2 * time.Second)
Confirm("do you love go?")
}
var cancelChannel chan struct{}
func Confirm(s string) bool {
//check if channel type holds a value then close the channel to remove previous confirmation input
if cancelChannel != nil {
fmt.Println("channel to be closed")
close(cancelChannel)
}
cancelChannel = make(chan struct{})
reader := bufio.NewReader(os.Stdin)
for {
fmt.Printf("%s [y/n]: ", s)
response, err := reader.ReadString('\n')
if err != nil {
log.Fatal(err)
}
response = strings.ToLower(strings.TrimSpace(response))
if response == "y" || response == "yes" {
return true
} else if response == "n" || response == "no" {
return false
}
if _, ok := <-cancelChannel; !ok {
fmt.Println("channel closed")
return false
}
}
}
As #JimB mentioned in comment you can't interrupt read on stdin although there is kinda shady trick how you can achieve it. It's possible to duplicate os.Stdin file descriptor using syscall (not recommended) and open it as non blocking file.
package main
import (
"bytes"
"context"
"errors"
"fmt"
"io"
"io/fs"
"io/ioutil"
"os"
"syscall"
"time"
)
func setNonblock(f *os.File) error {
c, err := f.SyscallConn()
if err != nil {
return err
}
var err2 error
err = c.Control(func(fd uintptr) {
err2 = syscall.SetNonblock(int(fd), true)
})
if err != nil {
return err
}
return err2
}
func nonBlockingFile(f *os.File) (*os.File, error) {
if err := setNonblock(f); err != nil {
return nil, err
}
fd, err := syscall.Dup(int(f.Fd()))
if err != nil {
return nil, err
}
f2 := os.NewFile(uintptr(fd), f.Name())
return f2, nil
}
func read(ctx context.Context, f *os.File) (io.Reader, error) {
r, err := nonBlockingFile(f)
if err != nil {
return nil, err
}
go func() {
defer r.Close()
<-ctx.Done()
}()
buff := bytes.NewBuffer([]byte{})
for {
_, err := io.Copy(buff, r)
if err != nil {
if errors.Is(err, fs.ErrClosed) {
break
}
panic(err)
}
}
return buff, nil
}
func main() {
ctx1, cancel := context.WithCancel(context.Background())
go func() {
time.Sleep(time.Second * 2)
cancel()
}()
buf1, err := read(ctx1, os.Stdin)
if err != nil {
panic(err)
}
ctx2, _ := context.WithTimeout(context.Background(), time.Second*2)
buf2, err := read(ctx2, os.Stdin)
fmt.Println("buf1")
fmt.Println(ioutil.ReadAll(buf1))
fmt.Println("buf2")
fmt.Println(ioutil.ReadAll(buf2))
}
Go on and explore the simplicity offer by go
https://pkg.go.dev/context#WithCancel
You can have a context that returning CancelFunc then you use context.WithCancel.
And execute cancel func if you want to terminate.
This is the good practice way, you can also do a dirty os.Exit(0) in another case.

Any way to set xerrors.Caller(1) when output an error by xerrors.Errorf()?

Running the below code, the stack trace is outputted with the line number of fmt.Print(...). But I want to output the line of logError(err). I think I need to call xerrors.Caller(1) to do that but I don't know how. Help me.
import (
"fmt"
"io/ioutil"
"golang.org/x/xerrors"
)
func main() {
_, err := ioutil.ReadFile("")
if err != nil {
logError(err)
return
}
}
func logError(err error) {
fmt.Printf("%+v", xerrors.Errorf(": %w", err))
}
I think what you want is:
import (
"fmt"
"io/ioutil"
"golang.org/x/xerrors"
)
func doWhatever() error {
_, err := ioutil.ReadFile("")
if err != nil {
return xerrors.Errorf("failed doing whatever: %w", err)
}
return nil
}
func main() {
err := doWhatever()
if err != nil {
logError(err)
return
}
}
func logError(err error) {
fmt.Printf("%+v", err)
}

Golang Gorilla Websocket stops receiving information at 120 seconds

I'm currently trying to connect to the CEX.IO bitcoin exchange's websocket, but have been having issues not only with CEX.IO but with others too. All of my connections drop around the 120-second mark which makes me think there is some TTL problem going on. The Process() goroutine in the main package ends up just hanging and waiting for data from the readLoop which just stops receiving data. I've included some read-only API keys in the code so you can test if you'd like.
package main
import (
"fmt"
"bitbucket.org/tradedefender/cryptocurrency/exchange-connector/cexio"
"github.com/shopspring/decimal"
"encoding/json"
"time"
)
type OrderBook struct {
Asks []Ask
Bids []Bid
}
type Ask struct {
Rate decimal.Decimal
Amount decimal.Decimal
}
type Bid struct {
Rate decimal.Decimal
Amount decimal.Decimal
}
func main() {
cexioConn := new(cexio.Connection)
err := cexioConn.Connect()
if err != nil {
fmt.Errorf("error: %s", err.Error())
}
err = cexioConn.Authenticate("TLwYkktLf7Im6nqSKt6UO1IrU", "9ImOJcR7Qj3LMIyPCzky0D7WE")
if err != nil {
fmt.Errorf("error: %s", err.Error())
}
readChannel := make(chan cexio.IntraAppMessage, 25)
go cexioConn.ReadLoop(readChannel)
processor := Processor{
WatchPairs: [][2]string{
[2]string{
"BTC", "USD",
},
},
conn: cexioConn,
}
go processor.Process(readChannel)
// LOL
for {
continue
}
}
type Processor struct {
WatchPairs [][2]string
conn *cexio.Connection
}
func (p *Processor) Process(ch <-chan cexio.IntraAppMessage) {
p.conn.SubscribeToOrderBook(p.WatchPairs[0])
pingTimer := time.Now().Unix()
for {
fmt.Printf("(%v)\n", time.Now().Unix())
if (time.Now().Unix() - pingTimer) >= 10 {
fmt.Println("sending ping")
p.conn.SendPing()
pingTimer = time.Now().Unix()
}
readMsg := <- ch
output, _ := json.Marshal(readMsg.SocketMessage)
fmt.Println(string(output))
if readMsg.SocketMessage.Event == "ping" {
fmt.Println("sending pong")
p.conn.SendPong()
pingTimer = time.Now().Unix()
}
}
}
Below is the connector to the cexio websocket. Here is a link to their API: https://cex.io/websocket-api
package cexio
import (
"github.com/gorilla/websocket"
//"github.com/shopspring/decimal"
"github.com/satori/go.uuid"
"encoding/hex"
"encoding/json"
"crypto/hmac"
"crypto/sha256"
"bytes"
"strconv"
"time"
"fmt"
)
const Url = "wss://ws.cex.io/ws/"
type Connection struct {
conn *websocket.Conn
}
type IntraAppMessage struct {
SocketMessage GenericMessage
ProgramMessage ProgramMessage
}
type GenericMessage struct {
Event string `json:"e"`
Data interface{} `json:"data"`
Auth AuthData `json:"auth,omitempty"`
Ok string `json:"ok,omitempty"`
Oid string `json:"oid,omitempty"`
Time int64 `json:"time,omitempty"`
}
type ProgramMessage struct {
Error string
}
type AuthData struct {
Key string `json:"key"`
Signature string `json:"signature"`
Timestamp int64 `json:"timestamp"`
}
type OrderBookSubscribeData struct {
Pair [2]string `json:"pair"`
Subscribe bool `json:"subscribe"`
Depth int `json:"depth"`
}
func (c *Connection) SendPong() error {
pongMsg := GenericMessage{
Event: "pong",
}
err := c.conn.WriteJSON(pongMsg)
if err != nil {
return nil
}
deadline := time.Now().Add(15*time.Second)
err = c.conn.WriteControl(websocket.PongMessage, nil, deadline)
if err != nil {
return err
}
return nil
}
func (c *Connection) SendPing() error {
pingMsg := GenericMessage{
Event: "get-balance",
Oid: uuid.NewV4().String(),
}
err := c.conn.WriteJSON(pingMsg)
if err != nil {
return err
}
deadline := time.Now().Add(15*time.Second)
err = c.conn.WriteControl(websocket.PingMessage, nil, deadline)
if err != nil {
return err
}
return nil
}
func (c *Connection) Connect() error {
dialer := *websocket.DefaultDialer
wsConn, _, err := dialer.Dial(Url, nil)
if err != nil {
return err
}
c.conn = wsConn
//c.conn.SetPingHandler(c.HandlePing)
for {
_, msgBytes, err := c.conn.ReadMessage()
if err != nil {
c.Disconnect()
return err
}
fmt.Println(string(msgBytes))
var m GenericMessage
err = json.Unmarshal(msgBytes, &m)
if err != nil {
c.Disconnect()
return err
}
if m.Event != "connected" {
c.Disconnect()
return err
} else {
break
}
}
return nil
}
func (c *Connection) Disconnect() error {
return c.conn.Close()
}
func (c *Connection) ReadLoop(ch chan<- IntraAppMessage) {
for {
fmt.Println("starting new read")
_, msgBytes, err := c.conn.ReadMessage()
if err != nil {
ch <- IntraAppMessage{
ProgramMessage: ProgramMessage{
Error: err.Error(),
},
}
continue
}
var m GenericMessage
err = json.Unmarshal(msgBytes, &m)
if err != nil {
ch <- IntraAppMessage{
ProgramMessage: ProgramMessage{
Error: err.Error(),
},
}
continue
}
ch <- IntraAppMessage{
SocketMessage: m,
}
}
}
func CreateSignature(timestamp int64, key, secret string) string {
secretBytes := []byte(secret)
h := hmac.New(sha256.New, secretBytes)
var buffer bytes.Buffer
buffer.WriteString(strconv.FormatInt(timestamp, 10))
buffer.WriteString(key)
h.Write(buffer.Bytes())
return hex.EncodeToString(h.Sum(nil))
}
func (c *Connection) Authenticate(key, secret string) error {
timestamp := time.Now().Unix()
signature := CreateSignature(timestamp, key, secret)
var authMsg GenericMessage
authMsg.Event = "auth"
authMsg.Auth = AuthData{
Key: key,
Signature: signature,
Timestamp: timestamp,
}
err := c.conn.WriteJSON(authMsg)
if err != nil {
return err
}
for {
_, msgBytes, err := c.conn.ReadMessage()
if err != nil {
c.Disconnect()
return err
}
fmt.Println(string(msgBytes))
var m GenericMessage
err = json.Unmarshal(msgBytes, &m)
if err != nil {
c.Disconnect()
return err
}
if m.Event != "auth" && m.Ok != "ok" {
c.Disconnect()
return err
} else {
break
}
}
return nil
}
func (c *Connection) SubscribeToOrderBook(pair [2]string) error {
sendMsg := GenericMessage{
Event: "order-book-subscribe",
Data: OrderBookSubscribeData{
Pair: pair,
Subscribe: true,
Depth: 0,
},
Oid: uuid.NewV4().String(),
}
err := c.conn.WriteJSON(sendMsg)
if err != nil {
return err
}
return nil
}
func (c *Connection) GetBalance() error {
sendMsg := GenericMessage{
Event: "get-balance",
Oid: uuid.NewV4().String(),
}
err := c.conn.WriteJSON(sendMsg)
if err != nil {
return err
}
return nil
}
Solution was to remove the
for {
continue
}
at the end of the main function

Resources