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