1 /**********
2 This library is free software; you can redistribute it and/or modify it under
3 the terms of the GNU Lesser General Public License as published by the
4 Free Software Foundation; either version 3 of the License, or (at your
5 option) any later version. (See <http://www.gnu.org/copyleft/lesser.html>.)
6
7 This library is distributed in the hope that it will be useful, but WITHOUT
8 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
9 FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
10 more details.
11
12 You should have received a copy of the GNU Lesser General Public License
13 along with this library; if not, write to the Free Software Foundation, Inc.,
14 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
15 **********/
16 // "mTunnel" multicast access service
17 // Copyright (c) 1996-2020 Live Networks, Inc. All rights reserved.
18 // Helper routines to implement 'group sockets'
19 // Implementation
20
21 #include "GroupsockHelper.hh"
22
23 #if (defined(__WIN32__) || defined(_WIN32)) && !defined(__MINGW32__)
24 #include <time.h>
25 extern "C" int initializeWinsockIfNecessary();
26 #else
27 #include <stdarg.h>
28 #include <time.h>
29 #include <sys/time.h>
30 #if !defined(_WIN32)
31 #include <netinet/tcp.h>
32 #ifdef __ANDROID_NDK__
33 #include <android/ndk-version.h>
34 #define ANDROID_OLD_NDK __NDK_MAJOR__ < 17
35 #endif
36 #endif
37 #include <fcntl.h>
38 #define initializeWinsockIfNecessary() 1
39 #endif
40 #if defined(__WIN32__) || defined(_WIN32) || defined(_QNX4)
41 #else
42 #include <signal.h>
43 #define USE_SIGNALS 1
44 #endif
45 #include <stdio.h>
46
47 // By default, use INADDR_ANY for the sending and receiving interfaces:
48 netAddressBits SendingInterfaceAddr = INADDR_ANY;
49 netAddressBits ReceivingInterfaceAddr = INADDR_ANY;
50
socketErr(UsageEnvironment & env,char const * errorMsg)51 static void socketErr(UsageEnvironment& env, char const* errorMsg) {
52 env.setResultErrMsg(errorMsg);
53 }
54
NoReuse(UsageEnvironment & env)55 NoReuse::NoReuse(UsageEnvironment& env)
56 : fEnv(env) {
57 groupsockPriv(fEnv)->reuseFlag = 0;
58 }
59
~NoReuse()60 NoReuse::~NoReuse() {
61 groupsockPriv(fEnv)->reuseFlag = 1;
62 reclaimGroupsockPriv(fEnv);
63 }
64
65
groupsockPriv(UsageEnvironment & env)66 _groupsockPriv* groupsockPriv(UsageEnvironment& env) {
67 if (env.groupsockPriv == NULL) { // We need to create it
68 _groupsockPriv* result = new _groupsockPriv;
69 result->socketTable = NULL;
70 result->reuseFlag = 1; // default value => allow reuse of socket numbers
71 env.groupsockPriv = result;
72 }
73 return (_groupsockPriv*)(env.groupsockPriv);
74 }
75
reclaimGroupsockPriv(UsageEnvironment & env)76 void reclaimGroupsockPriv(UsageEnvironment& env) {
77 _groupsockPriv* priv = (_groupsockPriv*)(env.groupsockPriv);
78 if (priv->socketTable == NULL && priv->reuseFlag == 1/*default value*/) {
79 // We can delete the structure (to save space); it will get created again, if needed:
80 delete priv;
81 env.groupsockPriv = NULL;
82 }
83 }
84
createSocket(int type)85 static int createSocket(int type) {
86 // Call "socket()" to create a (IPv4) socket of the specified type.
87 // But also set it to have the 'close on exec' property (if we can)
88 int sock;
89
90 #ifdef SOCK_CLOEXEC
91 sock = socket(AF_INET, type|SOCK_CLOEXEC, 0);
92 if (sock != -1 || errno != EINVAL) return sock;
93 // An "errno" of EINVAL likely means that the system wasn't happy with the SOCK_CLOEXEC; fall through and try again without it:
94 #endif
95
96 sock = socket(AF_INET, type, 0);
97 #ifdef FD_CLOEXEC
98 if (sock != -1) fcntl(sock, F_SETFD, FD_CLOEXEC);
99 #endif
100 return sock;
101 }
102
setupDatagramSocket(UsageEnvironment & env,Port port)103 int setupDatagramSocket(UsageEnvironment& env, Port port) {
104 if (!initializeWinsockIfNecessary()) {
105 socketErr(env, "Failed to initialize 'winsock': ");
106 return -1;
107 }
108
109 int newSocket = createSocket(SOCK_DGRAM);
110 if (newSocket < 0) {
111 socketErr(env, "unable to create datagram socket: ");
112 return newSocket;
113 }
114
115 int reuseFlag = groupsockPriv(env)->reuseFlag;
116 reclaimGroupsockPriv(env);
117 if (setsockopt(newSocket, SOL_SOCKET, SO_REUSEADDR,
118 (const char*)&reuseFlag, sizeof reuseFlag) < 0) {
119 socketErr(env, "setsockopt(SO_REUSEADDR) error: ");
120 closeSocket(newSocket);
121 return -1;
122 }
123
124 #if defined(__WIN32__) || defined(_WIN32)
125 // Windoze doesn't properly handle SO_REUSEPORT or IP_MULTICAST_LOOP
126 #else
127 #ifdef SO_REUSEPORT
128 if (setsockopt(newSocket, SOL_SOCKET, SO_REUSEPORT,
129 (const char*)&reuseFlag, sizeof reuseFlag) < 0) {
130 socketErr(env, "setsockopt(SO_REUSEPORT) error: ");
131 closeSocket(newSocket);
132 return -1;
133 }
134 #endif
135
136 #ifdef IP_MULTICAST_LOOP
137 const u_int8_t loop = 1;
138 if (setsockopt(newSocket, IPPROTO_IP, IP_MULTICAST_LOOP,
139 (const char*)&loop, sizeof loop) < 0) {
140 socketErr(env, "setsockopt(IP_MULTICAST_LOOP) error: ");
141 closeSocket(newSocket);
142 return -1;
143 }
144 #endif
145 #endif
146
147 // Note: Windoze requires binding, even if the port number is 0
148 netAddressBits addr = INADDR_ANY;
149 #if defined(__WIN32__) || defined(_WIN32)
150 #else
151 if (port.num() != 0 || ReceivingInterfaceAddr != INADDR_ANY) {
152 #endif
153 if (port.num() == 0) addr = ReceivingInterfaceAddr;
154 MAKE_SOCKADDR_IN(name, addr, port.num());
155 if (bind(newSocket, (struct sockaddr*)&name, sizeof name) != 0) {
156 char tmpBuffer[100];
157 sprintf(tmpBuffer, "bind() error (port number: %d): ",
158 ntohs(port.num()));
159 socketErr(env, tmpBuffer);
160 closeSocket(newSocket);
161 return -1;
162 }
163 #if defined(__WIN32__) || defined(_WIN32)
164 #else
165 }
166 #endif
167
168 // Set the sending interface for multicasts, if it's not the default:
169 if (SendingInterfaceAddr != INADDR_ANY) {
170 struct in_addr addr;
171 addr.s_addr = SendingInterfaceAddr;
172
173 if (setsockopt(newSocket, IPPROTO_IP, IP_MULTICAST_IF,
174 (const char*)&addr, sizeof addr) < 0) {
175 socketErr(env, "error setting outgoing multicast interface: ");
176 closeSocket(newSocket);
177 return -1;
178 }
179 }
180
181 return newSocket;
182 }
183
makeSocketNonBlocking(int sock)184 Boolean makeSocketNonBlocking(int sock) {
185 #if defined(__WIN32__) || defined(_WIN32)
186 unsigned long arg = 1;
187 return ioctlsocket(sock, FIONBIO, &arg) == 0;
188 #elif defined(VXWORKS)
189 int arg = 1;
190 return ioctl(sock, FIONBIO, (int)&arg) == 0;
191 #else
192 int curFlags = fcntl(sock, F_GETFL, 0);
193 return fcntl(sock, F_SETFL, curFlags|O_NONBLOCK) >= 0;
194 #endif
195 }
196
makeSocketBlocking(int sock,unsigned writeTimeoutInMilliseconds)197 Boolean makeSocketBlocking(int sock, unsigned writeTimeoutInMilliseconds) {
198 Boolean result;
199 #if defined(__WIN32__) || defined(_WIN32)
200 unsigned long arg = 0;
201 result = ioctlsocket(sock, FIONBIO, &arg) == 0;
202 #elif defined(VXWORKS)
203 int arg = 0;
204 result = ioctl(sock, FIONBIO, (int)&arg) == 0;
205 #else
206 int curFlags = fcntl(sock, F_GETFL, 0);
207 result = fcntl(sock, F_SETFL, curFlags&(~O_NONBLOCK)) >= 0;
208 #endif
209
210 if (writeTimeoutInMilliseconds > 0) {
211 #ifdef SO_SNDTIMEO
212 #if defined(__WIN32__) || defined(_WIN32)
213 DWORD msto = (DWORD)writeTimeoutInMilliseconds;
214 setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, (char *)&msto, sizeof(msto) );
215 #else
216 struct timeval tv;
217 tv.tv_sec = writeTimeoutInMilliseconds/1000;
218 tv.tv_usec = (writeTimeoutInMilliseconds%1000)*1000;
219 setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, (char *)&tv, sizeof tv);
220 #endif
221 #endif
222 }
223
224 return result;
225 }
226
setSocketKeepAlive(int sock)227 Boolean setSocketKeepAlive(int sock) {
228 #if defined(__WIN32__) || defined(_WIN32)
229 // How do we do this in Windows? For now, just make this a no-op in Windows:
230 #else
231 int const keepalive_enabled = 1;
232 if (setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (void*)&keepalive_enabled, sizeof keepalive_enabled) < 0) {
233 return False;
234 }
235
236 #ifdef TCP_KEEPIDLE
237 int const keepalive_time = 180;
238 if (setsockopt(sock, IPPROTO_TCP, TCP_KEEPIDLE, (void*)&keepalive_time, sizeof keepalive_time) < 0) {
239 return False;
240 }
241 #endif
242
243 #ifdef TCP_KEEPCNT
244 int const keepalive_count = 5;
245 if (setsockopt(sock, IPPROTO_TCP, TCP_KEEPCNT, (void*)&keepalive_count, sizeof keepalive_count) < 0) {
246 return False;
247 }
248 #endif
249
250 #ifdef TCP_KEEPINTVL
251 int const keepalive_interval = 20;
252 if (setsockopt(sock, IPPROTO_TCP, TCP_KEEPINTVL, (void*)&keepalive_interval, sizeof keepalive_interval) < 0) {
253 return False;
254 }
255 #endif
256 #endif
257
258 return True;
259 }
260
setupStreamSocket(UsageEnvironment & env,Port port,Boolean makeNonBlocking,Boolean setKeepAlive)261 int setupStreamSocket(UsageEnvironment& env,
262 Port port, Boolean makeNonBlocking, Boolean setKeepAlive) {
263 if (!initializeWinsockIfNecessary()) {
264 socketErr(env, "Failed to initialize 'winsock': ");
265 return -1;
266 }
267
268 int newSocket = createSocket(SOCK_STREAM);
269 if (newSocket < 0) {
270 socketErr(env, "unable to create stream socket: ");
271 return newSocket;
272 }
273
274 int reuseFlag = groupsockPriv(env)->reuseFlag;
275 reclaimGroupsockPriv(env);
276 if (setsockopt(newSocket, SOL_SOCKET, SO_REUSEADDR,
277 (const char*)&reuseFlag, sizeof reuseFlag) < 0) {
278 socketErr(env, "setsockopt(SO_REUSEADDR) error: ");
279 closeSocket(newSocket);
280 return -1;
281 }
282
283 // SO_REUSEPORT doesn't really make sense for TCP sockets, so we
284 // normally don't set them. However, if you really want to do this
285 // #define REUSE_FOR_TCP
286 #ifdef REUSE_FOR_TCP
287 #if defined(__WIN32__) || defined(_WIN32)
288 // Windoze doesn't properly handle SO_REUSEPORT
289 #else
290 #ifdef SO_REUSEPORT
291 if (setsockopt(newSocket, SOL_SOCKET, SO_REUSEPORT,
292 (const char*)&reuseFlag, sizeof reuseFlag) < 0) {
293 socketErr(env, "setsockopt(SO_REUSEPORT) error: ");
294 closeSocket(newSocket);
295 return -1;
296 }
297 #endif
298 #endif
299 #endif
300
301 // Note: Windoze requires binding, even if the port number is 0
302 #if defined(__WIN32__) || defined(_WIN32)
303 #else
304 if (port.num() != 0 || ReceivingInterfaceAddr != INADDR_ANY) {
305 #endif
306 MAKE_SOCKADDR_IN(name, ReceivingInterfaceAddr, port.num());
307 if (bind(newSocket, (struct sockaddr*)&name, sizeof name) != 0) {
308 char tmpBuffer[100];
309 sprintf(tmpBuffer, "bind() error (port number: %d): ",
310 ntohs(port.num()));
311 socketErr(env, tmpBuffer);
312 closeSocket(newSocket);
313 return -1;
314 }
315 #if defined(__WIN32__) || defined(_WIN32)
316 #else
317 }
318 #endif
319
320 if (makeNonBlocking) {
321 if (!makeSocketNonBlocking(newSocket)) {
322 socketErr(env, "failed to make non-blocking: ");
323 closeSocket(newSocket);
324 return -1;
325 }
326 }
327
328 // Set the keep alive mechanism for the TCP socket, to avoid "ghost sockets"
329 // that remain after an interrupted communication.
330 if (setKeepAlive) {
331 if (!setSocketKeepAlive(newSocket)) {
332 socketErr(env, "failed to set keep alive: ");
333 closeSocket(newSocket);
334 return -1;
335 }
336 }
337
338 return newSocket;
339 }
340
readSocket(UsageEnvironment & env,int socket,unsigned char * buffer,unsigned bufferSize,struct sockaddr_storage & fromAddress)341 int readSocket(UsageEnvironment& env,
342 int socket, unsigned char* buffer, unsigned bufferSize,
343 struct sockaddr_storage& fromAddress) {
344 SOCKLEN_T addressSize = sizeof fromAddress;
345 int bytesRead = recvfrom(socket, (char*)buffer, bufferSize, 0,
346 (struct sockaddr*)&fromAddress,
347 &addressSize);
348 if (bytesRead < 0) {
349 //##### HACK to work around bugs in Linux and Windows:
350 int err = env.getErrno();
351 if (err == 111 /*ECONNREFUSED (Linux)*/
352 #if defined(__WIN32__) || defined(_WIN32)
353 // What a piece of crap Windows is. Sometimes
354 // recvfrom() returns -1, but with an 'errno' of 0.
355 // This appears not to be a real error; just treat
356 // it as if it were a read of zero bytes, and hope
357 // we don't have to do anything else to 'reset'
358 // this alleged error:
359 || err == 0 || err == EWOULDBLOCK
360 #else
361 || err == EAGAIN
362 #endif
363 || err == 113 /*EHOSTUNREACH (Linux)*/) { // Why does Linux return this for datagram sock?
364 switch (fromAddress.ss_family) {
365 case AF_INET: {
366 ((sockaddr_in*)&fromAddress)->sin_addr.s_addr = 0;
367 break;
368 }
369 case AF_INET6: {
370 ((sockaddr_in6*)&fromAddress)->sin6_addr.__u6_addr.__u6_addr32[0] = 0;
371 ((sockaddr_in6*)&fromAddress)->sin6_addr.__u6_addr.__u6_addr32[1] = 0;
372 ((sockaddr_in6*)&fromAddress)->sin6_addr.__u6_addr.__u6_addr32[2] = 0;
373 ((sockaddr_in6*)&fromAddress)->sin6_addr.__u6_addr.__u6_addr32[3] = 0;
374 break;
375 }
376 }
377 return 0;
378 }
379 //##### END HACK
380 socketErr(env, "recvfrom() error: ");
381 } else if (bytesRead == 0) {
382 // "recvfrom()" on a stream socket can return 0 if the remote end has closed the connection. Treat this as an error:
383 return -1;
384 }
385
386 return bytesRead;
387 }
388
writeSocket(UsageEnvironment & env,int socket,struct in_addr address,portNumBits portNum,u_int8_t ttlArg,unsigned char * buffer,unsigned bufferSize)389 Boolean writeSocket(UsageEnvironment& env,
390 int socket, struct in_addr address, portNumBits portNum,
391 u_int8_t ttlArg,
392 unsigned char* buffer, unsigned bufferSize) {
393 // Before sending, set the socket's TTL:
394 #if defined(__WIN32__) || defined(_WIN32)
395 #define TTL_TYPE int
396 #else
397 #define TTL_TYPE u_int8_t
398 #endif
399 TTL_TYPE ttl = (TTL_TYPE)ttlArg;
400 if (setsockopt(socket, IPPROTO_IP, IP_MULTICAST_TTL,
401 (const char*)&ttl, sizeof ttl) < 0) {
402 socketErr(env, "setsockopt(IP_MULTICAST_TTL) error: ");
403 return False;
404 }
405
406 return writeSocket(env, socket, address, portNum, buffer, bufferSize);
407 }
408
writeSocket(UsageEnvironment & env,int socket,struct in_addr address,portNumBits portNum,unsigned char * buffer,unsigned bufferSize)409 Boolean writeSocket(UsageEnvironment& env,
410 int socket, struct in_addr address, portNumBits portNum,
411 unsigned char* buffer, unsigned bufferSize) {
412 do {
413 MAKE_SOCKADDR_IN(dest, address.s_addr, portNum);
414 int bytesSent = sendto(socket, (char*)buffer, bufferSize, 0,
415 (struct sockaddr*)&dest, sizeof dest);
416 if (bytesSent != (int)bufferSize) {
417 char tmpBuf[100];
418 sprintf(tmpBuf, "writeSocket(%d), sendTo() error: wrote %d bytes instead of %u: ", socket, bytesSent, bufferSize);
419 socketErr(env, tmpBuf);
420 break;
421 }
422
423 return True;
424 } while (0);
425
426 return False;
427 }
428
ignoreSigPipeOnSocket(int socketNum)429 void ignoreSigPipeOnSocket(int socketNum) {
430 #ifdef USE_SIGNALS
431 #ifdef SO_NOSIGPIPE
432 int set_option = 1;
433 setsockopt(socketNum, SOL_SOCKET, SO_NOSIGPIPE, &set_option, sizeof set_option);
434 #else
435 signal(SIGPIPE, SIG_IGN);
436 #endif
437 #endif
438 }
439
getBufferSize(UsageEnvironment & env,int bufOptName,int socket)440 static unsigned getBufferSize(UsageEnvironment& env, int bufOptName,
441 int socket) {
442 unsigned curSize;
443 SOCKLEN_T sizeSize = sizeof curSize;
444 if (getsockopt(socket, SOL_SOCKET, bufOptName,
445 (char*)&curSize, &sizeSize) < 0) {
446 socketErr(env, "getBufferSize() error: ");
447 return 0;
448 }
449
450 return curSize;
451 }
getSendBufferSize(UsageEnvironment & env,int socket)452 unsigned getSendBufferSize(UsageEnvironment& env, int socket) {
453 return getBufferSize(env, SO_SNDBUF, socket);
454 }
getReceiveBufferSize(UsageEnvironment & env,int socket)455 unsigned getReceiveBufferSize(UsageEnvironment& env, int socket) {
456 return getBufferSize(env, SO_RCVBUF, socket);
457 }
458
setBufferTo(UsageEnvironment & env,int bufOptName,int socket,unsigned requestedSize)459 static unsigned setBufferTo(UsageEnvironment& env, int bufOptName,
460 int socket, unsigned requestedSize) {
461 SOCKLEN_T sizeSize = sizeof requestedSize;
462 setsockopt(socket, SOL_SOCKET, bufOptName, (char*)&requestedSize, sizeSize);
463
464 // Get and return the actual, resulting buffer size:
465 return getBufferSize(env, bufOptName, socket);
466 }
setSendBufferTo(UsageEnvironment & env,int socket,unsigned requestedSize)467 unsigned setSendBufferTo(UsageEnvironment& env,
468 int socket, unsigned requestedSize) {
469 return setBufferTo(env, SO_SNDBUF, socket, requestedSize);
470 }
setReceiveBufferTo(UsageEnvironment & env,int socket,unsigned requestedSize)471 unsigned setReceiveBufferTo(UsageEnvironment& env,
472 int socket, unsigned requestedSize) {
473 return setBufferTo(env, SO_RCVBUF, socket, requestedSize);
474 }
475
increaseBufferTo(UsageEnvironment & env,int bufOptName,int socket,unsigned requestedSize)476 static unsigned increaseBufferTo(UsageEnvironment& env, int bufOptName,
477 int socket, unsigned requestedSize) {
478 // First, get the current buffer size. If it's already at least
479 // as big as what we're requesting, do nothing.
480 unsigned curSize = getBufferSize(env, bufOptName, socket);
481
482 // Next, try to increase the buffer to the requested size,
483 // or to some smaller size, if that's not possible:
484 while (requestedSize > curSize) {
485 SOCKLEN_T sizeSize = sizeof requestedSize;
486 if (setsockopt(socket, SOL_SOCKET, bufOptName,
487 (char*)&requestedSize, sizeSize) >= 0) {
488 // success
489 return requestedSize;
490 }
491 requestedSize = (requestedSize+curSize)/2;
492 }
493
494 return getBufferSize(env, bufOptName, socket);
495 }
increaseSendBufferTo(UsageEnvironment & env,int socket,unsigned requestedSize)496 unsigned increaseSendBufferTo(UsageEnvironment& env,
497 int socket, unsigned requestedSize) {
498 return increaseBufferTo(env, SO_SNDBUF, socket, requestedSize);
499 }
increaseReceiveBufferTo(UsageEnvironment & env,int socket,unsigned requestedSize)500 unsigned increaseReceiveBufferTo(UsageEnvironment& env,
501 int socket, unsigned requestedSize) {
502 return increaseBufferTo(env, SO_RCVBUF, socket, requestedSize);
503 }
504
clearMulticastAllSocketOption(int socket)505 static void clearMulticastAllSocketOption(int socket) {
506 #ifdef IP_MULTICAST_ALL
507 // This option is defined in modern versions of Linux to overcome a bug in the Linux kernel's default behavior.
508 // When set to 0, it ensures that we receive only packets that were sent to the specified IP multicast address,
509 // even if some other process on the same system has joined a different multicast group with the same port number.
510 int multicastAll = 0;
511 (void)setsockopt(socket, IPPROTO_IP, IP_MULTICAST_ALL, (void*)&multicastAll, sizeof multicastAll);
512 // Ignore the call's result. Should it fail, we'll still receive packets (just perhaps more than intended)
513 #endif
514 }
515
socketJoinGroup(UsageEnvironment & env,int socket,netAddressBits groupAddress)516 Boolean socketJoinGroup(UsageEnvironment& env, int socket,
517 netAddressBits groupAddress){
518 if (!IsMulticastAddress(groupAddress)) return True; // ignore this case
519
520 struct ip_mreq imr;
521 imr.imr_multiaddr.s_addr = groupAddress;
522 imr.imr_interface.s_addr = ReceivingInterfaceAddr;
523 if (setsockopt(socket, IPPROTO_IP, IP_ADD_MEMBERSHIP,
524 (const char*)&imr, sizeof (struct ip_mreq)) < 0) {
525 #if defined(__WIN32__) || defined(_WIN32)
526 if (env.getErrno() != 0) {
527 // That piece-of-shit toy operating system (Windows) sometimes lies
528 // about setsockopt() failing!
529 #endif
530 socketErr(env, "setsockopt(IP_ADD_MEMBERSHIP) error: ");
531 return False;
532 #if defined(__WIN32__) || defined(_WIN32)
533 }
534 #endif
535 }
536
537 clearMulticastAllSocketOption(socket);
538
539 return True;
540 }
541
socketLeaveGroup(UsageEnvironment &,int socket,netAddressBits groupAddress)542 Boolean socketLeaveGroup(UsageEnvironment&, int socket,
543 netAddressBits groupAddress) {
544 if (!IsMulticastAddress(groupAddress)) return True; // ignore this case
545
546 struct ip_mreq imr;
547 imr.imr_multiaddr.s_addr = groupAddress;
548 imr.imr_interface.s_addr = ReceivingInterfaceAddr;
549 if (setsockopt(socket, IPPROTO_IP, IP_DROP_MEMBERSHIP,
550 (const char*)&imr, sizeof (struct ip_mreq)) < 0) {
551 return False;
552 }
553
554 return True;
555 }
556
557 // The source-specific join/leave operations require special setsockopt()
558 // commands, and a special structure (ip_mreq_source). If the include files
559 // didn't define these, we do so here:
560 #if !defined(IP_ADD_SOURCE_MEMBERSHIP)
561 struct ip_mreq_source {
562 struct in_addr imr_multiaddr; /* IP multicast address of group */
563 struct in_addr imr_sourceaddr; /* IP address of source */
564 struct in_addr imr_interface; /* local IP address of interface */
565 };
566 #endif
567
568 #ifndef IP_ADD_SOURCE_MEMBERSHIP
569
570 #ifdef LINUX
571 #define IP_ADD_SOURCE_MEMBERSHIP 39
572 #define IP_DROP_SOURCE_MEMBERSHIP 40
573 #else
574 #define IP_ADD_SOURCE_MEMBERSHIP 25
575 #define IP_DROP_SOURCE_MEMBERSHIP 26
576 #endif
577
578 #endif
579
socketJoinGroupSSM(UsageEnvironment & env,int socket,netAddressBits groupAddress,netAddressBits sourceFilterAddr)580 Boolean socketJoinGroupSSM(UsageEnvironment& env, int socket,
581 netAddressBits groupAddress,
582 netAddressBits sourceFilterAddr) {
583 if (!IsMulticastAddress(groupAddress)) return True; // ignore this case
584
585 struct ip_mreq_source imr;
586 #if ANDROID_OLD_NDK
587 imr.imr_multiaddr = groupAddress;
588 imr.imr_sourceaddr = sourceFilterAddr;
589 imr.imr_interface = ReceivingInterfaceAddr;
590 #else
591 imr.imr_multiaddr.s_addr = groupAddress;
592 imr.imr_sourceaddr.s_addr = sourceFilterAddr;
593 imr.imr_interface.s_addr = ReceivingInterfaceAddr;
594 #endif
595 if (setsockopt(socket, IPPROTO_IP, IP_ADD_SOURCE_MEMBERSHIP,
596 (const char*)&imr, sizeof (struct ip_mreq_source)) < 0) {
597 socketErr(env, "setsockopt(IP_ADD_SOURCE_MEMBERSHIP) error: ");
598 return False;
599 }
600
601 clearMulticastAllSocketOption(socket);
602
603 return True;
604 }
605
socketLeaveGroupSSM(UsageEnvironment &,int socket,netAddressBits groupAddress,netAddressBits sourceFilterAddr)606 Boolean socketLeaveGroupSSM(UsageEnvironment& /*env*/, int socket,
607 netAddressBits groupAddress,
608 netAddressBits sourceFilterAddr) {
609 if (!IsMulticastAddress(groupAddress)) return True; // ignore this case
610
611 struct ip_mreq_source imr;
612 #if ANDROID_OLD_NDK
613 imr.imr_multiaddr = groupAddress;
614 imr.imr_sourceaddr = sourceFilterAddr;
615 imr.imr_interface = ReceivingInterfaceAddr;
616 #else
617 imr.imr_multiaddr.s_addr = groupAddress;
618 imr.imr_sourceaddr.s_addr = sourceFilterAddr;
619 imr.imr_interface.s_addr = ReceivingInterfaceAddr;
620 #endif
621 if (setsockopt(socket, IPPROTO_IP, IP_DROP_SOURCE_MEMBERSHIP,
622 (const char*)&imr, sizeof (struct ip_mreq_source)) < 0) {
623 return False;
624 }
625
626 return True;
627 }
628
getSourcePort0(int socket,portNumBits & resultPortNum)629 static Boolean getSourcePort0(int socket, portNumBits& resultPortNum/*host order*/) {
630 sockaddr_storage test;
631 switch (test.ss_family) {
632 case AF_INET: {
633 ((sockaddr_in*)&test)->sin_port = 0;
634 break;
635 }
636 case AF_INET6: {
637 ((sockaddr_in6*)&test)->sin6_port = 0;
638 }
639 }
640
641 SOCKLEN_T len = sizeof test;
642 if (getsockname(socket, (struct sockaddr*)&test, &len) < 0) return False;
643
644 resultPortNum = ntohs(portNum(test));
645 return True;
646 }
647
getSourcePort(UsageEnvironment & env,int socket,Port & port)648 Boolean getSourcePort(UsageEnvironment& env, int socket, Port& port) {
649 portNumBits portNum = 0;
650 if (!getSourcePort0(socket, portNum) || portNum == 0) {
651 // Hack - call bind(), then try again:
652 MAKE_SOCKADDR_IN(name, INADDR_ANY, 0);
653 bind(socket, (struct sockaddr*)&name, sizeof name);
654
655 if (!getSourcePort0(socket, portNum) || portNum == 0) {
656 socketErr(env, "getsockname() error: ");
657 return False;
658 }
659 }
660
661 port = Port(portNum);
662 return True;
663 }
664
badAddressForUs(ipv4AddressBits addr)665 static Boolean badAddressForUs(ipv4AddressBits addr) {
666 // Check for some possible erroneous addresses:
667 ipv4AddressBits nAddr = htonl(addr);
668 return (nAddr == 0x7F000001 /* 127.0.0.1 */
669 || nAddr == 0
670 || nAddr == (netAddressBits)(~0));
671 }
672
badAddressForUs(struct sockaddr_storage const & addr)673 static Boolean badAddressForUs(struct sockaddr_storage const& addr) {
674 if (addr.ss_family != AF_INET) return True; // fix later for IPv6
675
676 return badAddressForUs(((sockaddr_in*)&addr)->sin_addr.s_addr);
677 }
678
679 Boolean loopbackWorks = 1;
680
ourIPAddress(UsageEnvironment & env)681 netAddressBits ourIPAddress(UsageEnvironment& env) {
682 static netAddressBits ourAddress = 0;
683 int sock = -1;
684 struct in_addr testAddr;
685
686 if (ReceivingInterfaceAddr != INADDR_ANY) {
687 // Hack: If we were told to receive on a specific interface address, then
688 // define this to be our ip address:
689 ourAddress = ReceivingInterfaceAddr;
690 }
691
692 if (ourAddress == 0) {
693 // We need to find our source address
694 struct sockaddr_storage fromAddr;
695 fromAddr.ss_family = AF_INET;
696 ((sockaddr_in*)&fromAddr)->sin_addr.s_addr = 0;
697
698 // Get our address by sending a (0-TTL) multicast packet,
699 // receiving it, and looking at the source address used.
700 // (This is kinda bogus, but it provides the best guarantee
701 // that other nodes will think our address is the same as we do.)
702 do {
703 loopbackWorks = 0; // until we learn otherwise
704
705 #ifndef DISABLE_LOOPBACK_IP_ADDRESS_CHECK
706 if (inet_pton(AF_INET, "228.67.43.91", &testAddr.s_addr) != 1) break;
707 // The multicast address here was arbitrary
708 Port testPort(15947); // ditto
709
710 sock = setupDatagramSocket(env, testPort);
711 if (sock < 0) break;
712
713 if (!socketJoinGroup(env, sock, testAddr.s_addr)) break;
714
715 unsigned char testString[] = "hostIdTest";
716 unsigned testStringLength = sizeof testString;
717
718 if (!writeSocket(env, sock, testAddr, testPort.num(), 0,
719 testString, testStringLength)) break;
720
721 // Block until the socket is readable (with a 5-second timeout):
722 fd_set rd_set;
723 FD_ZERO(&rd_set);
724 FD_SET((unsigned)sock, &rd_set);
725 const unsigned numFds = sock+1;
726 struct timeval timeout;
727 timeout.tv_sec = 5;
728 timeout.tv_usec = 0;
729 int result = select(numFds, &rd_set, NULL, NULL, &timeout);
730 if (result <= 0) break;
731
732 unsigned char readBuffer[20];
733 int bytesRead = readSocket(env, sock,
734 readBuffer, sizeof readBuffer,
735 fromAddr);
736 if (bytesRead != (int)testStringLength
737 || strncmp((char*)readBuffer, (char*)testString, testStringLength) != 0) {
738 break;
739 }
740
741 // We use this packet's source address, if it's good:
742 loopbackWorks = !badAddressForUs(fromAddr);
743 #endif
744 } while (0);
745
746 if (sock >= 0) {
747 socketLeaveGroup(env, sock, testAddr.s_addr);
748 closeSocket(sock);
749 }
750
751 if (!loopbackWorks) do {
752 // We couldn't find our address using multicast loopback,
753 // so try instead to look it up directly - by first getting our host name, and then resolving this host name
754 char hostname[100];
755 hostname[0] = '\0';
756 int result = gethostname(hostname, sizeof hostname);
757 if (result != 0 || hostname[0] == '\0') {
758 env.setResultErrMsg("initial gethostname() failed");
759 break;
760 }
761
762 // Try to resolve "hostname" to an IP address:
763 NetAddressList addresses(hostname);
764 NetAddressList::Iterator iter(addresses);
765 NetAddress const* address;
766
767 // Take the first address that's not bad:
768 netAddressBits addr = 0;
769 while ((address = iter.nextAddress()) != NULL) {
770 netAddressBits a = *(netAddressBits*)(address->data());
771 if (!badAddressForUs(a)) {
772 addr = a;
773 break;
774 }
775 }
776
777 // Assign the address that we found to "fromAddr" (as if the 'loopback' method had worked), to simplify the code below:
778 fromAddr.ss_family = AF_INET;
779 ((sockaddr_in*)&fromAddr)->sin_addr.s_addr = addr;
780 } while (0);
781
782 // Make sure we have a good address:
783 if (badAddressForUs(fromAddr)) {
784 char tmp[100];
785 sprintf(tmp, "This computer has an invalid IP address: %s", AddressString(fromAddr).val());
786 env.setResultMsg(tmp);
787 fromAddr.ss_family = AF_INET;
788 ((sockaddr_in*)&fromAddr)->sin_addr.s_addr = 0;
789 }
790
791 ourAddress = fromAddr.ss_family == AF_INET ? ((sockaddr_in*)&fromAddr)->sin_addr.s_addr : 0;
792
793 // Use our newly-discovered IP address, and the current time,
794 // to initialize the random number generator's seed:
795 struct timeval timeNow;
796 gettimeofday(&timeNow, NULL);
797 unsigned seed = ourAddress^timeNow.tv_sec^timeNow.tv_usec;
798 our_srandom(seed);
799 }
800 return ourAddress;
801 }
802
chooseRandomIPv4SSMAddress(UsageEnvironment & env)803 netAddressBits chooseRandomIPv4SSMAddress(UsageEnvironment& env) {
804 // First, a hack to ensure that our random number generator is seeded:
805 (void) ourIPAddress(env);
806
807 // Choose a random address in the range [232.0.1.0, 232.255.255.255)
808 // i.e., [0xE8000100, 0xE8FFFFFF)
809 netAddressBits const first = 0xE8000100, lastPlus1 = 0xE8FFFFFF;
810 netAddressBits const range = lastPlus1 - first;
811
812 return ntohl(first + ((netAddressBits)our_random())%range);
813 }
814
timestampString()815 char const* timestampString() {
816 struct timeval tvNow;
817 gettimeofday(&tvNow, NULL);
818
819 #if !defined(_WIN32_WCE)
820 static char timeString[9]; // holds hh:mm:ss plus trailing '\0'
821
822 time_t tvNow_t = tvNow.tv_sec;
823 char const* ctimeResult = ctime(&tvNow_t);
824 if (ctimeResult == NULL) {
825 sprintf(timeString, "??:??:??");
826 } else {
827 char const* from = &ctimeResult[11];
828 int i;
829 for (i = 0; i < 8; ++i) {
830 timeString[i] = from[i];
831 }
832 timeString[i] = '\0';
833 }
834 #else
835 // WinCE apparently doesn't have "ctime()", so instead, construct
836 // a timestamp string just using the integer and fractional parts
837 // of "tvNow":
838 static char timeString[50];
839 sprintf(timeString, "%lu.%06ld", tvNow.tv_sec, tvNow.tv_usec);
840 #endif
841
842 return (char const*)&timeString;
843 }
844
845 #if (defined(__WIN32__) || defined(_WIN32)) && !defined(__MINGW32__)
846 // For Windoze, we need to implement our own gettimeofday()
847
848 // used to make sure that static variables in gettimeofday() aren't initialized simultaneously by multiple threads
849 static LONG initializeLock_gettimeofday = 0;
850
851 #if !defined(_WIN32_WCE)
852 #include <sys/timeb.h>
853 #endif
854
gettimeofday(struct timeval * tp,int *)855 int gettimeofday(struct timeval* tp, int* /*tz*/) {
856 static LARGE_INTEGER tickFrequency, epochOffset;
857
858 static Boolean isInitialized = False;
859
860 LARGE_INTEGER tickNow;
861
862 #if !defined(_WIN32_WCE)
863 QueryPerformanceCounter(&tickNow);
864 #else
865 tickNow.QuadPart = GetTickCount();
866 #endif
867
868 if (!isInitialized) {
869 if(1 == InterlockedIncrement(&initializeLock_gettimeofday)) {
870 #if !defined(_WIN32_WCE)
871 // For our first call, use "ftime()", so that we get a time with a proper epoch.
872 // For subsequent calls, use "QueryPerformanceCount()", because it's more fine-grain.
873 struct timeb tb;
874 ftime(&tb);
875 tp->tv_sec = tb.time;
876 tp->tv_usec = 1000*tb.millitm;
877
878 // Also get our counter frequency:
879 QueryPerformanceFrequency(&tickFrequency);
880 #else
881 /* FILETIME of Jan 1 1970 00:00:00. */
882 const LONGLONG epoch = 116444736000000000LL;
883 FILETIME fileTime;
884 LARGE_INTEGER time;
885 GetSystemTimeAsFileTime(&fileTime);
886
887 time.HighPart = fileTime.dwHighDateTime;
888 time.LowPart = fileTime.dwLowDateTime;
889
890 // convert to from 100ns time to unix timestamp in seconds, 1000*1000*10
891 tp->tv_sec = (long)((time.QuadPart - epoch) / 10000000L);
892
893 /*
894 GetSystemTimeAsFileTime has just a seconds resolution,
895 thats why wince-version of gettimeofday is not 100% accurate, usec accuracy would be calculated like this:
896 // convert 100 nanoseconds to usec
897 tp->tv_usec= (long)((time.QuadPart - epoch)%10000000L) / 10L;
898 */
899 tp->tv_usec = 0;
900
901 // resolution of GetTickCounter() is always milliseconds
902 tickFrequency.QuadPart = 1000;
903 #endif
904 // compute an offset to add to subsequent counter times, so we get a proper epoch:
905 epochOffset.QuadPart
906 = tp->tv_sec * tickFrequency.QuadPart + (tp->tv_usec * tickFrequency.QuadPart) / 1000000L - tickNow.QuadPart;
907
908 // next caller can use ticks for time calculation
909 isInitialized = True;
910 return 0;
911 } else {
912 InterlockedDecrement(&initializeLock_gettimeofday);
913 // wait until first caller has initialized static values
914 while(!isInitialized){
915 Sleep(1);
916 }
917 }
918 }
919
920 // adjust our tick count so that we get a proper epoch:
921 tickNow.QuadPart += epochOffset.QuadPart;
922
923 tp->tv_sec = (long)(tickNow.QuadPart / tickFrequency.QuadPart);
924 tp->tv_usec = (long)(((tickNow.QuadPart % tickFrequency.QuadPart) * 1000000L) / tickFrequency.QuadPart);
925
926 return 0;
927 }
928 #endif
929 #undef ANDROID_OLD_NDK
930