SMTP connection read welcome message - go

I try to connect on smtp server and read welcome message. This is my code:
package main
import (
"fmt"
"net"
"time"
"net/smtp"
"bufio"
)
func main() {
// attempt a connection
conn, _ := net.DialTimeout("tcp", "88.198.24.108:25", 15 * time.Second)
buf := bufio.NewReader(conn)
bytes, _ := buf.ReadBytes('\n')
fmt.Printf("%s", bytes)
client, err := smtp.NewClient(conn, "88.198.24.108")
if err != nil {
fmt.Println("1>>", err)
return
}
client.Quit()
conn.Close()
}
Problem is after read welcome message stop running and wait to go in timeout, I want to read/print welcome message and continue.
220 example.me ESMTP Haraka/2.8.18 ready
1>> 421 timeout

An inspection of the standard library source indicates that smtp.NewClient() reads the SMTP banner from the remote host and throws it away.
func NewClient(conn net.Conn, host string) (*Client, error) {
text := textproto.NewConn(conn)
_, _, err := text.ReadResponse(220)
if err != nil {
text.Close()
return nil, err
}
c := &Client{Text: text, conn: conn, serverName: host, localName: "localhost"}
_, c.tls = conn.(*tls.Conn)
return c, nil
}
You want to read this banner and decide whether to send mail based on its contents.
Since you have already read the banner yourself, and presumably will make a decision on that, instead of calling smtp.NewClient() you should then implement the rest of NewClient() in your own code, possibly something like this:
client := &smtp.Client{Text: text, conn: conn, serverName: host, localName: "localhost"}
_, client.tls = conn.(*tls.Conn)

Related

How can I add a 10 second timeout with tls.Dial ? (There is no tls.DialTimeout to correspond to net.DialTimeout)

What is the best way to add a timeout when using tls.Dial in Go?
I see the net package has net.DialTimeout, but unfortunately, the tls package doesn't have a corresponding function.
I presume I should be using a context or Dialer to implement a timeout, but I'm not an expert in Go and I can't find any good examples.
(1) I found tls.DialWithDialer, but I'm not sure how to create a net.Dialer that is configured with a timeout.
func DialWithDialer(dialer *net.Dialer, network, addr string, config *Config) (*Conn, error)
(2) I also found tls.DialContext, but I'm not sure how to use that to implement a timeout.
func (d *Dialer) DialContext(ctx context.Context, network, addr string) (net.Conn, error)
(3) I presume it might be possible to establish an initial connection using net.DialTimeout and then upgrade the connection and continue with the TLS handshake, but I can't find any examples that show how to do that.
Any help or guidance would be appreciated.
Here is my simple program that connects to a list of servers and prints some info about the certificate chain. When a server is not responding, this program hangs for a long time. All I want to do is time out after 10 seconds.
package main
import (
"bufio"
"crypto/tls"
"fmt"
"os"
)
func main() {
port := "443"
conf := &tls.Config{
InsecureSkipVerify: true,
}
s := bufio.NewScanner(os.Stdin)
for s.Scan() {
host := s.Text()
conn, err := tls.Dial("tcp", host+":"+port, conf)
if err != nil {
fmt.Println("Host:", host, "Dial:", err)
continue
}
defer conn.Close()
certs := conn.ConnectionState().PeerCertificates
for _, cert := range certs {
fmt.Println("Host:", host, "Issuer:", cert.Issuer)
}
}
}
As you mention in your question there are a few options; using DialContext is a common technique:
package main
import (
"bufio"
"context"
"crypto/tls"
"fmt"
"os"
"time"
)
func main() {
port := "443"
conf := &tls.Config{
InsecureSkipVerify: true,
}
s := bufio.NewScanner(os.Stdin)
for s.Scan() {
host := s.Text()
ctx, cancel := context.WithTimeout(context.Background(), 10 * time.Second)
d := tls.Dialer{
Config: conf,
}
conn, err := d.DialContext(ctx,"tcp", host+":"+port)
cancel() // Ensure cancel is always called
if err != nil {
fmt.Println("Host:", host, "Dial:", err)
continue
}
// warning: using defer in a loop may not have the expected result
// the connection will remain open until the function exists
defer conn.Close()
tlsConn := conn.(*tls.Conn)
certs := tlsConn.ConnectionState().PeerCertificates
for _, cert := range certs {
fmt.Println("Host:", host, "Issuer:", cert.Issuer)
}
}
}
Using the above approach makes it relatively simple to allow users of your code to cancel the request (accept a context and use it where the above has context.Background()). If this is not important to you then using a Dialer with Timeout is simpler:
conn, err := tls.DialWithDialer(&net.Dialer{Timeout: 10 * time.Second}, "tcp", host+":"+port, conf)
if err != nil {
fmt.Println("Host:", host, "Dial:", err)
continue
}
certs := conn.ConnectionState().PeerCertificates
for _, cert := range certs {
fmt.Println("Host:", host, "Issuer:", cert.Issuer)
}
conn.Close()

