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// "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>
25extern "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:
48netAddressBits SendingInterfaceAddr = INADDR_ANY;
49netAddressBits ReceivingInterfaceAddr = INADDR_ANY;
50
51static void socketErr(UsageEnvironment& env, char const* errorMsg) {
52 env.setResultErrMsg(errorMsg);
53}
54
55NoReuse::NoReuse(UsageEnvironment& env)
56 : fEnv(env) {
57 groupsockPriv(fEnv)->reuseFlag = 0;
58}
59
60NoReuse::~NoReuse() {
61 groupsockPriv(fEnv)->reuseFlag = 1;
62 reclaimGroupsockPriv(fEnv);
63}
64
65
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
76void 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
85static 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
103int 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
184Boolean 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
197Boolean 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
227Boolean 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 int const keepalive_count = 5;
244 if (setsockopt(sock, IPPROTO_TCP, TCP_KEEPCNT, (void*)&keepalive_count, sizeof keepalive_count) < 0) {
245 return False;
246 }
247
248 int const keepalive_interval = 20;
249 if (setsockopt(sock, IPPROTO_TCP, TCP_KEEPINTVL, (void*)&keepalive_interval, sizeof keepalive_interval) < 0) {
250 return False;
251 }
252#endif
253
254 return True;
255}
256
257int setupStreamSocket(UsageEnvironment& env,
258 Port port, Boolean makeNonBlocking, Boolean setKeepAlive) {
259 if (!initializeWinsockIfNecessary()) {
260 socketErr(env, "Failed to initialize 'winsock': ");
261 return -1;
262 }
263
264 int newSocket = createSocket(SOCK_STREAM);
265 if (newSocket < 0) {
266 socketErr(env, "unable to create stream socket: ");
267 return newSocket;
268 }
269
270 int reuseFlag = groupsockPriv(env)->reuseFlag;
271 reclaimGroupsockPriv(env);
272 if (setsockopt(newSocket, SOL_SOCKET, SO_REUSEADDR,
273 (const char*)&reuseFlag, sizeof reuseFlag) < 0) {
274 socketErr(env, "setsockopt(SO_REUSEADDR) error: ");
275 closeSocket(newSocket);
276 return -1;
277 }
278
279 // SO_REUSEPORT doesn't really make sense for TCP sockets, so we
280 // normally don't set them. However, if you really want to do this
281 // #define REUSE_FOR_TCP
282#ifdef REUSE_FOR_TCP
283#if defined(__WIN32__) || defined(_WIN32)
284 // Windoze doesn't properly handle SO_REUSEPORT
285#else
286#ifdef SO_REUSEPORT
287 if (setsockopt(newSocket, SOL_SOCKET, SO_REUSEPORT,
288 (const char*)&reuseFlag, sizeof reuseFlag) < 0) {
289 socketErr(env, "setsockopt(SO_REUSEPORT) error: ");
290 closeSocket(newSocket);
291 return -1;
292 }
293#endif
294#endif
295#endif
296
297 // Note: Windoze requires binding, even if the port number is 0
298#if defined(__WIN32__) || defined(_WIN32)
299#else
300 if (port.num() != 0 || ReceivingInterfaceAddr != INADDR_ANY) {
301#endif
302 MAKE_SOCKADDR_IN(name, ReceivingInterfaceAddr, port.num());
303 if (bind(newSocket, (struct sockaddr*)&name, sizeof name) != 0) {
304 char tmpBuffer[100];
305 sprintf(tmpBuffer, "bind() error (port number: %d): ",
306 ntohs(port.num()));
307 socketErr(env, tmpBuffer);
308 closeSocket(newSocket);
309 return -1;
310 }
311#if defined(__WIN32__) || defined(_WIN32)
312#else
313 }
314#endif
315
316 if (makeNonBlocking) {
317 if (!makeSocketNonBlocking(newSocket)) {
318 socketErr(env, "failed to make non-blocking: ");
319 closeSocket(newSocket);
320 return -1;
321 }
322 }
323
324 // Set the keep alive mechanism for the TCP socket, to avoid "ghost sockets"
325 // that remain after an interrupted communication.
326 if (setKeepAlive) {
327 if (!setSocketKeepAlive(newSocket)) {
328 socketErr(env, "failed to set keep alive: ");
329 closeSocket(newSocket);
330 return -1;
331 }
332 }
333
334 return newSocket;
335}
336
337int readSocket(UsageEnvironment& env,
338 int socket, unsigned char* buffer, unsigned bufferSize,
339 struct sockaddr_in& fromAddress) {
340 SOCKLEN_T addressSize = sizeof fromAddress;
341 int bytesRead = recvfrom(socket, (char*)buffer, bufferSize, 0,
342 (struct sockaddr*)&fromAddress,
343 &addressSize);
344 if (bytesRead < 0) {
345 //##### HACK to work around bugs in Linux and Windows:
346 int err = env.getErrno();
347 if (err == 111 /*ECONNREFUSED (Linux)*/
348#if defined(__WIN32__) || defined(_WIN32)
349 // What a piece of crap Windows is. Sometimes
350 // recvfrom() returns -1, but with an 'errno' of 0.
351 // This appears not to be a real error; just treat
352 // it as if it were a read of zero bytes, and hope
353 // we don't have to do anything else to 'reset'
354 // this alleged error:
355 || err == 0 || err == EWOULDBLOCK
356#else
357 || err == EAGAIN
358#endif
359 || err == 113 /*EHOSTUNREACH (Linux)*/) { // Why does Linux return this for datagram sock?
360 fromAddress.sin_addr.s_addr = 0;
361 return 0;
362 }
363 //##### END HACK
364 socketErr(env, "recvfrom() error: ");
365 } else if (bytesRead == 0) {
366 // "recvfrom()" on a stream socket can return 0 if the remote end has closed the connection. Treat this as an error:
367 return -1;
368 }
369
370 return bytesRead;
371}
372
373Boolean writeSocket(UsageEnvironment& env,
374 int socket, struct in_addr address, portNumBits portNum,
375 u_int8_t ttlArg,
376 unsigned char* buffer, unsigned bufferSize) {
377 // Before sending, set the socket's TTL:
378#if defined(__WIN32__) || defined(_WIN32)
379#define TTL_TYPE int
380#else
381#define TTL_TYPE u_int8_t
382#endif
383 TTL_TYPE ttl = (TTL_TYPE)ttlArg;
384 if (setsockopt(socket, IPPROTO_IP, IP_MULTICAST_TTL,
385 (const char*)&ttl, sizeof ttl) < 0) {
386 socketErr(env, "setsockopt(IP_MULTICAST_TTL) error: ");
387 return False;
388 }
389
390 return writeSocket(env, socket, address, portNum, buffer, bufferSize);
391}
392
393Boolean writeSocket(UsageEnvironment& env,
394 int socket, struct in_addr address, portNumBits portNum,
395 unsigned char* buffer, unsigned bufferSize) {
396 do {
397 MAKE_SOCKADDR_IN(dest, address.s_addr, portNum);
398 int bytesSent = sendto(socket, (char*)buffer, bufferSize, 0,
399 (struct sockaddr*)&dest, sizeof dest);
400 if (bytesSent != (int)bufferSize) {
401 char tmpBuf[100];
402 sprintf(tmpBuf, "writeSocket(%d), sendTo() error: wrote %d bytes instead of %u: ", socket, bytesSent, bufferSize);
403 socketErr(env, tmpBuf);
404 break;
405 }
406
407 return True;
408 } while (0);
409
410 return False;
411}
412
413void ignoreSigPipeOnSocket(int socketNum) {
414 #ifdef USE_SIGNALS
415 #ifdef SO_NOSIGPIPE
416 int set_option = 1;
417 setsockopt(socketNum, SOL_SOCKET, SO_NOSIGPIPE, &set_option, sizeof set_option);
418 #else
419 signal(SIGPIPE, SIG_IGN);
420 #endif
421 #endif
422}
423
424static unsigned getBufferSize(UsageEnvironment& env, int bufOptName,
425 int socket) {
426 unsigned curSize;
427 SOCKLEN_T sizeSize = sizeof curSize;
428 if (getsockopt(socket, SOL_SOCKET, bufOptName,
429 (char*)&curSize, &sizeSize) < 0) {
430 socketErr(env, "getBufferSize() error: ");
431 return 0;
432 }
433
434 return curSize;
435}
436unsigned getSendBufferSize(UsageEnvironment& env, int socket) {
437 return getBufferSize(env, SO_SNDBUF, socket);
438}
439unsigned getReceiveBufferSize(UsageEnvironment& env, int socket) {
440 return getBufferSize(env, SO_RCVBUF, socket);
441}
442
443static unsigned setBufferTo(UsageEnvironment& env, int bufOptName,
444 int socket, unsigned requestedSize) {
445 SOCKLEN_T sizeSize = sizeof requestedSize;
446 setsockopt(socket, SOL_SOCKET, bufOptName, (char*)&requestedSize, sizeSize);
447
448 // Get and return the actual, resulting buffer size:
449 return getBufferSize(env, bufOptName, socket);
450}
451unsigned setSendBufferTo(UsageEnvironment& env,
452 int socket, unsigned requestedSize) {
453 return setBufferTo(env, SO_SNDBUF, socket, requestedSize);
454}
455unsigned setReceiveBufferTo(UsageEnvironment& env,
456 int socket, unsigned requestedSize) {
457 return setBufferTo(env, SO_RCVBUF, socket, requestedSize);
458}
459
460static unsigned increaseBufferTo(UsageEnvironment& env, int bufOptName,
461 int socket, unsigned requestedSize) {
462 // First, get the current buffer size. If it's already at least
463 // as big as what we're requesting, do nothing.
464 unsigned curSize = getBufferSize(env, bufOptName, socket);
465
466 // Next, try to increase the buffer to the requested size,
467 // or to some smaller size, if that's not possible:
468 while (requestedSize > curSize) {
469 SOCKLEN_T sizeSize = sizeof requestedSize;
470 if (setsockopt(socket, SOL_SOCKET, bufOptName,
471 (char*)&requestedSize, sizeSize) >= 0) {
472 // success
473 return requestedSize;
474 }
475 requestedSize = (requestedSize+curSize)/2;
476 }
477
478 return getBufferSize(env, bufOptName, socket);
479}
480unsigned increaseSendBufferTo(UsageEnvironment& env,
481 int socket, unsigned requestedSize) {
482 return increaseBufferTo(env, SO_SNDBUF, socket, requestedSize);
483}
484unsigned increaseReceiveBufferTo(UsageEnvironment& env,
485 int socket, unsigned requestedSize) {
486 return increaseBufferTo(env, SO_RCVBUF, socket, requestedSize);
487}
488
489static void clearMulticastAllSocketOption(int socket) {
490#ifdef IP_MULTICAST_ALL
491 // This option is defined in modern versions of Linux to overcome a bug in the Linux kernel's default behavior.
492 // When set to 0, it ensures that we receive only packets that were sent to the specified IP multicast address,
493 // even if some other process on the same system has joined a different multicast group with the same port number.
494 int multicastAll = 0;
495 (void)setsockopt(socket, IPPROTO_IP, IP_MULTICAST_ALL, (void*)&multicastAll, sizeof multicastAll);
496 // Ignore the call's result. Should it fail, we'll still receive packets (just perhaps more than intended)
497#endif
498}
499
500Boolean socketJoinGroup(UsageEnvironment& env, int socket,
501 netAddressBits groupAddress){
502 if (!IsMulticastAddress(groupAddress)) return True; // ignore this case
503
504 struct ip_mreq imr;
505 imr.imr_multiaddr.s_addr = groupAddress;
506 imr.imr_interface.s_addr = ReceivingInterfaceAddr;
507 if (setsockopt(socket, IPPROTO_IP, IP_ADD_MEMBERSHIP,
508 (const char*)&imr, sizeof (struct ip_mreq)) < 0) {
509#if defined(__WIN32__) || defined(_WIN32)
510 if (env.getErrno() != 0) {
511 // That piece-of-shit toy operating system (Windows) sometimes lies
512 // about setsockopt() failing!
513#endif
514 socketErr(env, "setsockopt(IP_ADD_MEMBERSHIP) error: ");
515 return False;
516#if defined(__WIN32__) || defined(_WIN32)
517 }
518#endif
519 }
520
521 clearMulticastAllSocketOption(socket);
522
523 return True;
524}
525
526Boolean socketLeaveGroup(UsageEnvironment&, int socket,
527 netAddressBits groupAddress) {
528 if (!IsMulticastAddress(groupAddress)) return True; // ignore this case
529
530 struct ip_mreq imr;
531 imr.imr_multiaddr.s_addr = groupAddress;
532 imr.imr_interface.s_addr = ReceivingInterfaceAddr;
533 if (setsockopt(socket, IPPROTO_IP, IP_DROP_MEMBERSHIP,
534 (const char*)&imr, sizeof (struct ip_mreq)) < 0) {
535 return False;
536 }
537
538 return True;
539}
540
541// The source-specific join/leave operations require special setsockopt()
542// commands, and a special structure (ip_mreq_source). If the include files
543// didn't define these, we do so here:
544#if !defined(IP_ADD_SOURCE_MEMBERSHIP)
545struct ip_mreq_source {
546 struct in_addr imr_multiaddr; /* IP multicast address of group */
547 struct in_addr imr_sourceaddr; /* IP address of source */
548 struct in_addr imr_interface; /* local IP address of interface */
549};
550#endif
551
552#ifndef IP_ADD_SOURCE_MEMBERSHIP
553
554#ifdef LINUX
555#define IP_ADD_SOURCE_MEMBERSHIP 39
556#define IP_DROP_SOURCE_MEMBERSHIP 40
557#else
558#define IP_ADD_SOURCE_MEMBERSHIP 25
559#define IP_DROP_SOURCE_MEMBERSHIP 26
560#endif
561
562#endif
563
564Boolean socketJoinGroupSSM(UsageEnvironment& env, int socket,
565 netAddressBits groupAddress,
566 netAddressBits sourceFilterAddr) {
567 if (!IsMulticastAddress(groupAddress)) return True; // ignore this case
568
569 struct ip_mreq_source imr;
570#if ANDROID_OLD_NDK
571 imr.imr_multiaddr = groupAddress;
572 imr.imr_sourceaddr = sourceFilterAddr;
573 imr.imr_interface = ReceivingInterfaceAddr;
574#else
575 imr.imr_multiaddr.s_addr = groupAddress;
576 imr.imr_sourceaddr.s_addr = sourceFilterAddr;
577 imr.imr_interface.s_addr = ReceivingInterfaceAddr;
578#endif
579 if (setsockopt(socket, IPPROTO_IP, IP_ADD_SOURCE_MEMBERSHIP,
580 (const char*)&imr, sizeof (struct ip_mreq_source)) < 0) {
581 socketErr(env, "setsockopt(IP_ADD_SOURCE_MEMBERSHIP) error: ");
582 return False;
583 }
584
585 clearMulticastAllSocketOption(socket);
586
587 return True;
588}
589
590Boolean socketLeaveGroupSSM(UsageEnvironment& /*env*/, int socket,
591 netAddressBits groupAddress,
592 netAddressBits sourceFilterAddr) {
593 if (!IsMulticastAddress(groupAddress)) return True; // ignore this case
594
595 struct ip_mreq_source imr;
596#if ANDROID_OLD_NDK
597 imr.imr_multiaddr = groupAddress;
598 imr.imr_sourceaddr = sourceFilterAddr;
599 imr.imr_interface = ReceivingInterfaceAddr;
600#else
601 imr.imr_multiaddr.s_addr = groupAddress;
602 imr.imr_sourceaddr.s_addr = sourceFilterAddr;
603 imr.imr_interface.s_addr = ReceivingInterfaceAddr;
604#endif
605 if (setsockopt(socket, IPPROTO_IP, IP_DROP_SOURCE_MEMBERSHIP,
606 (const char*)&imr, sizeof (struct ip_mreq_source)) < 0) {
607 return False;
608 }
609
610 return True;
611}
612
613static Boolean getSourcePort0(int socket, portNumBits& resultPortNum/*host order*/) {
614 sockaddr_in test; test.sin_port = 0;
615 SOCKLEN_T len = sizeof test;
616 if (getsockname(socket, (struct sockaddr*)&test, &len) < 0) return False;
617
618 resultPortNum = ntohs(test.sin_port);
619 return True;
620}
621
622Boolean getSourcePort(UsageEnvironment& env, int socket, Port& port) {
623 portNumBits portNum = 0;
624 if (!getSourcePort0(socket, portNum) || portNum == 0) {
625 // Hack - call bind(), then try again:
626 MAKE_SOCKADDR_IN(name, INADDR_ANY, 0);
627 bind(socket, (struct sockaddr*)&name, sizeof name);
628
629 if (!getSourcePort0(socket, portNum) || portNum == 0) {
630 socketErr(env, "getsockname() error: ");
631 return False;
632 }
633 }
634
635 port = Port(portNum);
636 return True;
637}
638
639static Boolean badAddressForUs(netAddressBits addr) {
640 // Check for some possible erroneous addresses:
641 netAddressBits nAddr = htonl(addr);
642 return (nAddr == 0x7F000001 /* 127.0.0.1 */
643 || nAddr == 0
644 || nAddr == (netAddressBits)(~0));
645}
646
647Boolean loopbackWorks = 1;
648
649netAddressBits ourIPAddress(UsageEnvironment& env) {
650 static netAddressBits ourAddress = 0;
651 int sock = -1;
652 struct in_addr testAddr;
653
654 if (ReceivingInterfaceAddr != INADDR_ANY) {
655 // Hack: If we were told to receive on a specific interface address, then
656 // define this to be our ip address:
657 ourAddress = ReceivingInterfaceAddr;
658 }
659
660 if (ourAddress == 0) {
661 // We need to find our source address
662 struct sockaddr_in fromAddr;
663 fromAddr.sin_addr.s_addr = 0;
664
665 // Get our address by sending a (0-TTL) multicast packet,
666 // receiving it, and looking at the source address used.
667 // (This is kinda bogus, but it provides the best guarantee
668 // that other nodes will think our address is the same as we do.)
669 do {
670 loopbackWorks = 0; // until we learn otherwise
671
672#ifndef DISABLE_LOOPBACK_IP_ADDRESS_CHECK
673 testAddr.s_addr = our_inet_addr("228.67.43.91"); // arbitrary
674 Port testPort(15947); // ditto
675
676 sock = setupDatagramSocket(env, testPort);
677 if (sock < 0) break;
678
679 if (!socketJoinGroup(env, sock, testAddr.s_addr)) break;
680
681 unsigned char testString[] = "hostIdTest";
682 unsigned testStringLength = sizeof testString;
683
684 if (!writeSocket(env, sock, testAddr, testPort.num(), 0,
685 testString, testStringLength)) break;
686
687 // Block until the socket is readable (with a 5-second timeout):
688 fd_set rd_set;
689 FD_ZERO(&rd_set);
690 FD_SET((unsigned)sock, &rd_set);
691 const unsigned numFds = sock+1;
692 struct timeval timeout;
693 timeout.tv_sec = 5;
694 timeout.tv_usec = 0;
695 int result = select(numFds, &rd_set, NULL, NULL, &timeout);
696 if (result <= 0) break;
697
698 unsigned char readBuffer[20];
699 int bytesRead = readSocket(env, sock,
700 readBuffer, sizeof readBuffer,
701 fromAddr);
702 if (bytesRead != (int)testStringLength
703 || strncmp((char*)readBuffer, (char*)testString, testStringLength) != 0) {
704 break;
705 }
706
707 // We use this packet's source address, if it's good:
708 loopbackWorks = !badAddressForUs(fromAddr.sin_addr.s_addr);
709#endif
710 } while (0);
711
712 if (sock >= 0) {
713 socketLeaveGroup(env, sock, testAddr.s_addr);
714 closeSocket(sock);
715 }
716
717 if (!loopbackWorks) do {
718 // We couldn't find our address using multicast loopback,
719 // so try instead to look it up directly - by first getting our host name, and then resolving this host name
720 char hostname[100];
721 hostname[0] = '\0';
722 int result = gethostname(hostname, sizeof hostname);
723 if (result != 0 || hostname[0] == '\0') {
724 env.setResultErrMsg("initial gethostname() failed");
725 break;
726 }
727
728 // Try to resolve "hostname" to an IP address:
729 NetAddressList addresses(hostname);
730 NetAddressList::Iterator iter(addresses);
731 NetAddress const* address;
732
733 // Take the first address that's not bad:
734 netAddressBits addr = 0;
735 while ((address = iter.nextAddress()) != NULL) {
736 netAddressBits a = *(netAddressBits*)(address->data());
737 if (!badAddressForUs(a)) {
738 addr = a;
739 break;
740 }
741 }
742
743 // Assign the address that we found to "fromAddr" (as if the 'loopback' method had worked), to simplify the code below:
744 fromAddr.sin_addr.s_addr = addr;
745 } while (0);
746
747 // Make sure we have a good address:
748 netAddressBits from = fromAddr.sin_addr.s_addr;
749 if (badAddressForUs(from)) {
750 char tmp[100];
751 sprintf(tmp, "This computer has an invalid IP address: %s", AddressString(from).val());
752 env.setResultMsg(tmp);
753 from = 0;
754 }
755
756 ourAddress = from;
757
758 // Use our newly-discovered IP address, and the current time,
759 // to initialize the random number generator's seed:
760 struct timeval timeNow;
761 gettimeofday(&timeNow, NULL);
762 unsigned seed = ourAddress^timeNow.tv_sec^timeNow.tv_usec;
763 our_srandom(seed);
764 }
765 return ourAddress;
766}
767
768netAddressBits chooseRandomIPv4SSMAddress(UsageEnvironment& env) {
769 // First, a hack to ensure that our random number generator is seeded:
770 (void) ourIPAddress(env);
771
772 // Choose a random address in the range [232.0.1.0, 232.255.255.255)
773 // i.e., [0xE8000100, 0xE8FFFFFF)
774 netAddressBits const first = 0xE8000100, lastPlus1 = 0xE8FFFFFF;
775 netAddressBits const range = lastPlus1 - first;
776
777 return ntohl(first + ((netAddressBits)our_random())%range);
778}
779
780char const* timestampString() {
781 struct timeval tvNow;
782 gettimeofday(&tvNow, NULL);
783
784#if !defined(_WIN32_WCE)
785 static char timeString[9]; // holds hh:mm:ss plus trailing '\0'
786
787 time_t tvNow_t = tvNow.tv_sec;
788 char const* ctimeResult = ctime(&tvNow_t);
789 if (ctimeResult == NULL) {
790 sprintf(timeString, "??:??:??");
791 } else {
792 char const* from = &ctimeResult[11];
793 int i;
794 for (i = 0; i < 8; ++i) {
795 timeString[i] = from[i];
796 }
797 timeString[i] = '\0';
798 }
799#else
800 // WinCE apparently doesn't have "ctime()", so instead, construct
801 // a timestamp string just using the integer and fractional parts
802 // of "tvNow":
803 static char timeString[50];
804 sprintf(timeString, "%lu.%06ld", tvNow.tv_sec, tvNow.tv_usec);
805#endif
806
807 return (char const*)&timeString;
808}
809
810#if (defined(__WIN32__) || defined(_WIN32)) && !defined(__MINGW32__)
811// For Windoze, we need to implement our own gettimeofday()
812
813// used to make sure that static variables in gettimeofday() aren't initialized simultaneously by multiple threads
814static LONG initializeLock_gettimeofday = 0;
815
816#if !defined(_WIN32_WCE)
817#include <sys/timeb.h>
818#endif
819
820int gettimeofday(struct timeval* tp, int* /*tz*/) {
821 static LARGE_INTEGER tickFrequency, epochOffset;
822
823 static Boolean isInitialized = False;
824
825 LARGE_INTEGER tickNow;
826
827#if !defined(_WIN32_WCE)
828 QueryPerformanceCounter(&tickNow);
829#else
830 tickNow.QuadPart = GetTickCount();
831#endif
832
833 if (!isInitialized) {
834 if(1 == InterlockedIncrement(&initializeLock_gettimeofday)) {
835#if !defined(_WIN32_WCE)
836 // For our first call, use "ftime()", so that we get a time with a proper epoch.
837 // For subsequent calls, use "QueryPerformanceCount()", because it's more fine-grain.
838 struct timeb tb;
839 ftime(&tb);
840 tp->tv_sec = tb.time;
841 tp->tv_usec = 1000*tb.millitm;
842
843 // Also get our counter frequency:
844 QueryPerformanceFrequency(&tickFrequency);
845#else
846 /* FILETIME of Jan 1 1970 00:00:00. */
847 const LONGLONG epoch = 116444736000000000LL;
848 FILETIME fileTime;
849 LARGE_INTEGER time;
850 GetSystemTimeAsFileTime(&fileTime);
851
852 time.HighPart = fileTime.dwHighDateTime;
853 time.LowPart = fileTime.dwLowDateTime;
854
855 // convert to from 100ns time to unix timestamp in seconds, 1000*1000*10
856 tp->tv_sec = (long)((time.QuadPart - epoch) / 10000000L);
857
858 /*
859 GetSystemTimeAsFileTime has just a seconds resolution,
860 thats why wince-version of gettimeofday is not 100% accurate, usec accuracy would be calculated like this:
861 // convert 100 nanoseconds to usec
862 tp->tv_usec= (long)((time.QuadPart - epoch)%10000000L) / 10L;
863 */
864 tp->tv_usec = 0;
865
866 // resolution of GetTickCounter() is always milliseconds
867 tickFrequency.QuadPart = 1000;
868#endif
869 // compute an offset to add to subsequent counter times, so we get a proper epoch:
870 epochOffset.QuadPart
871 = tp->tv_sec * tickFrequency.QuadPart + (tp->tv_usec * tickFrequency.QuadPart) / 1000000L - tickNow.QuadPart;
872
873 // next caller can use ticks for time calculation
874 isInitialized = True;
875 return 0;
876 } else {
877 InterlockedDecrement(&initializeLock_gettimeofday);
878 // wait until first caller has initialized static values
879 while(!isInitialized){
880 Sleep(1);
881 }
882 }
883 }
884
885 // adjust our tick count so that we get a proper epoch:
886 tickNow.QuadPart += epochOffset.QuadPart;
887
888 tp->tv_sec = (long)(tickNow.QuadPart / tickFrequency.QuadPart);
889 tp->tv_usec = (long)(((tickNow.QuadPart % tickFrequency.QuadPart) * 1000000L) / tickFrequency.QuadPart);
890
891 return 0;
892}
893#endif
894#undef ANDROID_OLD_NDK
895