I'm trying to make a simple echo client and server that uses Unix sockets. In this example, the connection seems to be unidirectional. The server can receive data from the client, but it can't send the data back. If I use tcp connections instead, it works great. Does anyone know what's wrong?
Server
package main
import "net"
import "fmt"
func echoServer(c net.Conn) {
for {
buf := make([]byte, 512)
nr, err := c.Read(buf)
if err != nil {
return
}
data := buf[0:nr]
fmt.Printf("Received: %v", string(data))
_, err = c.Write(data)
if err != nil {
panic("Write: " + err.String())
}
}
}
func main() {
l, err := net.Listen("unix", "/tmp/echo.sock")
if err != nil {
println("listen error", err.String())
return
}
for {
fd, err := l.Accept()
if err != nil {
println("accept error", err.String())
return
}
go echoServer(fd)
}
}
Client
package main
import "net"
import "time"
func main() {
c,err := net.Dial("unix","", "/tmp/echo.sock")
if err != nil {
panic(err.String())
}
for {
_,err := c.Write([]byte("hi\n"))
if err != nil {
println(err.String())
}
time.Sleep(1e9)
}
}
In your example client, you don't seem to be reading the result from the server. When I add code to do that, I see the results from the server.
Also, take a look at how I used defer and break to make the reader goroutine exit.
Server
package main
import (
"log"
"net"
)
func echoServer(c net.Conn) {
for {
buf := make([]byte, 512)
nr, err := c.Read(buf)
if err != nil {
return
}
data := buf[0:nr]
println("Server got:", string(data))
_, err = c.Write(data)
if err != nil {
log.Fatal("Write: ", err)
}
}
}
func main() {
l, err := net.Listen("unix", "/tmp/echo.sock")
if err != nil {
log.Fatal("listen error:", err)
}
for {
fd, err := l.Accept()
if err != nil {
log.Fatal("accept error:", err)
}
go echoServer(fd)
}
}
Client
package main
import (
"io"
"log"
"net"
"time"
)
func reader(r io.Reader) {
buf := make([]byte, 1024)
for {
n, err := r.Read(buf[:])
if err != nil {
return
}
println("Client got:", string(buf[0:n]))
}
}
func main() {
c, err := net.Dial("unix", "/tmp/echo.sock")
if err != nil {
panic(err)
}
defer c.Close()
go reader(c)
for {
_, err := c.Write([]byte("hi"))
if err != nil {
log.Fatal("write error:", err)
break
}
time.Sleep(1e9)
}
}
Related
Go version: go1.14.4 darwin/amd64
TCP server
package main
import (
"fmt"
"io"
"net"
"time"
)
const (
maxBufLen int = 4 * 1024
)
type Server struct {
}
func (s *Server) Start() {
listener, err := net.Listen("tcp", "localhost:9001")
if err != nil {
panic(err)
}
var connections []net.Conn
defer func() {
for _, conn := range connections {
// ignore
_ = conn.Close()
}
}()
// loop...
for {
connected, err := listener.Accept()
if err != nil {
if ne, ok := err.(net.Error); ok && ne.Temporary() {
// temporary err will bi ignored
continue
} else {
fmt.Println(err)
return
}
}
go handleConn(connected, true)
connections = append(connections, connected)
}
}
func handleConn(conn net.Conn, server bool) {
buf := make([]byte, maxBufLen)
for {
// read
setTimeout(conn)
_, err := conn.Read(buf)
if err != nil {
if ne, ok := err.(net.Error); ok && (ne.Timeout() || ne.Temporary()) {
fmt.Println("need continue...")
continue
}
if err == io.EOF {
fmt.Println("EOF")
break
}
// other...
panic(err)
}
// handle recv msg.
s := string(buf)
if server {
//fmt.Println("server recv req ", s)
} else {
fmt.Println("client recv resp ", s)
}
if server {
output := "hi " + s
ob := []byte(output)
_, err := conn.Write(ob)
if err != nil {
fmt.Println(err)
break
}
}
}
}
func setTimeout(conn net.Conn) {
setErr := conn.SetReadDeadline(time.Now().Add(20 * time.Second))
if setErr != nil {
panic(setErr)
}
}
TCP client
package main
import (
"net"
"time"
)
type Client struct {
Exit chan struct{}
}
func (c *Client) Start() {
conn, err := net.Dial("tcp", "localhost:9001")
if err != nil {
panic(err)
}
defer conn.Close()
go handleWrite(conn)
go handleConn(conn, false)
<-c.Exit
}
func handleWrite(conn net.Conn) {
for {
input := "carryxyh"
_, err := conn.Write([]byte(input))
if err != nil {
panic(err)
}
<-time.After(100 * time.Second)
}
}
Main function
package main
import (
"fmt"
"os"
"os/signal"
"syscall"
)
type Starter interface {
Start()
}
func main() {
var s Server
var c Client
go s.Start()
go c.Start()
sigs := make(chan os.Signal)
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
<-sigs
c.Exit <- struct{}{}
fmt.Println("exit")
}
Running package main will print :
client recv resp hi carryxyh
client recv resp carryxyh
Printed twice. But from the program point of view, the server only writes information to the client once, and the content of the information should be hi carryxyh. But the client printed carryxyh in addition to hi carryxyh, which made me very confused.
During the investigation, I accidentally modified a line of code, and the program immediately returned to normal:
modeifycode
As shown above, I modified the server response information: output := "hi "+ s => output := "hi ", at this time the program only prints client recv resp hi.
This makes me completely confused, can anyone help me solve this problem? It would be better if there is a troubleshooting idea.
enter image description here
这里你忽略了读取的字节长度,可能返回0字节,你又把buf的内容 又回写给client了.
_, err := conn.Read(buf) 改成 bytesRead, err := conn.Read(buf) ;
if bytesRead <= 0 { // 没有buf 可以读取 }
--
eg:
if bytesRead == 0 && err == nil {
err = io.EOF
log.Errorf("[network] ReadOnce maybe always return (0, nil) and causes dead loop, Connection = %d, Local Address = %+v, Remote Address = %+v",
c.id, c.rawConnection.LocalAddr(), c.RemoteAddr())
}
I am using golang net/smtp to send mails
Whenever I send to my smtp server I need to capture the response from the server
Especially the DSN
For example my local smtp server gives a "ok queued as " at the end of the mail
I need to capture this and print in the logs
How can I do this
package main
import (
"log"
"net/smtp"
)
func sendEmail(msg []byte) {
c, err := smtp.Dial("localhost:25")
if err != nil {
log.Fatal(err)
}
if err := c.Mail("sender#example.org"); err != nil {
log.Fatal(err)
}
if err := c.Rcpt("recipient#example.net"); err != nil {
log.Fatal(err)
}
wc, err := c.Data()
if err != nil {
log.Fatal(err)
}
_, err = wc.Write(msg)
if err != nil {
log.Fatal(err)
}
//How do I get the response here ??
err = wc.Close()
if err != nil {
log.Fatal(err)
}
err = c.Quit()
if err != nil {
log.Fatal(err)
}
}
As mentioned in the comments you can use c.Text.ReadResponse():
package main
import (
"net/smtp"
)
func sendEmail(msg []byte) (code int, message string, err error) {
c, err := smtp.Dial("localhost:25")
if err != nil {
return
}
defer c.Quit() // make sure to quit the Client
if err = c.Mail("sender#example.org"); err != nil {
return
}
if err = c.Rcpt("recipient#example.net"); err != nil {
return
}
wc, err := c.Data()
if err != nil {
return
}
defer wc.Close() // make sure WriterCloser gets closed
_, err = wc.Write(msg)
if err != nil {
return
}
code, message, err = c.Text.ReadResponse(0)
return
}
The code, message and any err are now passed to the caller, don't use log.Fatal throughout your code, handle the error on the calling side.
package main
import (
"net/smtp"
)
func sendEmail(msg []byte) (code int, message string, err error) {
c, err := smtp.Dial("localhost:25")
if err != nil {
return
}
defer c.Quit() // make sure to quit the Client
if err = c.Mail("sender#example.org"); err != nil {
return
}
if err = c.Rcpt("recipient#example.net"); err != nil {
return
}
wc, err := c.Data()
if err != nil {
return
}
_, err = wc.Write(msg)
if err != nil {
return
}
code, message, err = closeData(c)
if err != nil {
return 0, "", err
}
return code, message, err
}
func closeData(client *smtp.Client) error {
d := &dataCloser{
c: client,
WriteCloser: client.Text.DotWriter(),
}
return d.Close()
}
type dataCloser struct {
c *smtp.Client
io.WriteCloser
}
func (d *dataCloser) Close() (int, string, error) {
d.WriteCloser.Close() // make sure WriterCloser gets closed
code, message, err := d.c.Text.ReadResponse(250)
fmt.Printf("Message %v, Error %v\n", message, err)
return code, message, err
}
I am trying to learn Golang (and net pkg). I have written a simple client
and server. Client sends two messages in sequence and reads server msg.
Server reads two messages from a client and sends one message - this hand
shake is hanging. Could you suggest what I am missing? Thanks.
When client combine two messages into one and sends it (so the server reads
only once), this does not hang.
/* ======= Server start ==============================================*/
package main
import (
"fmt"
"net"
"os"
)
func processClients(cfd net.Conn) {
clientMsg := make([]byte, 128)
_, err := cfd.Read(clientMsg) // read client msg
if err != nil {
return
}
clientID := make([]byte, 10)
_, err = cfd.Read(clientID) // read client id
if err != nil {
return
}
clientMsg = append(clientMsg, clientID...) // TBD: handle len boundaries
fmt.Println("Server received msg:", string(clientMsg))
_, err = cfd.Write(clientMsg[0:len(clientMsg)]) // ack client msg
if err != nil {
return
}
}
func initServer() {
lfd, err := net.Listen("unix", "/tmp/unix.socket")
if err != nil {
fmt.Println("net.Listen error", err)
return
}
defer os.Remove("/tmp/unix.socket")
fmt.Println("Listening on /tmp/unix.socket")
for {
fd, err := lfd.Accept()
if err != nil {
fmt.Println("Accept() error", fd)
break
}
go processClients(fd)
}
}
func main() {
initServer()
}
/* ======= Server end ===============================================*/
/* ======= Client Start ===============================================*/
package main
import (
"fmt"
"net"
"sync"
)
var wg sync.WaitGroup
var clientID int
func sendReq() {
fd, err := net.Dial("unix", "/tmp/unix.socket")
if err != nil {
return
}
defer fd.Close()
clientID++
id := fmt.Sprintf("%v", clientID)
_, err = fd.Write([]byte(id)) //send client id
if err != nil {
return
}
_, err = fd.Write([]byte("Hello there, I am client")) // send client msg
if err != nil {
return
}
serverMsg := make([]byte, 128)
_, err = fd.Read(serverMsg) // Read server msg
if err != nil {
return
}
fmt.Println(string(serverMsg))
wg.Done()
}
func main() {
wg.Add(5)
go sendReq()
go sendReq()
go sendReq()
go sendReq()
go sendReq()
wg.Wait()
}
/* ======= Client end ===============================================*/
I'm working on porting some existing Python code to Go. One bit is in charge of sending emails through Exchange server (SMTP + STARTTLS). Existing (simplified) code looks like this:
import smtplib
client = smtplib.SMTP("exchangeserver.com")
client.starttls()
client.login('user', 'password')
client.sendmail('user#exchangeserver.com',
['otheruser1#exchangeserver.com', 'otheruser2#exchangeserver.com'],
'..message..')
I'd like to do the same thing with Go, help is appreciated - thanks.
Edited:
Here is an example using the tls connection setup. I don't have one to test against, so it fails on my end connecting, but I suspect this is what you need to make it work
package main
import (
"crypto/tls"
"fmt"
"log"
"net/smtp"
)
func main() {
var (
host = "smtp.google.com"
port = 587
from = "foo#bar.com"
password = "baz"
to = []string{"bin#bar.com"}
msg = []byte("This is my message")
auth = smtp.PlainAuth("", from, password, "smtp.gmail.com")
)
serverAddr := fmt.Sprintf("%s:%d", host, port)
conn, err := tls.Dial("tcp", serverAddr, nil)
if err != nil {
log.Printf("Error Dialing %s\n", err)
return
}
client, err := smtp.NewClient(conn, host)
if err != nil {
log.Printf("Error SMTP connection: %s\n", err)
return
}
if ok, _ := client.Extension("AUTH"); ok {
if err := client.Auth(auth); err != nil {
log.Printf("Error during AUTH %s\n", err)
return
}
}
if err := client.Mail(from); err != nil {
log.Printf("Error: %s\n", err)
return
}
for _, addr := range to {
if err := client.Rcpt(addr); err != nil {
log.Printf("Error: %s\n", err)
return
}
}
w, err := client.Data()
if err != nil {
log.Printf("Error: %s\n", err)
return
}
_, err = w.Write(msg)
if err != nil {
log.Printf("Error: %s\n", err)
return
}
err = w.Close()
if err != nil {
log.Printf("Error: %s\n", err)
return
}
client.Quit()
}
I think something like this might work. You'll have to make the appropriate changes for server, user/password, etc. Let me know if you need more help.
package main
import (
"fmt"
"log"
"net/smtp"
)
func main() {
to := "foo#foo.com"
from := "bin#baz.com"
password := "myPassword"
subject := "subject line of email"
msg := "a one-line email message"
emailTemplate := `To: %s
Subject: %s
%s
`
body := fmt.Sprintf(emailTemplate, to, subject, msg)
auth := smtp.PlainAuth("", from, password, "smtp.gmail.com")
err := smtp.SendMail(
"smtp.gmail.com:587",
auth,
from,
[]string{to},
[]byte(body),
)
if err != nil {
log.Fatal(err)
}
}
To expand on Cory LaNou's answer, you can use StartTLS on a normal net.Conn. You need to define a tls.Config and then use that to upgrade the connection to use TLS.
The example below is based on Cory's answer and also steals some code from the crypto/tls test files for creating the RSA cert and private key. In production you'd obviously replace these with real certs and keys.
It may require some customization of the variables, as well as some changes to the tls.Config to suit your environment.
package main
import (
"crypto/rsa"
"crypto/tls"
"encoding/hex"
"fmt"
"log"
"math/big"
"net"
"net/smtp"
)
func main() {
var (
host = "smtp.myexchange.com"
port = 587
from = "sender#example.org"
password = "password"
to = []string{"recipient#example.org"}
msg = []byte("This is my message")
auth = smtp.PlainAuth("", from, password, "smtp.myexchange.com")
)
conf := new(tls.Config)
conf.Certificates = make([]tls.Certificate, 1)
conf.Certificates[0].Certificate = [][]byte{testRSACertificate}
conf.Certificates[0].PrivateKey = testRSAPrivateKey
conf.CipherSuites = []uint16{tls.TLS_RSA_WITH_RC4_128_SHA}
conf.InsecureSkipVerify = true
conf.MinVersion = tls.VersionSSL30
conf.MaxVersion = tls.VersionTLS10
serverAddr := fmt.Sprintf("%s:%d", host, port)
conn, err := net.Dial("tcp", serverAddr)
if err != nil {
log.Printf("Error Dialing %s\n", err)
return
}
client, err := smtp.NewClient(conn, host)
if err != nil {
log.Printf("Error SMTP connection: %s\n", err)
return
}
if err = client.StartTLS(conf); err != nil {
log.Printf("Error performing StartTLS: %s\n", err)
return
}
if ok, _ := client.Extension("AUTH"); ok {
if err := client.Auth(auth); err != nil {
log.Printf("Error during AUTH %s\n", err)
return
}
}
if err := client.Mail(from); err != nil {
log.Printf("Error: %s\n", err)
return
}
for _, addr := range to {
if err := client.Rcpt(addr); err != nil {
log.Printf("Error: %s\n", err)
return
}
}
w, err := client.Data()
if err != nil {
log.Printf("Error: %s\n", err)
return
}
_, err = w.Write(msg)
if err != nil {
log.Printf("Error: %s\n", err)
return
}
err = w.Close()
if err != nil {
log.Printf("Error: %s\n", err)
return
}
client.Quit()
}
// Code below from http://golang.org/src/pkg/crypto/tls/handshake_server_test.go
func bigFromString(s string) *big.Int {
ret := new(big.Int)
ret.SetString(s, 10)
return ret
}
func fromHex(s string) []byte {
b, _ := hex.DecodeString(s)
return b
}
var testRSACertificate = fromHex("308202b030820219a00302010202090085b0bba48a7fb8ca300d06092a864886f70d01010505003045310b3009060355040613024155311330110603550408130a536f6d652d53746174653121301f060355040a1318496e7465726e6574205769646769747320507479204c7464301e170d3130303432343039303933385a170d3131303432343039303933385a3045310b3009060355040613024155311330110603550408130a536f6d652d53746174653121301f060355040a1318496e7465726e6574205769646769747320507479204c746430819f300d06092a864886f70d010101050003818d0030818902818100bb79d6f517b5e5bf4610d0dc69bee62b07435ad0032d8a7a4385b71452e7a5654c2c78b8238cb5b482e5de1f953b7e62a52ca533d6fe125c7a56fcf506bffa587b263fb5cd04d3d0c921964ac7f4549f5abfef427100fe1899077f7e887d7df10439c4a22edb51c97ce3c04c3b326601cfafb11db8719a1ddbdb896baeda2d790203010001a381a73081a4301d0603551d0e04160414b1ade2855acfcb28db69ce2369ded3268e18883930750603551d23046e306c8014b1ade2855acfcb28db69ce2369ded3268e188839a149a4473045310b3009060355040613024155311330110603550408130a536f6d652d53746174653121301f060355040a1318496e7465726e6574205769646769747320507479204c746482090085b0bba48a7fb8ca300c0603551d13040530030101ff300d06092a864886f70d010105050003818100086c4524c76bb159ab0c52ccf2b014d7879d7a6475b55a9566e4c52b8eae12661feb4f38b36e60d392fdf74108b52513b1187a24fb301dbaed98b917ece7d73159db95d31d78ea50565cd5825a2d5a5f33c4b6d8c97590968c0f5298b5cd981f89205ff2a01ca31b9694dda9fd57e970e8266d71999b266e3850296c90a7bdd9")
var testRSAPrivateKey = &rsa.PrivateKey{
PublicKey: rsa.PublicKey{
N: bigFromString("131650079503776001033793877885499001334664249354723305978524647182322416328664556247316495448366990052837680518067798333412266673813370895702118944398081598789828837447552603077848001020611640547221687072142537202428102790818451901395596882588063427854225330436740647715202971973145151161964464812406232198521"),
E: 65537,
},
D: bigFromString("29354450337804273969007277378287027274721892607543397931919078829901848876371746653677097639302788129485893852488285045793268732234230875671682624082413996177431586734171663258657462237320300610850244186316880055243099640544518318093544057213190320837094958164973959123058337475052510833916491060913053867729"),
Primes: []*big.Int{
bigFromString("11969277782311800166562047708379380720136961987713178380670422671426759650127150688426177829077494755200794297055316163155755835813760102405344560929062149"),
bigFromString("10998999429884441391899182616418192492905073053684657075974935218461686523870125521822756579792315215543092255516093840728890783887287417039645833477273829"),
},
}
Playground
Is it possible to implement an ICMP ping in Go? The alternative is to fork a 'ping' process, but I'd rather write it in Go.
The following code shows how to perform a ping over IPv4 using a raw socket (requires root privs):
package main
import (
"log"
"net"
"os"
"golang.org/x/net/icmp"
"golang.org/x/net/ipv4"
)
const targetIP = "8.8.8.8"
func main() {
c, err := icmp.ListenPacket("ip4:icmp", "0.0.0.0")
if err != nil {
log.Fatalf("listen err, %s", err)
}
defer c.Close()
wm := icmp.Message{
Type: ipv4.ICMPTypeEcho, Code: 0,
Body: &icmp.Echo{
ID: os.Getpid() & 0xffff, Seq: 1,
Data: []byte("HELLO-R-U-THERE"),
},
}
wb, err := wm.Marshal(nil)
if err != nil {
log.Fatal(err)
}
if _, err := c.WriteTo(wb, &net.IPAddr{IP: net.ParseIP(targetIP)}); err != nil {
log.Fatalf("WriteTo err, %s", err)
}
rb := make([]byte, 1500)
n, peer, err := c.ReadFrom(rb)
if err != nil {
log.Fatal(err)
}
rm, err := icmp.ParseMessage(ipv4.ICMPTypeEchoReply.Protocol(), rb[:n])
if err != nil {
log.Fatal(err)
}
switch rm.Type {
case ipv4.ICMPTypeEchoReply:
log.Printf("got reflection from %v", peer)
default:
log.Printf("got %+v; want echo reply", rm)
}
}
Code based on the example found here: https://godoc.org/golang.org/x/net/icmp#PacketConn
In order to ping from Linux as a non-privileged user, see this post
Currently, the ICMP Echo (Ping) function isn't supported in the Go net package.
There's no support for sending ICMP
echo requests. You'd have to add
support to package net. ping
To perform this without root requirement you can use
package main
import (
"fmt"
"net"
"os"
"time"
"golang.org/x/net/icmp"
"golang.org/x/net/ipv4"
)
const target = "google.com"
func main() {
for {
time.Sleep(time.Second * 1)
Ping(target)
}
}
func Ping(target string) {
ip, err := net.ResolveIPAddr("ip4", target)
if err != nil {
panic(err)
}
conn, err := icmp.ListenPacket("udp4", "0.0.0.0")
if err != nil {
fmt.Printf("Error on ListenPacket")
panic(err)
}
defer conn.Close()
msg := icmp.Message{
Type: ipv4.ICMPTypeEcho, Code: 0,
Body: &icmp.Echo{
ID: os.Getpid() & 0xffff, Seq: 1,
Data: []byte(""),
},
}
msg_bytes, err := msg.Marshal(nil)
if err != nil {
fmt.Printf("Error on Marshal %v", msg_bytes)
panic(err)
}
// Write the message to the listening connection
if _, err := conn.WriteTo(msg_bytes, &net.UDPAddr{IP: net.ParseIP(ip.String())}); err != nil {
fmt.Printf("Error on WriteTo %v", err)
panic(err)
}
err = conn.SetReadDeadline(time.Now().Add(time.Second * 1))
if err != nil {
fmt.Printf("Error on SetReadDeadline %v", err)
panic(err)
}
reply := make([]byte, 1500)
n, _, err := conn.ReadFrom(reply)
if err != nil {
fmt.Printf("Error on ReadFrom %v", err)
panic(err)
}
parsed_reply, err := icmp.ParseMessage(1, reply[:n])
if err != nil {
fmt.Printf("Error on ParseMessage %v", err)
panic(err)
}
switch parsed_reply.Code {
case 0:
// Got a reply so we can save this
fmt.Printf("Got Reply from %s\n", target)
case 3:
fmt.Printf("Host %s is unreachable\n", target)
// Given that we don't expect google to be unreachable, we can assume that our network is down
case 11:
// Time Exceeded so we can assume our network is slow
fmt.Printf("Host %s is slow\n", target)
default:
// We don't know what this is so we can assume it's unreachable
fmt.Printf("Host %s is unreachable\n", target)
}
}