Golang SSH Source Port

I have a requirement to use a static source port. We will do an IPTables redirect rule based on this source port. So, the static source port is used as an identifier as multiple connections are pending to the same destination port on the server. Think poor man's TCP mux a la iptables.
I have followed the Golang examples and cobbled some messy code together. I am not a programmer.
The ssh.dial function handles a lot, that becomes apparent once you use net.dial along with ssh.NewClientConn, ssh.NewClient and ssh.NewSession.
I see there is no ProxyCommand like in OpenSSH config options. I was using:
ssh -o ProxyCommand="ncat --source-port %h %p" ...
to achieve the requirement in a Bash script.
Additionally, I apologise for a loaded question but ncat et al. allow me to reuse the source port immediately.
Whereas Golang SSH leaves a TIME-WAIT 0 0 192.168.99.53:31337 192.168.99.7:22 for 60 seconds on Arch Linux.
Obviously, subsequent binds to said source port result in an error.
WRT the below code:
I have omitted the ExampleHostKeyCheck function
I know the sClient.Listen is a poor attempt at getting this work
The remote port forward does NOT appear on the server
I'm assuming a LOT more code is now required to handle the channels etc.?
The shell command does work, the file appears in /tmp/
package main
import (
"bytes"
"log"
"net"
"os"
"bufio"
"strings"
"path/filepath"
"golang.org/x/crypto/ssh"
)
func main() {
hostKey, err := ExampleHostKeyCheck()
conf := &ssh.ClientConfig{
User: "robert",
Auth: []ssh.AuthMethod{
ssh.Password("password"),
},
HostKeyCallback: ssh.FixedHostKey(hostKey),
}
server, _ := net.ResolveTCPAddr("tcp", "centos7.ephemeric.local:22")
client, _ := net.ResolveTCPAddr("tcp", ":31337")
cc, err := net.DialTCP("tcp", client, server)
//cc, err := net.DialTCP("tcp", nil, server)
if err != nil {
log.Fatalf("%s", err)
}
defer cc.Close()
conn, chans, reqs, err := ssh.NewClientConn(cc, "", conf)
if err != nil {
log.Fatalf("%s", err)
}
defer conn.Close()
// https://stackoverflow.com/questions/35906991/go-x-crypto-ssh-how-to-establish-ssh-connection-to-private-instance-over-a-ba
sClient := ssh.NewClient(conn, chans, reqs)
if err != nil {
log.Fatalf("%s", err)
}
defer sClient.Close()
session, err := sClient.NewSession()
if err != nil {
log.Fatalf("%s", err)
}
defer session.Close()
sListen, err := sClient.Listen("tcp", "127.0.0.1:31337")
if err != nil {
log.Fatal("unable to register tcp forward: ", err)
}
defer sListen.Close()
var b bytes.Buffer // import "bytes"
session.Stdout = &b // get output
// You can also pass what gets input to the stdin, allowing you to pipe content from client to server session.Stdin = bytes.NewBufferString("MyInput").
err = session.Run("echo slobwashere >>/tmp/slobwashere; ls")
}
Thank you.

