summaryrefslogtreecommitdiff
path: root/pysnmp/carrier/sockmsg.py
diff options
context:
space:
mode:
Diffstat (limited to 'pysnmp/carrier/sockmsg.py')
-rw-r--r--pysnmp/carrier/sockmsg.py91
1 files changed, 91 insertions, 0 deletions
diff --git a/pysnmp/carrier/sockmsg.py b/pysnmp/carrier/sockmsg.py
new file mode 100644
index 0000000..d0666eb
--- /dev/null
+++ b/pysnmp/carrier/sockmsg.py
@@ -0,0 +1,91 @@
+#
+# The following routines act like sendto()/recvfrom() calls but additionally
+# support local address retrieval (what can be useful when listening on
+# 0.0.0.0 or [::]) and source address spoofing (for transparent proxying).
+#
+# These routines are based on POSIX sendmsg()/recvmsg() calls which were made
+# available since Python 3.3. Therefore this module is only Python 3.x
+# compatible.
+#
+# Parts of the code below is taken from:
+# http://carnivore.it/2012/10/12/python3.3_sendmsg_and_recvmsg
+#
+try:
+ import ctypes
+ import ipaddress
+except ImportError:
+ def getRecvFrom(addressType):
+ raise error.CarrierError('sendmsg()/recvmsg() interface is not supported by this OS and/or Python version')
+
+ def getSendTo(addressType):
+ raise error.CarrierError('sendmsg()/recvmsg() interface is not supported by this OS and/or Python version')
+else:
+ import sys
+ import socket
+ from pysnmp import debug
+ from pysnmp.carrier import sockfix, error
+
+ uint32_t = ctypes.c_uint32
+ in_addr_t = uint32_t
+
+ class in_addr(ctypes.Structure):
+ _fields_ = [('s_addr', in_addr_t)]
+
+ class in6_addr_U(ctypes.Union):
+ _fields_ = [
+ ('__u6_addr8', ctypes.c_uint8 * 16),
+ ('__u6_addr16', ctypes.c_uint16 * 8),
+ ('__u6_addr32', ctypes.c_uint32 * 4),
+ ]
+
+ class in6_addr(ctypes.Structure):
+ _fields_ = [
+ ('__in6_u', in6_addr_U),
+ ]
+
+ class in_pktinfo(ctypes.Structure):
+ _fields_ = [
+ ('ipi_ifindex', ctypes.c_int),
+ ('ipi_spec_dst', in_addr),
+ ('ipi_addr', in_addr),
+ ]
+
+ class in6_pktinfo(ctypes.Structure):
+ _fields_ = [
+ ('ipi6_addr', in6_addr),
+ ('ipi6_ifindex', ctypes.c_uint),
+ ]
+
+ def getRecvFrom(addressType):
+ def recvfrom(s, sz):
+ _to = None
+ data, ancdata, msg_flags, _from = s.recvmsg(sz, socket.CMSG_LEN(sz))
+ for anc in ancdata:
+ if anc[0] == socket.SOL_IP and anc[1] == socket.IP_PKTINFO:
+ addr = in_pktinfo.from_buffer_copy(anc[2])
+ addr = ipaddress.IPv4Address(memoryview(addr.ipi_addr).tobytes())
+ _to = (str(addr),s.getsockname()[1])
+ elif anc[0] == socket.SOL_IPV6 and anc[1] == socket.IPV6_PKTINFO:
+ addr = in6_pktinfo.from_buffer_copy(anc[2])
+ addr = ipaddress.ip_address(memoryview(addr.ipi6_addr).tobytes())
+ _to = (str(addr),s.getsockname()[1])
+ return data, addressType(_from).setLocalAddress(_to)
+ return recvfrom
+
+ def getSendTo(addressType):
+ def sendto(s, _data, _to):
+ ancdata = []
+ if type(_to) == addressType:
+ addr = ipaddress.ip_address(_to.getLocalAddress()[0])
+ else:
+ addr = ipaddress.ip_address(s.getsockname()[0])
+ if type(addr) == ipaddress.IPv4Address:
+ _f = in_pktinfo()
+ _f.ipi_spec_dst = in_addr.from_buffer_copy(addr.packed)
+ ancdata = [(socket.SOL_IP, socket.IP_PKTINFO, memoryview(_f).tobytes())]
+ elif s.family == socket.AF_INET6 and type(addr) == ipaddress.IPv6Address:
+ _f = in6_pktinfo()
+ _f.ipi6_addr = in6_addr.from_buffer_copy(addr.packed)
+ ancdata = [(socket.SOL_IPV6, socket.IPV6_PKTINFO, memoryview(_f).tobytes())]
+ return s.sendmsg([_data], ancdata, 0, _to)
+ return sendto