1/*
2 *  Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
3 *
4 *  Use of this source code is governed by a BSD-style license
5 *  that can be found in the LICENSE file in the root of the source
6 *  tree. An additional intellectual property rights grant can be found
7 *  in the file PATENTS.  All contributing project authors may
8 *  be found in the AUTHORS file in the root of the source tree.
9 */
10
11#include "webrtc/test/channel_transport/udp_socket_posix.h"
12
13#include <errno.h>
14#include <fcntl.h>
15#include <netdb.h>
16#include <stdio.h>
17#include <string.h>
18#include <sys/ioctl.h>
19#include <sys/types.h>
20#include <time.h>
21#include <unistd.h>
22
23#include "webrtc/system_wrappers/include/trace.h"
24#include "webrtc/test/channel_transport/udp_socket_manager_wrapper.h"
25#include "webrtc/test/channel_transport/udp_socket_wrapper.h"
26
27namespace webrtc {
28namespace test {
29UdpSocketPosix::UdpSocketPosix(const int32_t id, UdpSocketManager* mgr,
30                               bool ipV6Enable) : _id(id)
31{
32    WEBRTC_TRACE(kTraceMemory, kTraceTransport, id,
33                 "UdpSocketPosix::UdpSocketPosix()");
34
35    _wantsIncoming = false;
36    _mgr = mgr;
37
38    _obj = NULL;
39    _incomingCb = NULL;
40    _readyForDeletionCond = ConditionVariableWrapper::CreateConditionVariable();
41    _closeBlockingCompletedCond =
42        ConditionVariableWrapper::CreateConditionVariable();
43    _cs = CriticalSectionWrapper::CreateCriticalSection();
44    _readyForDeletion = false;
45    _closeBlockingActive = false;
46    _closeBlockingCompleted= false;
47    if(ipV6Enable)
48    {
49        _socket = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
50    }
51    else {
52        _socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
53    }
54
55    // Set socket to nonblocking mode.
56    int enable_non_blocking = 1;
57    if(ioctl(_socket, FIONBIO, &enable_non_blocking) == -1)
58    {
59        WEBRTC_TRACE(kTraceWarning, kTraceTransport, id,
60                     "Failed to make socket nonblocking");
61    }
62    // Enable close on fork for file descriptor so that it will not block until
63    // forked process terminates.
64    if(fcntl(_socket, F_SETFD, FD_CLOEXEC) == -1)
65    {
66        WEBRTC_TRACE(kTraceWarning, kTraceTransport, id,
67                     "Failed to set FD_CLOEXEC for socket");
68    }
69}
70
71UdpSocketPosix::~UdpSocketPosix()
72{
73    if(_socket != INVALID_SOCKET)
74    {
75        close(_socket);
76        _socket = INVALID_SOCKET;
77    }
78    if(_readyForDeletionCond)
79    {
80        delete _readyForDeletionCond;
81    }
82
83    if(_closeBlockingCompletedCond)
84    {
85        delete _closeBlockingCompletedCond;
86    }
87
88    if(_cs)
89    {
90        delete _cs;
91    }
92}
93
94bool UdpSocketPosix::SetCallback(CallbackObj obj, IncomingSocketCallback cb)
95{
96    _obj = obj;
97    _incomingCb = cb;
98
99    WEBRTC_TRACE(kTraceDebug, kTraceTransport, _id,
100                 "UdpSocketPosix(%p)::SetCallback", this);
101
102    if (_mgr->AddSocket(this))
103      {
104        WEBRTC_TRACE(kTraceDebug, kTraceTransport, _id,
105                     "UdpSocketPosix(%p)::SetCallback socket added to manager",
106                     this);
107        return true;   // socket is now ready for action
108      }
109
110    WEBRTC_TRACE(kTraceDebug, kTraceTransport, _id,
111                 "UdpSocketPosix(%p)::SetCallback error adding me to mgr",
112                 this);
113    return false;
114}
115
116bool UdpSocketPosix::SetSockopt(int32_t level, int32_t optname,
117                                const int8_t* optval, int32_t optlen)
118{
119   if(0 == setsockopt(_socket, level, optname, optval, optlen ))
120   {
121       return true;
122   }
123
124   WEBRTC_TRACE(kTraceError, kTraceTransport, _id,
125                "UdpSocketPosix::SetSockopt(), error:%d", errno);
126   return false;
127}
128
129int32_t UdpSocketPosix::SetTOS(int32_t serviceType)
130{
131    if (SetSockopt(IPPROTO_IP, IP_TOS ,(int8_t*)&serviceType ,4) != 0)
132    {
133        return -1;
134    }
135    return 0;
136}
137
138bool UdpSocketPosix::Bind(const SocketAddress& name)
139{
140    int size = sizeof(sockaddr);
141    if (0 == bind(_socket, reinterpret_cast<const sockaddr*>(&name),size))
142    {
143        return true;
144    }
145    WEBRTC_TRACE(kTraceError, kTraceTransport, _id,
146                 "UdpSocketPosix::Bind() error: %d", errno);
147    return false;
148}
149
150int32_t UdpSocketPosix::SendTo(const int8_t* buf, size_t len,
151                               const SocketAddress& to)
152{
153    int size = sizeof(sockaddr);
154    int retVal = sendto(_socket,buf, len, 0,
155                        reinterpret_cast<const sockaddr*>(&to), size);
156    if(retVal == SOCKET_ERROR)
157    {
158        WEBRTC_TRACE(kTraceError, kTraceTransport, _id,
159                     "UdpSocketPosix::SendTo() error: %d", errno);
160    }
161
162    return retVal;
163}
164
165SOCKET UdpSocketPosix::GetFd() { return _socket; }
166
167bool UdpSocketPosix::ValidHandle()
168{
169    return _socket != INVALID_SOCKET;
170}
171
172bool UdpSocketPosix::SetQos(int32_t /*serviceType*/,
173                            int32_t /*tokenRate*/,
174                            int32_t /*bucketSize*/,
175                            int32_t /*peekBandwith*/,
176                            int32_t /*minPolicedSize*/,
177                            int32_t /*maxSduSize*/,
178                            const SocketAddress& /*stRemName*/,
179                            int32_t /*overrideDSCP*/) {
180  return false;
181}
182
183void UdpSocketPosix::HasIncoming()
184{
185    // replace 2048 with a mcro define and figure out
186    // where 2048 comes from
187    int8_t buf[2048];
188    int retval;
189    SocketAddress from;
190#if defined(WEBRTC_MAC)
191    sockaddr sockaddrfrom;
192    memset(&from, 0, sizeof(from));
193    memset(&sockaddrfrom, 0, sizeof(sockaddrfrom));
194    socklen_t fromlen = sizeof(sockaddrfrom);
195#else
196    memset(&from, 0, sizeof(from));
197    socklen_t fromlen = sizeof(from);
198#endif
199
200#if defined(WEBRTC_MAC)
201        retval = recvfrom(_socket,buf, sizeof(buf), 0,
202                          reinterpret_cast<sockaddr*>(&sockaddrfrom), &fromlen);
203        memcpy(&from, &sockaddrfrom, fromlen);
204        from._sockaddr_storage.sin_family = sockaddrfrom.sa_family;
205#else
206        retval = recvfrom(_socket,buf, sizeof(buf), 0,
207                          reinterpret_cast<sockaddr*>(&from), &fromlen);
208#endif
209
210    switch(retval)
211    {
212    case 0:
213        // The peer has performed an orderly shutdown.
214        break;
215    case SOCKET_ERROR:
216        break;
217    default:
218        if (_wantsIncoming && _incomingCb)
219        {
220          _incomingCb(_obj, buf, retval, &from);
221        }
222        break;
223    }
224}
225
226bool UdpSocketPosix::WantsIncoming() { return _wantsIncoming; }
227
228void UdpSocketPosix::CloseBlocking()
229{
230    _cs->Enter();
231    _closeBlockingActive = true;
232    if(!CleanUp())
233    {
234        _closeBlockingActive = false;
235        _cs->Leave();
236        return;
237    }
238
239    while(!_readyForDeletion)
240    {
241        _readyForDeletionCond->SleepCS(*_cs);
242    }
243    _closeBlockingCompleted = true;
244    _closeBlockingCompletedCond->Wake();
245    _cs->Leave();
246}
247
248void UdpSocketPosix::ReadyForDeletion()
249{
250    _cs->Enter();
251    if(!_closeBlockingActive)
252    {
253        _cs->Leave();
254        return;
255    }
256    close(_socket);
257    _socket = INVALID_SOCKET;
258    _readyForDeletion = true;
259    _readyForDeletionCond->Wake();
260    while(!_closeBlockingCompleted)
261    {
262        _closeBlockingCompletedCond->SleepCS(*_cs);
263    }
264    _cs->Leave();
265}
266
267bool UdpSocketPosix::CleanUp()
268{
269    _wantsIncoming = false;
270
271    if (_socket == INVALID_SOCKET)
272    {
273        return false;
274    }
275
276    WEBRTC_TRACE(kTraceDebug, kTraceTransport, _id,
277                 "calling UdpSocketManager::RemoveSocket()...");
278    _mgr->RemoveSocket(this);
279    // After this, the socket should may be or will be as deleted. Return
280    // immediately.
281    return true;
282}
283
284}  // namespace test
285}  // namespace webrtc
286