How to make send message to particular URI after successfull webscoket connection?

I have a secure websocket server running on localhost:443/server-demo ( jetty websocket server).
Now I am writing a go client that can communicate with the websocket server. I am able to connect to the websocket server using right certificates. Here is the sample code.
package main
import (
"crypto/tls"
"crypto/x509"
"fmt"
"io"
"log"
)
func main() {
cert, err := tls.LoadX509KeyPair("nifi-1.10.0-bin/nifi-1.10.0/extras/gen-certs/certs/admin.pem", "nifi-1.10.0-bin/nifi-1.10.0/extras/gen-certs/certs/admin-key.pem")
if err != nil {
log.Fatalf("server: loadkeys: %s", err)
}
config := tls.Config{Certificates: []tls.Certificate{cert}, InsecureSkipVerify: true}
conn, err := tls.Dial("tcp", "127.0.0.1:443", &config)
if err != nil {
log.Fatalf("client: dial: %s", err)
}
defer conn.Close()
log.Println("client: connected to: ", conn.RemoteAddr())
state := conn.ConnectionState()
for _, v := range state.PeerCertificates {
fmt.Println(x509.MarshalPKIXPublicKey(v.PublicKey))
fmt.Println(v.Subject)
}
log.Println("client: handshake: ", state.HandshakeComplete)
log.Println("client: mutual: ", state.NegotiatedProtocolIsMutual)
message := "Hello\n"
n, err := io.WriteString(conn, message)
if err != nil {
log.Fatalf("client: write: %s", err)
}
log.Printf("client: wrote %q (%d bytes)", message, n)
reply := make([]byte, 256)
n, err = conn.Read(reply)
log.Printf("client: read %q (%d bytes)", string(reply[:n]), n)
log.Print("client: exiting")
}
The above code throws this error:
"HTTP/1.1 400 No URI\r\nContent-Type: text/html;charset=iso-8859-1\r\nContent-Length: 49\r\nConnection: close\r\nServer: Jetty(9.4.19.v20190610)\r\n\r\n<h1>Bad Message 400</h1><pre>reason: No URI</pre>" (188 bytes)
My question is after making the connection how can I send message to particular URI? i.e I want to send a message to wss://localhost:443/server-demo.
The code in a question does not establish a WebSocket connection to the server.
To establish the WebSocket connection, the application must write a WebSocket handshake to conn and receive the handshake response. See the RFC for the details.
Most applications use a websocket package than handles all of these details. The gorilla/websocket package is a popular choice.
This code should get you started with gorilla:
cert, err := tls.LoadX509KeyPair("nifi-1.10.0-bin/nifi-1.10.0/extras/gen-certs/certs/admin.pem", "nifi-1.10.0-bin/nifi-1.10.0/extras/gen-certs/certs/admin-key.pem")
if err != nil {
log.Fatalf("server: loadkeys: %s", err)
}
config := tls.Config{Certificates: []tls.Certificate{cert}, InsecureSkipVerify: true}
d := websocket.Dialer{
TLSClientConfig: &config,
}
c, _, err := d.Dial("wss://localhost:443/server-demo", nil)
if err != nil {
log.Fatal(err)
}
defer c.Close()
// Use `c` to send and receive messages

Different behaviors of golang tcp socket on different OS

