Skip to content

Commit 5e0b53d

Browse files
authored
An attempt to emulate port binding on raw UDP sockets (#45)
1 parent e9db7fb commit 5e0b53d

File tree

1 file changed

+30
-20
lines changed

1 file changed

+30
-20
lines changed

multicast-relay.py

Lines changed: 30 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,8 @@ def __init__(self, interfaces, noTransmitInterfaces, ifFilter, waitForIP, ttl,
202202

203203
self.recentChecksums = []
204204

205+
self.bindings = set()
206+
205207
self.listenAddr = listen
206208
self.listenSock = None
207209
if remote:
@@ -313,6 +315,7 @@ def addListener(self, addr, port, service):
313315
if self.isMulticast(addr):
314316
rx.bind((addr, port))
315317
self.receivers.append(rx)
318+
self.bindings.add((addr, port))
316319

317320
@staticmethod
318321
def unicastIpToMac(ip, procNetArp=None):
@@ -461,6 +464,9 @@ def transmitPacket(self, sock, srcMac, destMac, ipHeaderLength, ipPacket):
461464
else:
462465
self.logger.info('Error sending packet: %s' % str(e))
463466

467+
def match(self, addr, port):
468+
return ((addr, port)) in self.bindings
469+
464470
def loop(self):
465471
# Record where the most recent SSDP searches came from, to relay unicast answers
466472
# Note: ideally we'd be more clever and record multiple, but in practice
@@ -532,26 +538,6 @@ def loop(self):
532538
(data, addr) = s.recvfrom(10240)
533539
addr = addr[0]
534540

535-
if self.remoteSockets() and not (receivingInterface == 'remote' and self.noRemoteRelay):
536-
packet = self.aes.encrypt(self.MAGIC + socket.inet_aton(addr) + data)
537-
for remoteConnection in self.remoteSockets():
538-
if remoteConnection == s:
539-
continue
540-
try:
541-
remoteConnection.sendall(struct.pack('!H', len(packet)) + packet)
542-
543-
for remote in self.remoteAddrs:
544-
if remote['socket'] == remoteConnection and remote['connecting']:
545-
self.logger.info('REMOTE: Connection to %s established' % remote['addr'])
546-
remote['connecting'] = False
547-
except socket.error as e:
548-
if e.errno == errno.EAGAIN:
549-
pass
550-
else:
551-
self.logger.info('REMOTE: Failed to connect to %s: %s' % (self.remoteAddr, str(e)))
552-
self.removeConnection(remoteConnection)
553-
continue
554-
555541
eighthDataByte = data[8]
556542
if sys.version_info > (3, 0):
557543
eighthDataByte = bytes([data[8]])
@@ -585,6 +571,30 @@ def loop(self):
585571
srcPort = struct.unpack('!H', data[ipHeaderLength+0:ipHeaderLength+2])[0]
586572
dstPort = struct.unpack('!H', data[ipHeaderLength+2:ipHeaderLength+4])[0]
587573

574+
# raw sockets cannot be bound to a specific port, so we receive all UDP packets with matching dstAddr
575+
if receivingInterface == 'local' and not self.match(dstAddr, dstPort):
576+
continue
577+
578+
if self.remoteSockets() and not (receivingInterface == 'remote' and self.noRemoteRelay):
579+
packet = self.aes.encrypt(self.MAGIC + socket.inet_aton(addr) + data)
580+
for remoteConnection in self.remoteSockets():
581+
if remoteConnection == s:
582+
continue
583+
try:
584+
remoteConnection.sendall(struct.pack('!H', len(packet)) + packet)
585+
586+
for remote in self.remoteAddrs:
587+
if remote['socket'] == remoteConnection and remote['connecting']:
588+
self.logger.info('REMOTE: Connection to %s established' % remote['addr'])
589+
remote['connecting'] = False
590+
except socket.error as e:
591+
if e.errno == errno.EAGAIN:
592+
pass
593+
else:
594+
self.logger.info('REMOTE: Failed to connect to %s: %s' % (self.remoteAddr, str(e)))
595+
self.removeConnection(remoteConnection)
596+
continue
597+
588598
origSrcAddr = srcAddr
589599
origSrcPort = srcPort
590600
origDstAddr = dstAddr

0 commit comments

Comments
 (0)