Created
January 5, 2017 08:47
-
-
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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