I want to simulate the http server with tcp socket written in Go. The program runs well on Linux or Mac, but has some problems on Windows. I have configured the built-in firewall to allow connections on the port. On Windows, when the browser requests the program, it shows me "This site can’t be reached. The connection was reset." Instead, it can response "Hello world" correctly on Linux or Mac. As follows.
// implement http server with tcp socket
package main
import (
"log"
"net"
"os"
)
var content = []byte(`HTTP/1.1 200 OK
Content-type: text/plain
Hello world!`)
func handleConn(conn net.Conn) {
conn.Write(content)
defer conn.Close()
}
func main() {
addr := "localhost:10000"
listener, err := net.Listen("tcp", addr)
checkErr(err)
defer listener.Close()
for {
conn, err := listener.Accept()
if err != nil {
log.Fatal(err)
continue
}
go handleConn(conn)
}
}
func checkErr(err error) {
if err != nil {
log.Fatal(err)
os.Exit(1)
}
}
I try to change some code to read all the bytes from the connection on Windows, after that, the program responses correctly.
func handleConn(conn net.Conn) {
var buf = make([]byte, 1024)
_, err := conn.Read(buf)
checkErr(err)
conn.Write(content)
defer conn.Close()
}
But I don't know why must I read all the bytes of connection on Windows.

How to listen to a client continiously using gob in Golang

In my use case I would like to continuously listen to a TCP connection and receive the value. The expected value is an object. So I am using gob decoder to receive the value from the connection. I would like to continuously listen to the connection and receive the object using go routines. I have the code snippet here[It is part of the application. code snippet does not compile]. It is getting value for the first time but not receiving for the subsequent objects.
func main() {
//...
// SOME CODE
//...
// All hosts who are connected; a map wherein
// the keys are ip addreses and the values are
//net.Conn objects
allClients := make(map[string]net.Conn)
tMaps := make(chan map[string]int64)
for {
select {
// Accept new clients
//
case conn := <-newConnections:
log.Printf("Accepted new client, #%s", hostIp)
// Constantly read incoming messages from this
// client in a goroutine and push those onto
// the tMaps channel for broadcast to others.
//
go func(conn net.Conn) {
dec := gob.NewDecoder(conn)
for {
var tMap map[string]int64
err := dec.Decode(&tMap)
if err != nil {
fmt.Println("Error in decoding ", err)
break
}
log.Printf("Received values: %+v", tMap)
//update throttle map based on the received value
tMaps <- throttleMap
}
}(conn)
}
}
Could anyone help me on this?
Let's look at the basics of a TCP server in Go.
First, there is the "listening" part. We can set that up like this:
package main
import (
"fmt"
"io"
"net"
"time"
)
func main() {
ln, err := net.Listen("tcp", ":9000")
if err != nil {
panic(err)
}
defer ln.Close()
for {
conn, err := ln.Accept()
if err != nil {
panic(err)
}
io.WriteString(conn, fmt.Sprint("Hello World\n", time.Now(), "\n"))
conn.Close()
}
}
Notice the infinite for loop. It is always running and looping over that code. What does the code that is being looped over do? If a connection comes in on the port which is being listened on, then that connection is accepted. We then do something with that connection. In this case, we write back to it with io.WriteString. To this one connection, we are sending a response. We then close the connection. And if there are more connections, we're ready to accept them.
Now let's create a client to connect to the TCP server. This is known as "dialing" in to the TCP server.
To run all of this code on your machine, run the TCP server code above. To run the code, go to your terminal and enter: go run main.go
Now put the code directly below into another file. Launch another tab in your terminal. Run that code also by entering: go run main.go
The code below which "dials" in to your TCP server will connect to the server and the TCP server will respond, then close the connection.
Here is the code for dialing into a TCP server as a client:
package main
import (
"fmt"
"io/ioutil"
"net"
)
func main() {
conn, err := net.Dial("tcp", "localhost:9000")
if err != nil {
panic(err)
}
defer conn.Close()
bs, _ := ioutil.ReadAll(conn)
fmt.Println(string(bs))
}
We can take these basics and start having fun.
Let's create an "echo" server.
This will illustrate accepting many connections.
package main
import (
"io"
"net"
)
func main() {
ln, err := net.Listen("tcp", ":9000")
if err != nil {
panic(err)
}
defer ln.Close()
for {
conn, err := ln.Accept()
if err != nil {
panic(err)
}
// handles unlimited connections
go func() {
io.Copy(conn, conn)
conn.Close()
}()
}
}
Run the file above the same way as before: go run main.go
If you get an error, make sure you have closed the TCP server we were running from the previous example. You close the TCP server with ctrl+c in the terminal.
Now that your new TCP server is running, let's connect to it using Telnet.
On windows you can install telnet; on Mac, it should already be there. Use this command to run telnet and connect to your TCP server: telnet localhost 9000
Now for one more example - an in-memory database like Redis:
package main
import (
"bufio"
"fmt"
"io"
"log"
"net"
"strings"
)
var data = make(map[string]string)
func handle(conn net.Conn) {
defer conn.Close()
scanner := bufio.NewScanner(conn)
for scanner.Scan() {
ln := scanner.Text()
fs := strings.Fields(ln)
if len(fs) < 2 {
io.WriteString(conn, "This is an in-memory database \n" +
"Use SET, GET, DEL like this: \n" +
"SET key value \n" +
"GET key \n" +
"DEL key \n\n" +
"For example - try these commands: \n" +
"SET fav chocolate \n" +
"GET fav \n\n\n")
continue
}
switch fs[0] {
case "GET":
key := fs[1]
value := data[key]
fmt.Fprintf(conn, "%s\n", value)
case "SET":
if len(fs) != 3 {
io.WriteString(conn, "EXPECTED VALUE\n")
continue
}
key := fs[1]
value := fs[2]
data[key] = value
case "DEL":
key := fs[1]
delete(data, key)
default:
io.WriteString(conn, "INVALID COMMAND "+fs[0]+"\n")
}
}
}
func main() {
li, err := net.Listen("tcp", ":9000")
if err != nil {
log.Fatalln(err)
}
defer li.Close()
for {
conn, err := li.Accept()
if err != nil {
log.Fatalln(err)
}
handle(conn)
}
}
And adding in concurrency:
package main
import (
"bufio"
"fmt"
"io"
"log"
"net"
"strings"
)
type Command struct {
Fields []string
Result chan string
}
func redisServer(commands chan Command) {
var data = make(map[string]string)
for cmd := range commands {
if len(cmd.Fields) < 2 {
cmd.Result <- "Expected at least 2 arguments"
continue
}
fmt.Println("GOT COMMAND", cmd)
switch cmd.Fields[0] {
// GET <KEY>
case "GET":
key := cmd.Fields[1]
value := data[key]
cmd.Result <- value
// SET <KEY> <VALUE>
case "SET":
if len(cmd.Fields) != 3 {
cmd.Result <- "EXPECTED VALUE"
continue
}
key := cmd.Fields[1]
value := cmd.Fields[2]
data[key] = value
cmd.Result <- ""
// DEL <KEY>
case "DEL":
key := cmd.Fields[1]
delete(data, key)
cmd.Result <- ""
default:
cmd.Result <- "INVALID COMMAND " + cmd.Fields[0] + "\n"
}
}
}
func handle(commands chan Command, conn net.Conn) {
defer conn.Close()
scanner := bufio.NewScanner(conn)
for scanner.Scan() {
ln := scanner.Text()
fs := strings.Fields(ln)
result := make(chan string)
commands <- Command{
Fields: fs,
Result: result,
}
io.WriteString(conn, <-result+"\n")
}
}
func main() {
li, err := net.Listen("tcp", ":9000")
if err != nil {
log.Fatalln(err)
}
defer li.Close()
commands := make(chan Command)
go redisServer(commands)
for {
conn, err := li.Accept()
if err != nil {
log.Fatalln(err)
}
go handle(commands, conn)
}
}
See my lectures from my CSUF class describing all of this here. And one more great resource.

Resources