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 <wtf/Assertions.h>
38#include <wtf/Functional.h>
39#include <wtf/OwnArrayPtr.h>
40
41#if PLATFORM(QT)
42#include <QPointer>
43#include <QSocketNotifier>
44#elif PLATFORM(GTK)
45#include <glib.h>
46#endif
47
48namespace CoreIPC {
49
50static const size_t messageMaxSize = 4096;
51static const size_t attachmentMaxAmount = 255;
52
53enum {
54    MessageBodyIsOutOfLine = 1U << 31
55};
56
57class MessageInfo {
58public:
59    MessageInfo() { }
60
61    MessageInfo(size_t bodySize, size_t initialAttachmentCount)
62        : m_bodySize(bodySize)
63        , m_attachmentCount(initialAttachmentCount)
64        , m_isMessageBodyOutOfLine(false)
65    {
66    }
67
68    void setMessageBodyIsOutOfLine()
69    {
70        ASSERT(!isMessageBodyIsOutOfLine());
71
72        m_isMessageBodyOutOfLine = true;
73        m_attachmentCount++;
74    }
75
76    bool isMessageBodyIsOutOfLine() const { return m_isMessageBodyOutOfLine; }
77
78    size_t bodySize() const { return m_bodySize; }
79
80    size_t attachmentCount() const { return m_attachmentCount; }
81
82private:
83    size_t m_bodySize;
84    size_t m_attachmentCount;
85    bool m_isMessageBodyOutOfLine;
86};
87
88class AttachmentInfo {
89public:
90    AttachmentInfo()
91        : m_type(Attachment::Uninitialized)
92        , m_size(0)
93        , m_isNull(false)
94    {
95    }
96
97    void setType(Attachment::Type type) { m_type = type; }
98    Attachment::Type getType() { return m_type; }
99    void setSize(size_t size)
100    {
101        ASSERT(m_type == Attachment::MappedMemoryType);
102        m_size = size;
103    }
104
105    size_t getSize()
106    {
107        ASSERT(m_type == Attachment::MappedMemoryType);
108        return m_size;
109    }
110
111    // The attachment is not null unless explicitly set.
112    void setNull() { m_isNull = true; }
113    bool isNull() { return m_isNull; }
114
115private:
116    Attachment::Type m_type;
117    size_t m_size;
118    bool m_isNull;
119};
120
121void Connection::platformInitialize(Identifier identifier)
122{
123    m_socketDescriptor = identifier;
124    m_readBuffer.resize(messageMaxSize);
125    m_readBufferSize = 0;
126    m_fileDescriptors.resize(attachmentMaxAmount);
127    m_fileDescriptorsSize = 0;
128
129#if PLATFORM(QT)
130    m_socketNotifier = 0;
131#endif
132}
133
134void Connection::platformInvalidate()
135{
136    // In GTK+ platform the socket is closed by the work queue.
137#if !PLATFORM(GTK)
138    if (m_socketDescriptor != -1)
139        while (close(m_socketDescriptor) == -1 && errno == EINTR) { }
140#endif
141
142    if (!m_isConnected)
143        return;
144
145#if PLATFORM(GTK)
146    m_connectionQueue->unregisterSocketEventHandler(m_socketDescriptor);
147#endif
148
149#if PLATFORM(QT)
150    delete m_socketNotifier;
151    m_socketNotifier = 0;
152#endif
153
154#if PLATFORM(EFL)
155    m_connectionQueue->unregisterSocketEventHandler(m_socketDescriptor);
156#endif
157
158    m_socketDescriptor = -1;
159    m_isConnected = false;
160}
161
162#if PLATFORM(QT)
163class SocketNotifierResourceGuard {
164public:
165    SocketNotifierResourceGuard(QSocketNotifier* socketNotifier)
166        : m_socketNotifier(socketNotifier)
167    {
168        m_socketNotifier.data()->setEnabled(false);
169    }
170
171    ~SocketNotifierResourceGuard()
172    {
173        if (m_socketNotifier)
174            m_socketNotifier.data()->setEnabled(true);
175    }
176
177private:
178    QPointer<QSocketNotifier> const m_socketNotifier;
179};
180#endif
181
182template<class T, class iterator>
183class AttachmentResourceGuard {
184public:
185    AttachmentResourceGuard(T& attachments)
186        : m_attachments(attachments)
187    {
188    }
189    ~AttachmentResourceGuard()
190    {
191        iterator end = m_attachments.end();
192        for (iterator i = m_attachments.begin(); i != end; ++i)
193            i->dispose();
194    }
195private:
196    T& m_attachments;
197};
198
199bool Connection::processMessage()
200{
201    if (m_readBufferSize < sizeof(MessageInfo))
202        return false;
203
204    uint8_t* messageData = m_readBuffer.data();
205    MessageInfo messageInfo;
206    memcpy(&messageInfo, messageData, sizeof(messageInfo));
207    messageData += sizeof(messageInfo);
208
209    size_t messageLength = sizeof(MessageInfo) + messageInfo.attachmentCount() * sizeof(AttachmentInfo) + (messageInfo.isMessageBodyIsOutOfLine() ? 0 : messageInfo.bodySize());
210    if (m_readBufferSize < messageLength)
211        return false;
212
213    size_t attachmentFileDescriptorCount = 0;
214    size_t attachmentCount = messageInfo.attachmentCount();
215    OwnArrayPtr<AttachmentInfo> attachmentInfo;
216
217    if (attachmentCount) {
218        attachmentInfo = adoptArrayPtr(new AttachmentInfo[attachmentCount]);
219        memcpy(attachmentInfo.get(), messageData, sizeof(AttachmentInfo) * attachmentCount);
220        messageData += sizeof(AttachmentInfo) * attachmentCount;
221
222        for (size_t i = 0; i < attachmentCount; ++i) {
223            switch (attachmentInfo[i].getType()) {
224            case Attachment::MappedMemoryType:
225            case Attachment::SocketType:
226                if (!attachmentInfo[i].isNull())
227                    attachmentFileDescriptorCount++;
228                break;
229            case Attachment::Uninitialized:
230            default:
231                ASSERT_NOT_REACHED();
232                break;
233            }
234        }
235
236        if (messageInfo.isMessageBodyIsOutOfLine())
237            attachmentCount--;
238    }
239
240    Vector<Attachment> attachments(attachmentCount);
241    AttachmentResourceGuard<Vector<Attachment>, Vector<Attachment>::iterator> attachementDisposer(attachments);
242    RefPtr<WebKit::SharedMemory> oolMessageBody;
243
244    size_t fdIndex = 0;
245    for (size_t i = 0; i < attachmentCount; ++i) {
246        int fd = -1;
247        switch (attachmentInfo[i].getType()) {
248        case Attachment::MappedMemoryType:
249            if (!attachmentInfo[i].isNull())
250                fd = m_fileDescriptors[fdIndex++];
251            attachments[attachmentCount - i - 1] = Attachment(fd, attachmentInfo[i].getSize());
252            break;
253        case Attachment::SocketType:
254            if (!attachmentInfo[i].isNull())
255                fd = m_fileDescriptors[fdIndex++];
256            attachments[attachmentCount - i - 1] = Attachment(fd);
257            break;
258        case Attachment::Uninitialized:
259            attachments[attachmentCount - i - 1] = Attachment();
260        default:
261            break;
262        }
263    }
264
265    if (messageInfo.isMessageBodyIsOutOfLine()) {
266        ASSERT(messageInfo.bodySize());
267
268        if (attachmentInfo[attachmentCount].isNull()) {
269            ASSERT_NOT_REACHED();
270            return false;
271        }
272
273        WebKit::SharedMemory::Handle handle;
274        handle.adoptFromAttachment(m_fileDescriptors[attachmentFileDescriptorCount - 1], attachmentInfo[attachmentCount].getSize());
275
276        oolMessageBody = WebKit::SharedMemory::create(handle, WebKit::SharedMemory::ReadOnly);
277        if (!oolMessageBody) {
278            ASSERT_NOT_REACHED();
279            return false;
280        }
281    }
282
283    ASSERT(attachments.size() == (messageInfo.isMessageBodyIsOutOfLine() ? messageInfo.attachmentCount() - 1 : messageInfo.attachmentCount()));
284
285    uint8_t* messageBody = messageData;
286    if (messageInfo.isMessageBodyIsOutOfLine())
287        messageBody = reinterpret_cast<uint8_t*>(oolMessageBody->data());
288
289    OwnPtr<MessageDecoder> decoder;
290    if (attachments.isEmpty())
291        decoder = MessageDecoder::create(DataReference(messageBody, messageInfo.bodySize()));
292    else
293        decoder = MessageDecoder::create(DataReference(messageBody, messageInfo.bodySize()), attachments);
294
295    processIncomingMessage(decoder.release());
296
297    if (m_readBufferSize > messageLength) {
298        memmove(m_readBuffer.data(), m_readBuffer.data() + messageLength, m_readBufferSize - messageLength);
299        m_readBufferSize -= messageLength;
300    } else
301        m_readBufferSize = 0;
302
303    if (attachmentFileDescriptorCount) {
304        if (m_fileDescriptorsSize > attachmentFileDescriptorCount) {
305            size_t fileDescriptorsLength = attachmentFileDescriptorCount * sizeof(int);
306            memmove(m_fileDescriptors.data(), m_fileDescriptors.data() + fileDescriptorsLength, m_fileDescriptorsSize - fileDescriptorsLength);
307            m_fileDescriptorsSize -= fileDescriptorsLength;
308        } else
309            m_fileDescriptorsSize = 0;
310    }
311
312
313    return true;
314}
315
316static ssize_t readBytesFromSocket(int socketDescriptor, uint8_t* buffer, int count, int* fileDescriptors, size_t* fileDescriptorsCount)
317{
318    struct msghdr message;
319    memset(&message, 0, sizeof(message));
320
321    struct iovec iov[1];
322    memset(&iov, 0, sizeof(iov));
323
324    message.msg_controllen = CMSG_SPACE(sizeof(int) * attachmentMaxAmount);
325    OwnArrayPtr<char> attachmentDescriptorBuffer = adoptArrayPtr(new char[message.msg_controllen]);
326    memset(attachmentDescriptorBuffer.get(), 0, message.msg_controllen);
327    message.msg_control = attachmentDescriptorBuffer.get();
328
329    iov[0].iov_base = buffer;
330    iov[0].iov_len = count;
331
332    message.msg_iov = iov;
333    message.msg_iovlen = 1;
334
335    while (true) {
336        ssize_t bytesRead = recvmsg(socketDescriptor, &message, 0);
337
338        if (bytesRead < 0) {
339            if (errno == EINTR)
340                continue;
341
342            return -1;
343        }
344
345        bool found = false;
346        struct cmsghdr* controlMessage;
347        for (controlMessage = CMSG_FIRSTHDR(&message); controlMessage; controlMessage = CMSG_NXTHDR(&message, controlMessage)) {
348            if (controlMessage->cmsg_level == SOL_SOCKET && controlMessage->cmsg_type == SCM_RIGHTS) {
349                *fileDescriptorsCount = (controlMessage->cmsg_len - CMSG_LEN(0)) / sizeof(int);
350                memcpy(fileDescriptors, CMSG_DATA(controlMessage), sizeof(int) * *fileDescriptorsCount);
351
352                for (size_t i = 0; i < *fileDescriptorsCount; ++i) {
353                    while (fcntl(fileDescriptors[i], F_SETFL, FD_CLOEXEC) == -1) {
354                        if (errno != EINTR) {
355                            ASSERT_NOT_REACHED();
356                            break;
357                        }
358                    }
359                }
360
361                found = true;
362                break;
363            }
364        }
365
366        if (!found)
367            *fileDescriptorsCount = 0;
368
369        return bytesRead;
370    }
371
372    return -1;
373}
374
375void Connection::readyReadHandler()
376{
377#if PLATFORM(QT)
378    SocketNotifierResourceGuard socketNotifierEnabler(m_socketNotifier);
379#endif
380
381    while (true) {
382        size_t fileDescriptorsCount = 0;
383        size_t bytesToRead = m_readBuffer.size() - m_readBufferSize;
384        ssize_t bytesRead = readBytesFromSocket(m_socketDescriptor, m_readBuffer.data() + m_readBufferSize, bytesToRead,
385                                                m_fileDescriptors.data() + m_fileDescriptorsSize, &fileDescriptorsCount);
386
387        if (bytesRead < 0) {
388            // EINTR was already handled by readBytesFromSocket.
389            if (errno == EAGAIN || errno == EWOULDBLOCK)
390                return;
391
392            // FIXME: Handle other errors here?
393            return;
394        }
395
396        m_readBufferSize += bytesRead;
397        m_fileDescriptorsSize += fileDescriptorsCount;
398
399        if (!bytesRead) {
400            connectionDidClose();
401            return;
402        }
403
404        // Process messages from data received.
405        while (true) {
406            if (!processMessage())
407                break;
408        }
409    }
410}
411
412bool Connection::open()
413{
414#if PLATFORM(QT)
415    ASSERT(!m_socketNotifier);
416#endif
417
418    int flags = fcntl(m_socketDescriptor, F_GETFL, 0);
419    while (fcntl(m_socketDescriptor, F_SETFL, flags | O_NONBLOCK) == -1) {
420        if (errno != EINTR) {
421            ASSERT_NOT_REACHED();
422            return false;
423        }
424    }
425
426    m_isConnected = true;
427#if PLATFORM(QT)
428    m_socketNotifier = m_connectionQueue->registerSocketEventHandler(m_socketDescriptor, QSocketNotifier::Read, WTF::bind(&Connection::readyReadHandler, this));
429#elif PLATFORM(GTK)
430    m_connectionQueue->registerSocketEventHandler(m_socketDescriptor, G_IO_IN, WTF::bind(&Connection::readyReadHandler, this), WTF::bind(&Connection::connectionDidClose, this));
431#elif PLATFORM(EFL)
432    m_connectionQueue->registerSocketEventHandler(m_socketDescriptor, WTF::bind(&Connection::readyReadHandler, this));
433#endif
434
435    // Schedule a call to readyReadHandler. Data may have arrived before installation of the signal
436    // handler.
437    m_connectionQueue->dispatch(WTF::bind(&Connection::readyReadHandler, this));
438
439    return true;
440}
441
442bool Connection::platformCanSendOutgoingMessages() const
443{
444    return m_isConnected;
445}
446
447bool Connection::sendOutgoingMessage(PassOwnPtr<MessageEncoder> encoder)
448{
449#if PLATFORM(QT)
450    ASSERT(m_socketNotifier);
451#endif
452
453    COMPILE_ASSERT(sizeof(MessageInfo) + attachmentMaxAmount * sizeof(size_t) <= messageMaxSize, AttachmentsFitToMessageInline);
454
455    Vector<Attachment> attachments = encoder->releaseAttachments();
456    AttachmentResourceGuard<Vector<Attachment>, Vector<Attachment>::iterator> attachementDisposer(attachments);
457
458    if (attachments.size() > (attachmentMaxAmount - 1)) {
459        ASSERT_NOT_REACHED();
460        return false;
461    }
462
463    MessageInfo messageInfo(encoder->bufferSize(), attachments.size());
464    size_t messageSizeWithBodyInline = sizeof(messageInfo) + (attachments.size() * sizeof(AttachmentInfo)) + encoder->bufferSize();
465    if (messageSizeWithBodyInline > messageMaxSize && encoder->bufferSize()) {
466        RefPtr<WebKit::SharedMemory> oolMessageBody = WebKit::SharedMemory::create(encoder->bufferSize());
467        if (!oolMessageBody)
468            return false;
469
470        WebKit::SharedMemory::Handle handle;
471        if (!oolMessageBody->createHandle(handle, WebKit::SharedMemory::ReadOnly))
472            return false;
473
474        messageInfo.setMessageBodyIsOutOfLine();
475
476        memcpy(oolMessageBody->data(), encoder->buffer(), encoder->bufferSize());
477
478        attachments.append(handle.releaseToAttachment());
479    }
480
481    struct msghdr message;
482    memset(&message, 0, sizeof(message));
483
484    struct iovec iov[3];
485    memset(&iov, 0, sizeof(iov));
486
487    message.msg_iov = iov;
488    int iovLength = 1;
489
490    iov[0].iov_base = reinterpret_cast<void*>(&messageInfo);
491    iov[0].iov_len = sizeof(messageInfo);
492
493    OwnArrayPtr<AttachmentInfo> attachmentInfo = adoptArrayPtr(new AttachmentInfo[attachments.size()]);
494
495    size_t attachmentFDBufferLength = 0;
496    if (!attachments.isEmpty()) {
497        for (size_t i = 0; i < attachments.size(); ++i) {
498            if (attachments[i].fileDescriptor() != -1)
499                attachmentFDBufferLength++;
500        }
501    }
502    OwnArrayPtr<char> attachmentFDBuffer = adoptArrayPtr(new char[CMSG_SPACE(sizeof(int) * attachmentFDBufferLength)]);
503
504    if (!attachments.isEmpty()) {
505        int* fdPtr = 0;
506
507        if (attachmentFDBufferLength) {
508            message.msg_control = attachmentFDBuffer.get();
509            message.msg_controllen = CMSG_SPACE(sizeof(int) * attachmentFDBufferLength);
510            memset(message.msg_control, 0, message.msg_controllen);
511
512            struct cmsghdr* cmsg = CMSG_FIRSTHDR(&message);
513            cmsg->cmsg_level = SOL_SOCKET;
514            cmsg->cmsg_type = SCM_RIGHTS;
515            cmsg->cmsg_len = CMSG_LEN(sizeof(int) * attachmentFDBufferLength);
516
517            fdPtr = reinterpret_cast<int*>(CMSG_DATA(cmsg));
518        }
519
520        int fdIndex = 0;
521        for (size_t i = 0; i < attachments.size(); ++i) {
522            attachmentInfo[i].setType(attachments[i].type());
523
524            switch (attachments[i].type()) {
525            case Attachment::MappedMemoryType:
526                attachmentInfo[i].setSize(attachments[i].size());
527                // Fall trhough, set file descriptor or null.
528            case Attachment::SocketType:
529                if (attachments[i].fileDescriptor() != -1) {
530                    ASSERT(fdPtr);
531                    fdPtr[fdIndex++] = attachments[i].fileDescriptor();
532                } else
533                    attachmentInfo[i].setNull();
534                break;
535            case Attachment::Uninitialized:
536            default:
537                break;
538            }
539        }
540
541        iov[iovLength].iov_base = attachmentInfo.get();
542        iov[iovLength].iov_len = sizeof(AttachmentInfo) * attachments.size();
543        ++iovLength;
544    }
545
546    if (!messageInfo.isMessageBodyIsOutOfLine() && encoder->bufferSize()) {
547        iov[iovLength].iov_base = reinterpret_cast<void*>(encoder->buffer());
548        iov[iovLength].iov_len = encoder->bufferSize();
549        ++iovLength;
550    }
551
552    message.msg_iovlen = iovLength;
553
554    int bytesSent = 0;
555    while ((bytesSent = sendmsg(m_socketDescriptor, &message, 0)) == -1) {
556        if (errno != EINTR)
557            return false;
558    }
559    return true;
560}
561
562#if PLATFORM(QT)
563void Connection::setShouldCloseConnectionOnProcessTermination(WebKit::PlatformProcessIdentifier process)
564{
565    m_connectionQueue->dispatchOnTermination(process, WTF::bind(&Connection::connectionDidClose, this));
566}
567#endif
568
569} // namespace CoreIPC
570