Skip to content

Instantly share code, notes, and snippets.

@irumiha
Created January 5, 2017 08:47
Show Gist options
  • Save irumiha/525f37258e0bd569a73e4efed207abbe to your computer and use it in GitHub Desktop.
Save irumiha/525f37258e0bd569a73e4efed207abbe to your computer and use it in GitHub Desktop.
PermissiveUDPConn is a UDP connection that allows responses to come from a different IP address than the one a packet was sent to.
package snmp
import (
"net"
"os"
"syscall"
"time"
)
// PermissiveUDPConn is a UDP connection that allows responses to come from
// a different IP address than the one a packet was sent to.
type PermissiveUDPConn struct {
conn net.PacketConn
timeout time.Duration
remoteAddr net.UDPAddr
sckFile *os.File
sck int
}
// Read reads data from the connection.
// Read can be made to time out and return a Error with Timeout() == true
// after a fixed time limit; see SetDeadline and SetReadDeadline.
func (c *PermissiveUDPConn) Read(b []byte) (n int, err error) {
c.conn.SetReadDeadline(time.Now().Add(c.timeout))
n, _, err = c.conn.ReadFrom(b)
return
}
// Write writes data to the connection.
// Write can be made to time out and return a Error with Timeout() == true
// after a fixed time limit; see SetDeadline and SetWriteDeadline.
func (c *PermissiveUDPConn) Write(b []byte) (n int, err error) {
return c.conn.WriteTo(b, &c.remoteAddr)
}
// Close closes the connection.
// Any blocked Read or Write operations will be unblocked and return errors.
func (c *PermissiveUDPConn) Close() error {
c.sckFile.Close()
return c.conn.Close()
}
// LocalAddr returns the local network address.
func (c *PermissiveUDPConn) LocalAddr() net.Addr {
return c.conn.LocalAddr()
}
// RemoteAddr returns the remote network address.
func (c *PermissiveUDPConn) RemoteAddr() net.Addr {
return &c.remoteAddr
}
// SetDeadline sets the read and write deadlines associated
// with the connection. It is equivalent to calling both
// SetReadDeadline and SetWriteDeadline.
//
// A deadline is an absolute time after which I/O operations
// fail with a timeout (see type Error) instead of
// blocking. The deadline applies to all future I/O, not just
// the immediately following call to Read or Write.
//
// An idle timeout can be implemented by repeatedly extending
// the deadline after successful Read or Write calls.
//
// A zero value for t means I/O operations will not time out.
func (c *PermissiveUDPConn) SetDeadline(t time.Time) error {
return c.conn.SetWriteDeadline(t)
}
// SetReadDeadline sets the deadline for future Read calls.
// A zero value for t means Read will not time out.
func (c *PermissiveUDPConn) SetReadDeadline(t time.Time) error {
return c.conn.SetReadDeadline(t)
}
// SetWriteDeadline sets the deadline for future Write calls.
// Even if write times out, it may return n > 0, indicating that
// some of the data was successfully written.
// A zero value for t means Write will not time out.
func (c *PermissiveUDPConn) SetWriteDeadline(t time.Time) error {
return c.conn.SetWriteDeadline(t)
}
// PermissiveUDPDialTimeout initiates a connection to the target host
func PermissiveUDPDialTimeout(
addr string,
timeout time.Duration,
) (*PermissiveUDPConn, error) {
conn := &PermissiveUDPConn{}
conn.timeout = timeout
raddr, err := net.ResolveUDPAddr("udp4", addr)
if err != nil {
return nil, err
}
conn.remoteAddr = *raddr
sck, err := sysSocket(syscall.AF_INET, syscall.SOCK_DGRAM, syscall.IPPROTO_IP)
if err != nil {
return nil, err
}
conn.sck = sck
conn.sckFile = os.NewFile(uintptr(sck), "socket")
oc, err := net.FilePacketConn(conn.sckFile)
if err != nil {
return nil, err
}
conn.conn = oc
return conn, nil
}
// copy from fd_unix.go - this is different for windows...
func closesocket(s int) error {
return syscall.Close(s)
}
func sysSocket(family, sotype, proto int) (int, error) {
// See ../syscall/exec_unix.go for description of ForkLock.
syscall.ForkLock.RLock()
s, err := syscall.Socket(family, sotype, proto)
if err == nil {
syscall.CloseOnExec(s)
}
syscall.ForkLock.RUnlock()
if err != nil {
return -1, os.NewSyscallError("socket", err)
}
if err = syscall.SetNonblock(s, true); err != nil {
syscall.Close(s)
return -1, os.NewSyscallError("setnonblock", err)
}
return s, nil
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment