Commit 9897e733 authored by Juan Batiz-Benet's avatar Juan Batiz-Benet

initial thing works. needs tests.

parents
// +build darwin freebsd dragonfly netbsd openbsd
package reuseport
import (
unix "golang.org/x/sys/unix"
)
var soReusePort = unix.SO_REUSEPORT
var soReuseAddr = unix.SO_REUSEADDR
// +build linux
package reuseport
import (
unix "golang.org/x/sys/unix"
)
var soReusePort = 15 // this is not defined in unix go pkg.
var soReuseAddr = unix.SO_REUSEADDR
// +build darwin freebsd dragonfly netbsd openbsd linux
package reuseport
import (
"fmt"
"net"
"os"
"strconv"
"syscall"
resolve "github.com/jbenet/go-net-resolve-addr"
sockaddrnet "github.com/jbenet/go-sockaddr/net"
)
const (
tcp4 = 52 // "4"
tcp6 = 54 // "6"
filePrefix = "port."
)
func dial(dialer net.Dialer, netw, addr string) (c net.Conn, err error) {
var (
family, fd int
file *os.File
remoteSockaddr syscall.Sockaddr
localSockaddr syscall.Sockaddr
)
netAddr, err := resolve.ResolveAddr("dial", netw, addr)
if err != nil {
fmt.Println("resolve addr failed")
return nil, err
}
fmt.Println("resolve addr ok")
switch netAddr.(type) {
case *net.TCPAddr, *net.UDPAddr:
default:
return nil, ErrUnsupportedProtocol
}
switch dialer.LocalAddr.(type) {
case *net.TCPAddr, *net.UDPAddr:
default:
return nil, ErrUnsupportedProtocol
}
family = sockaddrnet.NetAddrAF(netAddr)
localSockaddr = sockaddrnet.NetAddrToSockaddr(dialer.LocalAddr)
remoteSockaddr = sockaddrnet.NetAddrToSockaddr(netAddr)
if fd, err = syscall.Socket(family, syscall.SOCK_STREAM, syscall.IPPROTO_TCP); err != nil {
fmt.Println("tcp socket failed")
return nil, err
}
if err = syscall.SetsockoptInt(fd, syscall.SOL_SOCKET, soReuseAddr, 1); err != nil {
fmt.Println("reuse addr failed")
return nil, err
}
if err = syscall.SetsockoptInt(fd, syscall.SOL_SOCKET, soReusePort, 1); err != nil {
fmt.Println("reuse port failed")
return nil, err
}
if localSockaddr != nil {
if err = syscall.Bind(fd, localSockaddr); err != nil {
fmt.Println("bind failed")
return nil, err
}
}
// Set backlog size to the maximum
if err = syscall.Connect(fd, remoteSockaddr); err != nil {
fmt.Println("connect failed")
return nil, err
}
// File Name get be nil
file = os.NewFile(uintptr(fd), filePrefix+strconv.Itoa(os.Getpid()))
if c, err = net.FileConn(file); err != nil {
return nil, err
}
if err = file.Close(); err != nil {
return nil, err
}
return c, err
}
func listen(netw, addr string) (l net.Listener, err error) {
var (
family, fd int
file *os.File
sockaddr syscall.Sockaddr
)
netAddr, err := resolve.ResolveAddr("listen", netw, addr)
if err != nil {
fmt.Println("resolve addr failed")
return nil, err
}
fmt.Println("resolve addr ok")
switch netAddr.(type) {
case *net.TCPAddr, *net.UDPAddr:
default:
return nil, ErrUnsupportedProtocol
}
family = sockaddrnet.NetAddrAF(netAddr)
sockaddr = sockaddrnet.NetAddrToSockaddr(netAddr)
if fd, err = syscall.Socket(family, syscall.SOCK_STREAM, syscall.IPPROTO_TCP); err != nil {
fmt.Println("socket failed")
return nil, err
}
if err = syscall.SetsockoptInt(fd, syscall.SOL_SOCKET, soReusePort, 1); err != nil {
fmt.Println("setsockopt reuseport failed")
return nil, err
}
if err = syscall.SetsockoptInt(fd, syscall.SOL_SOCKET, soReuseAddr, 1); err != nil {
fmt.Println("setsockopt reuseaddr failed")
return nil, err
}
if err = syscall.Bind(fd, sockaddr); err != nil {
fmt.Println("bind failed")
return nil, err
}
// Set backlog size to the maximum
if err = syscall.Listen(fd, syscall.SOMAXCONN); err != nil {
fmt.Println("listen failed")
return nil, err
}
// File Name get be nil
file = os.NewFile(uintptr(fd), filePrefix+strconv.Itoa(os.Getpid()))
if l, err = net.FileListener(file); err != nil {
return nil, err
}
if err = file.Close(); err != nil {
return nil, err
}
return l, err
}
package reuseport
import (
"net"
)
// TODO. for now, just pass it over to net.Listen/net.Dial
func listen(network, address string) (net.Listener, error) {
return net.Listen(network, address)
}
func dial(dialer net.Dialer, network, address string) (net.Conn, error) {
return dialer.Dial(network, address)
}
package reuseport
import (
"errors"
"net"
)
// ErrUnsuportedProtocol signals that the protocol is not currently
// supported by this package. This package currently only supports TCP.
var ErrUnsupportedProtocol = errors.New("protocol not yet supported")
// ErrReuseFailed is returned if a reuse attempt was unsuccessful.
var ErrReuseFailed = errors.New("protocol not yet supported")
// Listen listens at the given network and address. see net.Listen
// Returns a net.Listener created from a file discriptor for a socket
// with SO_REUSEPORT and SO_REUSEADDR option set.
func Listen(network, address string) (net.Listener, error) {
return listen(network, address)
}
// Dialer is used to specify the Dial options, much like net.Dialer.
// We simply wrap a net.Dialer.
type Dialer struct {
D net.Dialer
}
// Dial dials the given network and address. see net.Dialer.Dial
// Returns a net.Conn created from a file discriptor for a socket
// with SO_REUSEPORT and SO_REUSEADDR option set.
func (d *Dialer) Dial(network, address string) (net.Conn, error) {
return dial(d.D, network, address)
}
package main
import (
"fmt"
"io"
"net"
"os"
resolve "github.com/jbenet/go-net-resolve-addr"
reuse "github.com/jbenet/go-reuseport"
)
func main() {
l1, err := reuse.Listen("tcp", "0.0.0.0:11111")
maybeDie(err)
fmt.Printf("listening on %s\n", l1.Addr())
l2, err := reuse.Listen("tcp", "0.0.0.0:22222")
maybeDie(err)
fmt.Printf("listening on %s\n", l2.Addr())
a1, err := resolve.ResolveAddr("dial", "tcp", "127.0.0.1:11111")
maybeDie(err)
a3, err := resolve.ResolveAddr("dial", "tcp", "127.0.0.1:33333")
maybeDie(err)
d1 := reuse.Dialer{net.Dialer{LocalAddr: a1}}
d2 := reuse.Dialer{net.Dialer{LocalAddr: a3}}
go func() {
l2to1foo, err := l2.Accept()
maybeDie(err)
fmt.Printf("%s accepted conn from %s\n", addrStr(l2.Addr()), addrStr(l2to1foo.RemoteAddr()))
fmt.Println("safe")
l1to2bar, err := l1.Accept()
maybeDie(err)
fmt.Printf("%s accepted conn from %s\n", addrStr(l1.Addr()), addrStr(l1to2bar.RemoteAddr()))
io.Copy(l1to2bar, l2to1foo)
}()
d1to2foo, err := d1.Dial("tcp4", "127.0.0.1:22222")
maybeDie(err)
fmt.Printf("dialing from %s to %s\n", d1.D.LocalAddr, "127.0.0.1:22222")
d2to1bar, err := d2.Dial("tcp4", "127.0.0.1:11111")
maybeDie(err)
fmt.Printf("dialing from %s to %s\n", d2.D.LocalAddr, "127.0.0.1:11111")
go io.Copy(d1to2foo, os.Stdin)
io.Copy(os.Stdout, d2to1bar)
}
func die(err error) {
fmt.Fprintf(os.Stderr, "%s\n", err)
os.Exit(-1)
}
func maybeDie(err error) {
if err != nil {
die(err)
}
}
func addrStr(a net.Addr) string {
return fmt.Sprintf("%s/%s", a.Network(), a)
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment