summaryrefslogtreecommitdiff
path: root/implementation/endpoints/include/local_server_endpoint_impl_receive_op.hpp
blob: 38e166236eb9dcace1642e972fc8545cb9a0b45b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
// Copyright (C) 2020-2021 Bayerische Motoren Werke Aktiengesellschaft (BMW AG)
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

#ifndef VSOMEIP_V3_LOCAL_SERVER_ENDPOINT_IMPL_RECEIVE_OP_HPP_
#define VSOMEIP_V3_LOCAL_SERVER_ENDPOINT_IMPL_RECEIVE_OP_HPP_

#if VSOMEIP_BOOST_VERSION >= 106600
#if defined(__linux__) || defined(ANDROID)

#include <boost/asio/local/stream_protocol.hpp>

namespace vsomeip_v3 {

using socket_type_t = boost::asio::local::stream_protocol::socket;
using receive_handler_t = std::function<
    void (boost::system::error_code const &_error, size_t _size,
          const std::uint32_t &, const std::uint32_t &)>;

struct local_server_endpoint_impl_receive_op {

    socket_type_t &socket_;
    receive_handler_t handler_;
    byte_t *buffer_;
    size_t length_;
    uid_t uid_;
    gid_t gid_;
    size_t bytes_;

    void operator()(boost::system::error_code _error) {

        if (!_error) {
            if (!socket_.native_non_blocking())
                socket_.native_non_blocking(true, _error);

            for (;;) {
                ssize_t its_result;
                int its_flags(0);

                // Set buffer
                struct iovec its_vec[1];
                its_vec[0].iov_base = buffer_;
                its_vec[0].iov_len = length_;

                union {
                    struct cmsghdr cmh;
                    char   control[CMSG_SPACE(sizeof(struct ucred))];
                } control_un;

                // Set 'control_un' to describe ancillary data that we want to receive
                control_un.cmh.cmsg_len = CMSG_LEN(sizeof(struct ucred));
                control_un.cmh.cmsg_level = SOL_SOCKET;
                control_un.cmh.cmsg_type = SCM_CREDENTIALS;

                // Build header with all informations to call ::recvmsg
                msghdr its_header = msghdr();
                its_header.msg_iov = its_vec;
                its_header.msg_iovlen = 1;
                its_header.msg_control = control_un.control;
                its_header.msg_controllen = sizeof(control_un.control);

                // Call recvmsg and handle its result
                errno = 0;
                its_result = ::recvmsg(socket_.native_handle(), &its_header, its_flags);
                _error = boost::system::error_code(its_result < 0 ? errno : 0,
                        boost::asio::error::get_system_category());
                bytes_ += _error ? 0 : static_cast<size_t>(its_result);

                if (_error == boost::asio::error::interrupted)
                    continue;

                if (_error == boost::asio::error::would_block
                        || _error == boost::asio::error::try_again) {
                    socket_.async_wait(socket_type_t::wait_read, *this);
                    return;
                }

                if (_error)
                    break;

                if (bytes_ == 0)
                    _error = boost::asio::error::eof;

                // Extract credentials (UID/GID)
                struct ucred *its_credentials;
                for (struct cmsghdr *cmsg = CMSG_FIRSTHDR(&its_header);
                     cmsg != NULL;
                     cmsg = CMSG_NXTHDR(&its_header, cmsg))
                {
                    if (cmsg->cmsg_level == SOL_SOCKET
                        && cmsg->cmsg_type == SCM_CREDENTIALS
                        && cmsg->cmsg_len == CMSG_LEN(sizeof(struct ucred))) {

                        its_credentials = (struct ucred *) CMSG_DATA(cmsg);
                        if (its_credentials) {
                            uid_ = its_credentials->uid;
                            gid_ = its_credentials->gid;
                            break;
                        }
                    }
                }

                break;
            }
        }

        // Call the handler
        handler_(_error, bytes_, uid_, gid_);
    }
};

} // namespace vsomeip

#endif // __linux__ || ANDROID
#endif // VSOMEIP_BOOST_VERSION >= 106600

#endif // VSOMEIP_V3_LOCAL_SERVER_ENDPOINT_IMPL_RECEIVE_OP_HPP_