1/*
2 * Copyright (C) 2010 Apple Inc. All rights reserved.
3 * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies)
4 * Copyright (C) 2011 Igalia S.L.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
17 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
19 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
20 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
21 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
22 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
23 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
24 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
25 * THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28#include "config.h"
29#include "Connection.h"
30
31#include "DataReference.h"
32#include "SharedMemory.h"
33#include <sys/socket.h>
34#include <unistd.h>
35#include <errno.h>
36#include <fcntl.h>
37#include <poll.h>
38#include <wtf/Assertions.h>
39#include <wtf/Functional.h>
40#include <wtf/StdLibExtras.h>
41#include <wtf/UniStdExtras.h>
42
43#if PLATFORM(GTK)
44#include <glib.h>
45#endif
46
47#ifdef SOCK_SEQPACKET
48#define SOCKET_TYPE SOCK_SEQPACKET
49#else
50#if PLATFORM(GTK)
51#define SOCKET_TYPE SOCK_STREAM
52#else
53#define SOCKET_TYPE SOCK_DGRAM
54#endif
55#endif // SOCK_SEQPACKET
56
57namespace IPC {
58
59static const size_t messageMaxSize = 4096;
60static const size_t attachmentMaxAmount = 255;
61
62enum {
63    MessageBodyIsOutOfLine = 1U << 31
64};
65
66class MessageInfo {
67public:
68    MessageInfo() { }
69
70    MessageInfo(size_t bodySize, size_t initialAttachmentCount)
71        : m_bodySize(bodySize)
72        , m_attachmentCount(initialAttachmentCount)
73        , m_isMessageBodyOutOfLine(false)
74    {
75    }
76
77    void setMessageBodyIsOutOfLine()
78    {
79        ASSERT(!isMessageBodyIsOutOfLine());
80
81        m_isMessageBodyOutOfLine = true;
82        m_attachmentCount++;
83    }
84
85    bool isMessageBodyIsOutOfLine() const { return m_isMessageBodyOutOfLine; }
86
87    size_t bodySize() const { return m_bodySize; }
88
89    size_t attachmentCount() const { return m_attachmentCount; }
90
91private:
92    size_t m_bodySize;
93    size_t m_attachmentCount;
94    bool m_isMessageBodyOutOfLine;
95};
96
97class AttachmentInfo {
98public:
99    AttachmentInfo()
100        : m_type(Attachment::Uninitialized)
101        , m_size(0)
102        , m_isNull(false)
103    {
104    }
105
106    void setType(Attachment::Type type) { m_type = type; }
107    Attachment::Type getType() { return m_type; }
108    void setSize(size_t size)
109    {
110        ASSERT(m_type == Attachment::MappedMemoryType);
111        m_size = size;
112    }
113
114    size_t getSize()
115    {
116        ASSERT(m_type == Attachment::MappedMemoryType);
117        return m_size;
118    }
119
120    // The attachment is not null unless explicitly set.
121    void setNull() { m_isNull = true; }
122    bool isNull() { return m_isNull; }
123
124private:
125    Attachment::Type m_type;
126    size_t m_size;
127    bool m_isNull;
128};
129
130void Connection::platformInitialize(Identifier identifier)
131{
132    m_socketDescriptor = identifier;
133    m_readBuffer.resize(messageMaxSize);
134    m_readBufferSize = 0;
135    m_fileDescriptors.resize(attachmentMaxAmount);
136    m_fileDescriptorsSize = 0;
137}
138
139void Connection::platformInvalidate()
140{
141    // In GTK+ platform the socket is closed by the work queue.
142#if !PLATFORM(GTK)
143    if (m_socketDescriptor != -1)
144        closeWithRetry(m_socketDescriptor);
145#endif
146
147    if (!m_isConnected)
148        return;
149
150#if PLATFORM(GTK) || PLATFORM(EFL)
151    m_connectionQueue->unregisterSocketEventHandler(m_socketDescriptor);
152#endif
153
154    m_socketDescriptor = -1;
155    m_isConnected = false;
156}
157
158template<class T, class iterator>
159class AttachmentResourceGuard {
160public:
161    AttachmentResourceGuard(T& attachments)
162        : m_attachments(attachments)
163    {
164    }
165    ~AttachmentResourceGuard()
166    {
167        iterator end = m_attachments.end();
168        for (iterator i = m_attachments.begin(); i != end; ++i)
169            i->dispose();
170    }
171private:
172    T& m_attachments;
173};
174
175bool Connection::processMessage()
176{
177    if (m_readBufferSize < sizeof(MessageInfo))
178        return false;
179
180    uint8_t* messageData = m_readBuffer.data();
181    MessageInfo messageInfo;
182    memcpy(&messageInfo, messageData, sizeof(messageInfo));
183    messageData += sizeof(messageInfo);
184
185    size_t messageLength = sizeof(MessageInfo) + messageInfo.attachmentCount() * sizeof(AttachmentInfo) + (messageInfo.isMessageBodyIsOutOfLine() ? 0 : messageInfo.bodySize());
186    if (m_readBufferSize < messageLength)
187        return false;
188
189    size_t attachmentFileDescriptorCount = 0;
190    size_t attachmentCount = messageInfo.attachmentCount();
191    std::unique_ptr<AttachmentInfo[]> attachmentInfo;
192
193    if (attachmentCount) {
194        attachmentInfo = std::make_unique<AttachmentInfo[]>(attachmentCount);
195        memcpy(attachmentInfo.get(), messageData, sizeof(AttachmentInfo) * attachmentCount);
196        messageData += sizeof(AttachmentInfo) * attachmentCount;
197
198        for (size_t i = 0; i < attachmentCount; ++i) {
199            switch (attachmentInfo[i].getType()) {
200            case Attachment::MappedMemoryType:
201            case Attachment::SocketType:
202                if (!attachmentInfo[i].isNull())
203                    attachmentFileDescriptorCount++;
204                break;
205            case Attachment::Uninitialized:
206            default:
207                ASSERT_NOT_REACHED();
208                break;
209            }
210        }
211
212        if (messageInfo.isMessageBodyIsOutOfLine())
213            attachmentCount--;
214    }
215
216    Vector<Attachment> attachments(attachmentCount);
217    AttachmentResourceGuard<Vector<Attachment>, Vector<Attachment>::iterator> attachementDisposer(attachments);
218    RefPtr<WebKit::SharedMemory> oolMessageBody;
219
220    size_t fdIndex = 0;
221    for (size_t i = 0; i < attachmentCount; ++i) {
222        int fd = -1;
223        switch (attachmentInfo[i].getType()) {
224        case Attachment::MappedMemoryType:
225            if (!attachmentInfo[i].isNull())
226                fd = m_fileDescriptors[fdIndex++];
227            attachments[attachmentCount - i - 1] = Attachment(fd, attachmentInfo[i].getSize());
228            break;
229        case Attachment::SocketType:
230            if (!attachmentInfo[i].isNull())
231                fd = m_fileDescriptors[fdIndex++];
232            attachments[attachmentCount - i - 1] = Attachment(fd);
233            break;
234        case Attachment::Uninitialized:
235            attachments[attachmentCount - i - 1] = Attachment();
236        default:
237            break;
238        }
239    }
240
241    if (messageInfo.isMessageBodyIsOutOfLine()) {
242        ASSERT(messageInfo.bodySize());
243
244        if (attachmentInfo[attachmentCount].isNull()) {
245            ASSERT_NOT_REACHED();
246            return false;
247        }
248
249        WebKit::SharedMemory::Handle handle;
250        handle.adoptFromAttachment(m_fileDescriptors[attachmentFileDescriptorCount - 1], attachmentInfo[attachmentCount].getSize());
251
252        oolMessageBody = WebKit::SharedMemory::create(handle, WebKit::SharedMemory::ReadOnly);
253        if (!oolMessageBody) {
254            ASSERT_NOT_REACHED();
255            return false;
256        }
257    }
258
259    ASSERT(attachments.size() == (messageInfo.isMessageBodyIsOutOfLine() ? messageInfo.attachmentCount() - 1 : messageInfo.attachmentCount()));
260
261    uint8_t* messageBody = messageData;
262    if (messageInfo.isMessageBodyIsOutOfLine())
263        messageBody = reinterpret_cast<uint8_t*>(oolMessageBody->data());
264
265    auto decoder = std::make_unique<MessageDecoder>(DataReference(messageBody, messageInfo.bodySize()), WTF::move(attachments));
266
267    processIncomingMessage(WTF::move(decoder));
268
269    if (m_readBufferSize > messageLength) {
270        memmove(m_readBuffer.data(), m_readBuffer.data() + messageLength, m_readBufferSize - messageLength);
271        m_readBufferSize -= messageLength;
272    } else
273        m_readBufferSize = 0;
274
275    if (attachmentFileDescriptorCount) {
276        if (m_fileDescriptorsSize > attachmentFileDescriptorCount) {
277            size_t fileDescriptorsLength = attachmentFileDescriptorCount * sizeof(int);
278            memmove(m_fileDescriptors.data(), m_fileDescriptors.data() + fileDescriptorsLength, m_fileDescriptorsSize - fileDescriptorsLength);
279            m_fileDescriptorsSize -= fileDescriptorsLength;
280        } else
281            m_fileDescriptorsSize = 0;
282    }
283
284
285    return true;
286}
287
288static ssize_t readBytesFromSocket(int socketDescriptor, uint8_t* buffer, int count, int* fileDescriptors, size_t* fileDescriptorsCount)
289{
290    struct msghdr message;
291    memset(&message, 0, sizeof(message));
292
293    struct iovec iov[1];
294    memset(&iov, 0, sizeof(iov));
295
296    message.msg_controllen = CMSG_SPACE(sizeof(int) * attachmentMaxAmount);
297    auto attachmentDescriptorBuffer = std::make_unique<char[]>(message.msg_controllen);
298    memset(attachmentDescriptorBuffer.get(), 0, message.msg_controllen);
299    message.msg_control = attachmentDescriptorBuffer.get();
300
301    iov[0].iov_base = buffer;
302    iov[0].iov_len = count;
303
304    message.msg_iov = iov;
305    message.msg_iovlen = 1;
306
307    while (true) {
308        ssize_t bytesRead = recvmsg(socketDescriptor, &message, 0);
309
310        if (bytesRead < 0) {
311            if (errno == EINTR)
312                continue;
313
314            return -1;
315        }
316
317        bool found = false;
318        struct cmsghdr* controlMessage;
319        for (controlMessage = CMSG_FIRSTHDR(&message); controlMessage; controlMessage = CMSG_NXTHDR(&message, controlMessage)) {
320            if (controlMessage->cmsg_level == SOL_SOCKET && controlMessage->cmsg_type == SCM_RIGHTS) {
321                *fileDescriptorsCount = (controlMessage->cmsg_len - CMSG_LEN(0)) / sizeof(int);
322                memcpy(fileDescriptors, CMSG_DATA(controlMessage), sizeof(int) * *fileDescriptorsCount);
323
324                for (size_t i = 0; i < *fileDescriptorsCount; ++i) {
325                    while (fcntl(fileDescriptors[i], F_SETFD, FD_CLOEXEC) == -1) {
326                        if (errno != EINTR) {
327                            ASSERT_NOT_REACHED();
328                            break;
329                        }
330                    }
331                }
332
333                found = true;
334                break;
335            }
336        }
337
338        if (!found)
339            *fileDescriptorsCount = 0;
340
341        return bytesRead;
342    }
343
344    return -1;
345}
346
347void Connection::readyReadHandler()
348{
349    while (true) {
350        size_t fileDescriptorsCount = 0;
351        size_t bytesToRead = m_readBuffer.size() - m_readBufferSize;
352        ssize_t bytesRead = readBytesFromSocket(m_socketDescriptor, m_readBuffer.data() + m_readBufferSize, bytesToRead,
353                                                m_fileDescriptors.data() + m_fileDescriptorsSize, &fileDescriptorsCount);
354
355        if (bytesRead < 0) {
356            // EINTR was already handled by readBytesFromSocket.
357            if (errno == EAGAIN || errno == EWOULDBLOCK)
358                return;
359
360            WTFLogAlways("Error receiving IPC message on socket %d in process %d: %s", m_socketDescriptor, getpid(), strerror(errno));
361            connectionDidClose();
362            return;
363        }
364
365        m_readBufferSize += bytesRead;
366        m_fileDescriptorsSize += fileDescriptorsCount;
367
368        if (!bytesRead) {
369            connectionDidClose();
370            return;
371        }
372
373        // Process messages from data received.
374        while (true) {
375            if (!processMessage())
376                break;
377        }
378    }
379}
380
381bool Connection::open()
382{
383    int flags = fcntl(m_socketDescriptor, F_GETFL, 0);
384    while (fcntl(m_socketDescriptor, F_SETFL, flags | O_NONBLOCK) == -1) {
385        if (errno != EINTR) {
386            ASSERT_NOT_REACHED();
387            return false;
388        }
389    }
390
391    m_isConnected = true;
392#if PLATFORM(GTK)
393    RefPtr<Connection> protector(this);
394    m_connectionQueue->registerSocketEventHandler(m_socketDescriptor,
395        [=] {
396            protector->readyReadHandler();
397        },
398        [=] {
399            protector->connectionDidClose();
400        });
401#elif PLATFORM(EFL)
402    RefPtr<Connection> protector(this);
403    m_connectionQueue->registerSocketEventHandler(m_socketDescriptor,
404        [protector] {
405            protector->readyReadHandler();
406        });
407#endif
408
409    // Schedule a call to readyReadHandler. Data may have arrived before installation of the signal
410    // handler.
411    m_connectionQueue->dispatch(WTF::bind(&Connection::readyReadHandler, this));
412
413    return true;
414}
415
416bool Connection::platformCanSendOutgoingMessages() const
417{
418    return m_isConnected;
419}
420
421bool Connection::sendOutgoingMessage(std::unique_ptr<MessageEncoder> encoder)
422{
423    COMPILE_ASSERT(sizeof(MessageInfo) + attachmentMaxAmount * sizeof(size_t) <= messageMaxSize, AttachmentsFitToMessageInline);
424
425    Vector<Attachment> attachments = encoder->releaseAttachments();
426    AttachmentResourceGuard<Vector<Attachment>, Vector<Attachment>::iterator> attachementDisposer(attachments);
427
428    if (attachments.size() > (attachmentMaxAmount - 1)) {
429        ASSERT_NOT_REACHED();
430        return false;
431    }
432
433    MessageInfo messageInfo(encoder->bufferSize(), attachments.size());
434    size_t messageSizeWithBodyInline = sizeof(messageInfo) + (attachments.size() * sizeof(AttachmentInfo)) + encoder->bufferSize();
435    if (messageSizeWithBodyInline > messageMaxSize && encoder->bufferSize()) {
436        RefPtr<WebKit::SharedMemory> oolMessageBody = WebKit::SharedMemory::create(encoder->bufferSize());
437        if (!oolMessageBody)
438            return false;
439
440        WebKit::SharedMemory::Handle handle;
441        if (!oolMessageBody->createHandle(handle, WebKit::SharedMemory::ReadOnly))
442            return false;
443
444        messageInfo.setMessageBodyIsOutOfLine();
445
446        memcpy(oolMessageBody->data(), encoder->buffer(), encoder->bufferSize());
447
448        attachments.append(handle.releaseToAttachment());
449    }
450
451    struct msghdr message;
452    memset(&message, 0, sizeof(message));
453
454    struct iovec iov[3];
455    memset(&iov, 0, sizeof(iov));
456
457    message.msg_iov = iov;
458    int iovLength = 1;
459
460    iov[0].iov_base = reinterpret_cast<void*>(&messageInfo);
461    iov[0].iov_len = sizeof(messageInfo);
462
463    auto attachmentInfo = std::make_unique<AttachmentInfo[]>(attachments.size());
464
465    size_t attachmentFDBufferLength = 0;
466    if (!attachments.isEmpty()) {
467        for (size_t i = 0; i < attachments.size(); ++i) {
468            if (attachments[i].fileDescriptor() != -1)
469                attachmentFDBufferLength++;
470        }
471    }
472    auto attachmentFDBuffer = std::make_unique<char[]>(CMSG_SPACE(sizeof(int) * attachmentFDBufferLength));
473
474    if (!attachments.isEmpty()) {
475        int* fdPtr = 0;
476
477        if (attachmentFDBufferLength) {
478            message.msg_control = attachmentFDBuffer.get();
479            message.msg_controllen = CMSG_SPACE(sizeof(int) * attachmentFDBufferLength);
480            memset(message.msg_control, 0, message.msg_controllen);
481
482            struct cmsghdr* cmsg = CMSG_FIRSTHDR(&message);
483            cmsg->cmsg_level = SOL_SOCKET;
484            cmsg->cmsg_type = SCM_RIGHTS;
485            cmsg->cmsg_len = CMSG_LEN(sizeof(int) * attachmentFDBufferLength);
486
487            fdPtr = reinterpret_cast<int*>(CMSG_DATA(cmsg));
488        }
489
490        int fdIndex = 0;
491        for (size_t i = 0; i < attachments.size(); ++i) {
492            attachmentInfo[i].setType(attachments[i].type());
493
494            switch (attachments[i].type()) {
495            case Attachment::MappedMemoryType:
496                attachmentInfo[i].setSize(attachments[i].size());
497                // Fall trhough, set file descriptor or null.
498            case Attachment::SocketType:
499                if (attachments[i].fileDescriptor() != -1) {
500                    ASSERT(fdPtr);
501                    fdPtr[fdIndex++] = attachments[i].fileDescriptor();
502                } else
503                    attachmentInfo[i].setNull();
504                break;
505            case Attachment::Uninitialized:
506            default:
507                break;
508            }
509        }
510
511        iov[iovLength].iov_base = attachmentInfo.get();
512        iov[iovLength].iov_len = sizeof(AttachmentInfo) * attachments.size();
513        ++iovLength;
514    }
515
516    if (!messageInfo.isMessageBodyIsOutOfLine() && encoder->bufferSize()) {
517        iov[iovLength].iov_base = reinterpret_cast<void*>(encoder->buffer());
518        iov[iovLength].iov_len = encoder->bufferSize();
519        ++iovLength;
520    }
521
522    message.msg_iovlen = iovLength;
523
524    int bytesSent = 0;
525    while ((bytesSent = sendmsg(m_socketDescriptor, &message, 0)) == -1) {
526        if (errno == EINTR)
527            continue;
528        if (errno == EAGAIN || errno == EWOULDBLOCK) {
529            struct pollfd pollfd;
530
531            pollfd.fd = m_socketDescriptor;
532            pollfd.events = POLLOUT;
533            pollfd.revents = 0;
534            poll(&pollfd, 1, -1);
535            continue;
536        }
537
538        WTFLogAlways("Error sending IPC message: %s", strerror(errno));
539        return false;
540    }
541    return true;
542}
543
544Connection::SocketPair Connection::createPlatformConnection(unsigned options)
545{
546    int sockets[2];
547    RELEASE_ASSERT(socketpair(AF_UNIX, SOCKET_TYPE, 0, sockets) != -1);
548
549    if (options & SetCloexecOnServer) {
550        // Don't expose the child socket to the parent process.
551        while (fcntl(sockets[1], F_SETFD, FD_CLOEXEC)  == -1)
552            RELEASE_ASSERT(errno != EINTR);
553    }
554
555    if (options & SetCloexecOnClient) {
556        // Don't expose the parent socket to potential future children.
557        while (fcntl(sockets[0], F_SETFD, FD_CLOEXEC) == -1)
558            RELEASE_ASSERT(errno != EINTR);
559    }
560
561    SocketPair socketPair = { sockets[0], sockets[1] };
562    return socketPair;
563}
564
565void Connection::willSendSyncMessage(unsigned flags)
566{
567    UNUSED_PARAM(flags);
568}
569
570void Connection::didReceiveSyncReply(unsigned flags)
571{
572    UNUSED_PARAM(flags);
573}
574
575} // namespace IPC
576