1/**********
2This library is free software; you can redistribute it and/or modify it under
3the terms of the GNU Lesser General Public License as published by the
4Free Software Foundation; either version 3 of the License, or (at your
5option) any later version. (See <http://www.gnu.org/copyleft/lesser.html>.)
6
7This library is distributed in the hope that it will be useful, but WITHOUT
8ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
9FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
10more details.
11
12You should have received a copy of the GNU Lesser General Public License
13along with this library; if not, write to the Free Software Foundation, Inc.,
1451 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
15**********/
16// Copyright (c) 1996-2020 Live Networks, Inc. All rights reserved.
17// 'Group sockets'
18// Implementation
19
20#include "Groupsock.hh"
21#include "GroupsockHelper.hh"
22//##### Eventually fix the following #include; we shouldn't know about tunnels
23#include "TunnelEncaps.hh"
24
25#ifndef NO_SSTREAM
26#include <sstream>
27#endif
28#include <stdio.h>
29
30///////// OutputSocket //////////
31
32OutputSocket::OutputSocket(UsageEnvironment& env)
33 : Socket(env, 0 /* let kernel choose port */),
34 fSourcePort(0), fLastSentTTL(256/*hack: a deliberately invalid value*/) {
35}
36
37OutputSocket::OutputSocket(UsageEnvironment& env, Port port)
38 : Socket(env, port),
39 fSourcePort(0), fLastSentTTL(256/*hack: a deliberately invalid value*/) {
40}
41
42OutputSocket::~OutputSocket() {
43}
44
45Boolean OutputSocket::write(netAddressBits address, portNumBits portNum, u_int8_t ttl,
46 unsigned char* buffer, unsigned bufferSize) {
47 struct in_addr destAddr; destAddr.s_addr = address;
48 if ((unsigned)ttl == fLastSentTTL) {
49 // Optimization: Don't do a 'set TTL' system call again
50 if (!writeSocket(env(), socketNum(), destAddr, portNum, buffer, bufferSize)) return False;
51 } else {
52 if (!writeSocket(env(), socketNum(), destAddr, portNum, ttl, buffer, bufferSize)) return False;
53 fLastSentTTL = (unsigned)ttl;
54 }
55
56 if (sourcePortNum() == 0) {
57 // Now that we've sent a packet, we can find out what the
58 // kernel chose as our ephemeral source port number:
59 if (!getSourcePort(env(), socketNum(), fSourcePort)) {
60 if (DebugLevel >= 1)
61 env() << *this
62 << ": failed to get source port: "
63 << env().getResultMsg() << "\n";
64 return False;
65 }
66 }
67
68 return True;
69}
70
71// By default, we don't do reads:
72Boolean OutputSocket
73::handleRead(unsigned char* /*buffer*/, unsigned /*bufferMaxSize*/,
74 unsigned& /*bytesRead*/, struct sockaddr_in& /*fromAddressAndPort*/) {
75 return True;
76}
77
78
79///////// destRecord //////////
80
81destRecord
82::destRecord(struct in_addr const& addr, Port const& port, u_int8_t ttl, unsigned sessionId,
83 destRecord* next)
84 : fNext(next), fGroupEId(addr, port.num(), ttl), fSessionId(sessionId) {
85}
86
87destRecord::~destRecord() {
88 delete fNext;
89}
90
91
92///////// Groupsock //////////
93
94NetInterfaceTrafficStats Groupsock::statsIncoming;
95NetInterfaceTrafficStats Groupsock::statsOutgoing;
96NetInterfaceTrafficStats Groupsock::statsRelayedIncoming;
97NetInterfaceTrafficStats Groupsock::statsRelayedOutgoing;
98
99// Constructor for a source-independent multicast group
100Groupsock::Groupsock(UsageEnvironment& env, struct in_addr const& groupAddr,
101 Port port, u_int8_t ttl)
102 : OutputSocket(env, port),
103 deleteIfNoMembers(False), isSlave(False),
104 fDests(new destRecord(groupAddr, port, ttl, 0, NULL)),
105 fIncomingGroupEId(groupAddr, port.num(), ttl) {
106
107 if (!socketJoinGroup(env, socketNum(), groupAddr.s_addr)) {
108 if (DebugLevel >= 1) {
109 env << *this << ": failed to join group: "
110 << env.getResultMsg() << "\n";
111 }
112 }
113
114 // Make sure we can get our source address:
115 if (ourIPAddress(env) == 0) {
116 if (DebugLevel >= 0) { // this is a fatal error
117 env << "Unable to determine our source address: "
118 << env.getResultMsg() << "\n";
119 }
120 }
121
122 if (DebugLevel >= 2) env << *this << ": created\n";
123}
124
125// Constructor for a source-specific multicast group
126Groupsock::Groupsock(UsageEnvironment& env, struct in_addr const& groupAddr,
127 struct in_addr const& sourceFilterAddr,
128 Port port)
129 : OutputSocket(env, port),
130 deleteIfNoMembers(False), isSlave(False),
131 fDests(new destRecord(groupAddr, port, 255, 0, NULL)),
132 fIncomingGroupEId(groupAddr, sourceFilterAddr, port.num()) {
133 // First try a SSM join. If that fails, try a regular join:
134 if (!socketJoinGroupSSM(env, socketNum(), groupAddr.s_addr,
135 sourceFilterAddr.s_addr)) {
136 if (DebugLevel >= 3) {
137 env << *this << ": SSM join failed: "
138 << env.getResultMsg();
139 env << " - trying regular join instead\n";
140 }
141 if (!socketJoinGroup(env, socketNum(), groupAddr.s_addr)) {
142 if (DebugLevel >= 1) {
143 env << *this << ": failed to join group: "
144 << env.getResultMsg() << "\n";
145 }
146 }
147 }
148
149 if (DebugLevel >= 2) env << *this << ": created\n";
150}
151
152Groupsock::~Groupsock() {
153 if (isSSM()) {
154 if (!socketLeaveGroupSSM(env(), socketNum(), groupAddress().s_addr,
155 sourceFilterAddress().s_addr)) {
156 socketLeaveGroup(env(), socketNum(), groupAddress().s_addr);
157 }
158 } else {
159 socketLeaveGroup(env(), socketNum(), groupAddress().s_addr);
160 }
161
162 delete fDests;
163
164 if (DebugLevel >= 2) env() << *this << ": deleting\n";
165}
166
167destRecord* Groupsock
168::createNewDestRecord(struct in_addr const& addr, Port const& port, u_int8_t ttl,
169 unsigned sessionId, destRecord* next) {
170 // Default implementation:
171 return new destRecord(addr, port, ttl, sessionId, next);
172}
173
174void
175Groupsock::changeDestinationParameters(struct in_addr const& newDestAddr,
176 Port newDestPort, int newDestTTL, unsigned sessionId) {
177 destRecord* dest;
178 for (dest = fDests; dest != NULL && dest->fSessionId != sessionId; dest = dest->fNext) {}
179
180 if (dest == NULL) { // There's no existing 'destRecord' for this "sessionId"; add a new one:
181 fDests = createNewDestRecord(newDestAddr, newDestPort, newDestTTL, sessionId, fDests);
182 return;
183 }
184
185 // "dest" is an existing 'destRecord' for this "sessionId"; change its values to the new ones:
186 struct in_addr destAddr = dest->fGroupEId.groupAddress();
187 if (newDestAddr.s_addr != 0) {
188 if (newDestAddr.s_addr != destAddr.s_addr
189 && IsMulticastAddress(newDestAddr.s_addr)) {
190 // If the new destination is a multicast address, then we assume that
191 // we want to join it also. (If this is not in fact the case, then
192 // call "multicastSendOnly()" afterwards.)
193 socketLeaveGroup(env(), socketNum(), destAddr.s_addr);
194 socketJoinGroup(env(), socketNum(), newDestAddr.s_addr);
195 }
196 destAddr.s_addr = newDestAddr.s_addr;
197 }
198
199 portNumBits destPortNum = dest->fGroupEId.portNum();
200 if (newDestPort.num() != 0) {
201 if (newDestPort.num() != destPortNum
202 && IsMulticastAddress(destAddr.s_addr)) {
203 // Also bind to the new port number:
204 changePort(newDestPort);
205 // And rejoin the multicast group:
206 socketJoinGroup(env(), socketNum(), destAddr.s_addr);
207 }
208 destPortNum = newDestPort.num();
209 }
210
211 u_int8_t destTTL = ttl();
212 if (newDestTTL != ~0) destTTL = (u_int8_t)newDestTTL;
213
214 dest->fGroupEId = GroupEId(destAddr, destPortNum, destTTL);
215
216 // Finally, remove any other 'destRecord's that might also have this "sessionId":
217 removeDestinationFrom(dest->fNext, sessionId);
218}
219
220unsigned Groupsock
221::lookupSessionIdFromDestination(struct sockaddr_in const& destAddrAndPort) const {
222 destRecord* dest = lookupDestRecordFromDestination(destAddrAndPort);
223 if (dest == NULL) return 0;
224
225 return dest->fSessionId;
226}
227
228void Groupsock::addDestination(struct in_addr const& addr, Port const& port, unsigned sessionId) {
229 // Default implementation:
230 // If there's no existing 'destRecord' with the same "addr", "port", and "sessionId", add a new one:
231 for (destRecord* dest = fDests; dest != NULL; dest = dest->fNext) {
232 if (sessionId == dest->fSessionId
233 && addr.s_addr == dest->fGroupEId.groupAddress().s_addr
234 && port.num() == dest->fGroupEId.portNum()) {
235 return;
236 }
237 }
238
239 fDests = createNewDestRecord(addr, port, 255, sessionId, fDests);
240}
241
242void Groupsock::removeDestination(unsigned sessionId) {
243 // Default implementation:
244 removeDestinationFrom(fDests, sessionId);
245}
246
247void Groupsock::removeAllDestinations() {
248 delete fDests; fDests = NULL;
249}
250
251void Groupsock::multicastSendOnly() {
252 // We disable this code for now, because - on some systems - leaving the multicast group seems to cause sent packets
253 // to not be received by other applications (at least, on the same host).
254#if 0
255 socketLeaveGroup(env(), socketNum(), fIncomingGroupEId.groupAddress().s_addr);
256 for (destRecord* dests = fDests; dests != NULL; dests = dests->fNext) {
257 socketLeaveGroup(env(), socketNum(), dests->fGroupEId.groupAddress().s_addr);
258 }
259#endif
260}
261
262Boolean Groupsock::output(UsageEnvironment& env, unsigned char* buffer, unsigned bufferSize,
263 DirectedNetInterface* interfaceNotToFwdBackTo) {
264 do {
265 // First, do the datagram send, to each destination:
266 Boolean writeSuccess = True;
267 for (destRecord* dests = fDests; dests != NULL; dests = dests->fNext) {
268 if (!write(dests->fGroupEId.groupAddress().s_addr, dests->fGroupEId.portNum(), dests->fGroupEId.ttl(),
269 buffer, bufferSize)) {
270 writeSuccess = False;
271 break;
272 }
273 }
274 if (!writeSuccess) break;
275 statsOutgoing.countPacket(bufferSize);
276 statsGroupOutgoing.countPacket(bufferSize);
277
278 // Then, forward to our members:
279 int numMembers = 0;
280 if (!members().IsEmpty()) {
281 numMembers =
282 outputToAllMembersExcept(interfaceNotToFwdBackTo,
283 ttl(), buffer, bufferSize,
284 ourIPAddress(env));
285 if (numMembers < 0) break;
286 }
287
288 if (DebugLevel >= 3) {
289 env << *this << ": wrote " << bufferSize << " bytes, ttl " << (unsigned)ttl();
290 if (numMembers > 0) {
291 env << "; relayed to " << numMembers << " members";
292 }
293 env << "\n";
294 }
295 return True;
296 } while (0);
297
298 if (DebugLevel >= 0) { // this is a fatal error
299 UsageEnvironment::MsgString msg = strDup(env.getResultMsg());
300 env.setResultMsg("Groupsock write failed: ", msg);
301 delete[] (char*)msg;
302 }
303 return False;
304}
305
306Boolean Groupsock::handleRead(unsigned char* buffer, unsigned bufferMaxSize,
307 unsigned& bytesRead,
308 struct sockaddr_in& fromAddressAndPort) {
309 // Read data from the socket, and relay it across any attached tunnels
310 //##### later make this code more general - independent of tunnels
311
312 bytesRead = 0;
313
314 int maxBytesToRead = bufferMaxSize - TunnelEncapsulationTrailerMaxSize;
315 int numBytes = readSocket(env(), socketNum(),
316 buffer, maxBytesToRead, fromAddressAndPort);
317 if (numBytes < 0) {
318 if (DebugLevel >= 0) { // this is a fatal error
319 UsageEnvironment::MsgString msg = strDup(env().getResultMsg());
320 env().setResultMsg("Groupsock read failed: ", msg);
321 delete[] (char*)msg;
322 }
323 return False;
324 }
325
326 // If we're a SSM group, make sure the source address matches:
327 if (isSSM()
328 && fromAddressAndPort.sin_addr.s_addr != sourceFilterAddress().s_addr) {
329 return True;
330 }
331
332 // We'll handle this data.
333 // Also write it (with the encapsulation trailer) to each member,
334 // unless the packet was originally sent by us to begin with.
335 bytesRead = numBytes;
336
337 int numMembers = 0;
338 if (!wasLoopedBackFromUs(env(), fromAddressAndPort)) {
339 statsIncoming.countPacket(numBytes);
340 statsGroupIncoming.countPacket(numBytes);
341 numMembers =
342 outputToAllMembersExcept(NULL, ttl(),
343 buffer, bytesRead,
344 fromAddressAndPort.sin_addr.s_addr);
345 if (numMembers > 0) {
346 statsRelayedIncoming.countPacket(numBytes);
347 statsGroupRelayedIncoming.countPacket(numBytes);
348 }
349 }
350 if (DebugLevel >= 3) {
351 env() << *this << ": read " << bytesRead << " bytes from " << AddressString(fromAddressAndPort).val() << ", port " << ntohs(fromAddressAndPort.sin_port);
352 if (numMembers > 0) {
353 env() << "; relayed to " << numMembers << " members";
354 }
355 env() << "\n";
356 }
357
358 return True;
359}
360
361Boolean Groupsock::wasLoopedBackFromUs(UsageEnvironment& env,
362 struct sockaddr_in& fromAddressAndPort) {
363 if (fromAddressAndPort.sin_addr.s_addr == ourIPAddress(env) ||
364 fromAddressAndPort.sin_addr.s_addr == 0x7F000001/*127.0.0.1*/) {
365 if (fromAddressAndPort.sin_port == sourcePortNum()) {
366#ifdef DEBUG_LOOPBACK_CHECKING
367 if (DebugLevel >= 3) {
368 env() << *this << ": got looped-back packet\n";
369 }
370#endif
371 return True;
372 }
373 }
374
375 return False;
376}
377
378destRecord* Groupsock
379::lookupDestRecordFromDestination(struct sockaddr_in const& destAddrAndPort) const {
380 for (destRecord* dest = fDests; dest != NULL; dest = dest->fNext) {
381 if (destAddrAndPort.sin_addr.s_addr == dest->fGroupEId.groupAddress().s_addr
382 && destAddrAndPort.sin_port == dest->fGroupEId.portNum()) {
383 return dest;
384 }
385 }
386 return NULL;
387}
388
389void Groupsock::removeDestinationFrom(destRecord*& dests, unsigned sessionId) {
390 destRecord** destsPtr = &dests;
391 while (*destsPtr != NULL) {
392 if (sessionId == (*destsPtr)->fSessionId) {
393 // Remove the record pointed to by *destsPtr :
394 destRecord* next = (*destsPtr)->fNext;
395 (*destsPtr)->fNext = NULL;
396 delete (*destsPtr);
397 *destsPtr = next;
398 } else {
399 destsPtr = &((*destsPtr)->fNext);
400 }
401 }
402}
403
404int Groupsock::outputToAllMembersExcept(DirectedNetInterface* exceptInterface,
405 u_int8_t ttlToFwd,
406 unsigned char* data, unsigned size,
407 netAddressBits sourceAddr) {
408 // Don't forward TTL-0 packets
409 if (ttlToFwd == 0) return 0;
410
411 DirectedNetInterfaceSet::Iterator iter(members());
412 unsigned numMembers = 0;
413 DirectedNetInterface* interf;
414 while ((interf = iter.next()) != NULL) {
415 // Check whether we've asked to exclude this interface:
416 if (interf == exceptInterface)
417 continue;
418
419 // Check that the packet's source address makes it OK to
420 // be relayed across this interface:
421 UsageEnvironment& saveEnv = env();
422 // because the following call may delete "this"
423 if (!interf->SourceAddrOKForRelaying(saveEnv, sourceAddr)) {
424 if (strcmp(saveEnv.getResultMsg(), "") != 0) {
425 // Treat this as a fatal error
426 return -1;
427 } else {
428 continue;
429 }
430 }
431
432 if (numMembers == 0) {
433 // We know that we're going to forward to at least one
434 // member, so fill in the tunnel encapsulation trailer.
435 // (Note: Allow for it not being 4-byte-aligned.)
436 TunnelEncapsulationTrailer* trailerInPacket
437 = (TunnelEncapsulationTrailer*)&data[size];
438 TunnelEncapsulationTrailer* trailer;
439
440 Boolean misaligned = ((uintptr_t)trailerInPacket & 3) != 0;
441 unsigned trailerOffset;
442 u_int8_t tunnelCmd;
443 if (isSSM()) {
444 // add an 'auxilliary address' before the trailer
445 trailerOffset = TunnelEncapsulationTrailerAuxSize;
446 tunnelCmd = TunnelDataAuxCmd;
447 } else {
448 trailerOffset = 0;
449 tunnelCmd = TunnelDataCmd;
450 }
451 unsigned trailerSize = TunnelEncapsulationTrailerSize + trailerOffset;
452 unsigned tmpTr[TunnelEncapsulationTrailerMaxSize];
453 if (misaligned) {
454 trailer = (TunnelEncapsulationTrailer*)&tmpTr;
455 } else {
456 trailer = trailerInPacket;
457 }
458 trailer += trailerOffset;
459
460 if (fDests != NULL) {
461 trailer->address() = fDests->fGroupEId.groupAddress().s_addr;
462 Port destPort(ntohs(fDests->fGroupEId.portNum()));
463 trailer->port() = destPort; // structure copy
464 }
465 trailer->ttl() = ttlToFwd;
466 trailer->command() = tunnelCmd;
467
468 if (isSSM()) {
469 trailer->auxAddress() = sourceFilterAddress().s_addr;
470 }
471
472 if (misaligned) {
473 memmove(trailerInPacket, trailer-trailerOffset, trailerSize);
474 }
475
476 size += trailerSize;
477 }
478
479 interf->write(data, size);
480 ++numMembers;
481 }
482
483 return numMembers;
484}
485
486UsageEnvironment& operator<<(UsageEnvironment& s, const Groupsock& g) {
487 UsageEnvironment& s1 = s << timestampString() << " Groupsock("
488 << g.socketNum() << ": "
489 << AddressString(g.groupAddress()).val()
490 << ", " << g.port() << ", ";
491 if (g.isSSM()) {
492 return s1 << "SSM source: "
493 << AddressString(g.sourceFilterAddress()).val() << ")";
494 } else {
495 return s1 << (unsigned)(g.ttl()) << ")";
496 }
497}
498
499
500////////// GroupsockLookupTable //////////
501
502
503// A hash table used to index Groupsocks by socket number.
504
505static HashTable*& getSocketTable(UsageEnvironment& env) {
506 _groupsockPriv* priv = groupsockPriv(env);
507 if (priv->socketTable == NULL) { // We need to create it
508 priv->socketTable = HashTable::create(ONE_WORD_HASH_KEYS);
509 }
510 return priv->socketTable;
511}
512
513static Boolean unsetGroupsockBySocket(Groupsock const* groupsock) {
514 do {
515 if (groupsock == NULL) break;
516
517 int sock = groupsock->socketNum();
518 // Make sure "sock" is in bounds:
519 if (sock < 0) break;
520
521 HashTable*& sockets = getSocketTable(groupsock->env());
522
523 Groupsock* gs = (Groupsock*)sockets->Lookup((char*)(long)sock);
524 if (gs == NULL || gs != groupsock) break;
525 sockets->Remove((char*)(long)sock);
526
527 if (sockets->IsEmpty()) {
528 // We can also delete the table (to reclaim space):
529 delete sockets; sockets = NULL;
530 reclaimGroupsockPriv(gs->env());
531 }
532
533 return True;
534 } while (0);
535
536 return False;
537}
538
539static Boolean setGroupsockBySocket(UsageEnvironment& env, int sock,
540 Groupsock* groupsock) {
541 do {
542 // Make sure the "sock" parameter is in bounds:
543 if (sock < 0) {
544 char buf[100];
545 sprintf(buf, "trying to use bad socket (%d)", sock);
546 env.setResultMsg(buf);
547 break;
548 }
549
550 HashTable* sockets = getSocketTable(env);
551
552 // Make sure we're not replacing an existing Groupsock (although that shouldn't happen)
553 Boolean alreadyExists
554 = (sockets->Lookup((char*)(long)sock) != 0);
555 if (alreadyExists) {
556 char buf[100];
557 sprintf(buf, "Attempting to replace an existing socket (%d)", sock);
558 env.setResultMsg(buf);
559 break;
560 }
561
562 sockets->Add((char*)(long)sock, groupsock);
563 return True;
564 } while (0);
565
566 return False;
567}
568
569static Groupsock* getGroupsockBySocket(UsageEnvironment& env, int sock) {
570 do {
571 // Make sure the "sock" parameter is in bounds:
572 if (sock < 0) break;
573
574 HashTable* sockets = getSocketTable(env);
575 return (Groupsock*)sockets->Lookup((char*)(long)sock);
576 } while (0);
577
578 return NULL;
579}
580
581Groupsock*
582GroupsockLookupTable::Fetch(UsageEnvironment& env,
583 netAddressBits groupAddress,
584 Port port, u_int8_t ttl,
585 Boolean& isNew) {
586 isNew = False;
587 Groupsock* groupsock;
588 do {
589 groupsock = (Groupsock*) fTable.Lookup(groupAddress, (~0), port);
590 if (groupsock == NULL) { // we need to create one:
591 groupsock = AddNew(env, groupAddress, (~0), port, ttl);
592 if (groupsock == NULL) break;
593 isNew = True;
594 }
595 } while (0);
596
597 return groupsock;
598}
599
600Groupsock*
601GroupsockLookupTable::Fetch(UsageEnvironment& env,
602 netAddressBits groupAddress,
603 netAddressBits sourceFilterAddr, Port port,
604 Boolean& isNew) {
605 isNew = False;
606 Groupsock* groupsock;
607 do {
608 groupsock
609 = (Groupsock*) fTable.Lookup(groupAddress, sourceFilterAddr, port);
610 if (groupsock == NULL) { // we need to create one:
611 groupsock = AddNew(env, groupAddress, sourceFilterAddr, port, 0);
612 if (groupsock == NULL) break;
613 isNew = True;
614 }
615 } while (0);
616
617 return groupsock;
618}
619
620Groupsock*
621GroupsockLookupTable::Lookup(netAddressBits groupAddress, Port port) {
622 return (Groupsock*) fTable.Lookup(groupAddress, (~0), port);
623}
624
625Groupsock*
626GroupsockLookupTable::Lookup(netAddressBits groupAddress,
627 netAddressBits sourceFilterAddr, Port port) {
628 return (Groupsock*) fTable.Lookup(groupAddress, sourceFilterAddr, port);
629}
630
631Groupsock* GroupsockLookupTable::Lookup(UsageEnvironment& env, int sock) {
632 return getGroupsockBySocket(env, sock);
633}
634
635Boolean GroupsockLookupTable::Remove(Groupsock const* groupsock) {
636 unsetGroupsockBySocket(groupsock);
637 return fTable.Remove(groupsock->groupAddress().s_addr,
638 groupsock->sourceFilterAddress().s_addr,
639 groupsock->port());
640}
641
642Groupsock* GroupsockLookupTable::AddNew(UsageEnvironment& env,
643 netAddressBits groupAddress,
644 netAddressBits sourceFilterAddress,
645 Port port, u_int8_t ttl) {
646 Groupsock* groupsock;
647 do {
648 struct in_addr groupAddr; groupAddr.s_addr = groupAddress;
649 if (sourceFilterAddress == netAddressBits(~0)) {
650 // regular, ISM groupsock
651 groupsock = new Groupsock(env, groupAddr, port, ttl);
652 } else {
653 // SSM groupsock
654 struct in_addr sourceFilterAddr;
655 sourceFilterAddr.s_addr = sourceFilterAddress;
656 groupsock = new Groupsock(env, groupAddr, sourceFilterAddr, port);
657 }
658
659 if (groupsock == NULL || groupsock->socketNum() < 0) break;
660
661 if (!setGroupsockBySocket(env, groupsock->socketNum(), groupsock)) break;
662
663 fTable.Add(groupAddress, sourceFilterAddress, port, (void*)groupsock);
664 } while (0);
665
666 return groupsock;
667}
668
669GroupsockLookupTable::Iterator::Iterator(GroupsockLookupTable& groupsocks)
670 : fIter(AddressPortLookupTable::Iterator(groupsocks.fTable)) {
671}
672
673Groupsock* GroupsockLookupTable::Iterator::next() {
674 return (Groupsock*) fIter.next();
675};
676