File: main.cpp

package info (click to toggle)
kdevelop 4%3A22.12.2-1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 70,096 kB
  • sloc: cpp: 284,635; javascript: 3,558; python: 3,422; sh: 1,319; ansic: 685; xml: 331; php: 95; lisp: 66; makefile: 39; sed: 12
file content (378 lines) | stat: -rw-r--r-- 13,014 bytes parent folder | download | duplicates (3)
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
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
/*
    SPDX-FileCopyrightText: 2011 David Nolden <david.nolden.kdevelop@art-master.de>

    SPDX-License-Identifier: LGPL-2.0-or-later
*/

#include <cassert>
#include <cerrno>
#include <cstdlib>
#include <cstring>
#include <fcntl.h>
#include <iostream>
#include <netdb.h>
#include <netinet/in.h>
#include <sstream>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/un.h>
#include <unistd.h>

#ifndef HAVE_MSG_NOSIGNAL
#define MSG_NOSIGNAL 0
#endif

/**
 * The goal of this utility is transforming the abstract unix-socket which is used by dbus
 * into a TCP socket which can be forwarded to a target machine by ssh tunneling, and then on
 * the target machine back into an abstract unix socket.
 *
 * This tool basically works similar to the "socat" utility, except that it works properly
 * for this special case. It is merely responsible for the transformation between abstract unix
 * sockets and tcp sockets.
 *
 * Furthermore, this tool makes the 'EXTERNAL' dbus authentication mechanism work even across
 * machines with different user IDs.
 *
 * This is how the EXTERNAL mechanism works (I found this in a comment of some ruby dbus library):
 *   Take the user id (eg integer 1000) make a string out of it "1000", take
 *   each character and determine hex value "1" => 0x31, "0" => 0x30. You
 *   obtain for "1000" => 31303030 This is what the server is expecting.
 *   Why? I dunno. How did I come to that conclusion? by looking at rbus
 *   code. I have no idea how he found that out.
 *
 * The dbus client performs the EXTERNAL authentication by sending "AUTH EXTERNAL 31303030\r\n" once
 * after opening the connection, so we can "repair" the authentication by overwriting the token in that
 * string through the correct one.
 * */

const bool debug = false;

/**
 * Returns the valid dbus EXTERNAL authentication token for the current user (see above)
 * */
std::string getAuthToken()
{
    // Get uid
    int uid = getuid();

    std::ostringstream uidStream;
    uidStream << uid;

    std::string uidStr = uidStream.str();

    const char hexdigits[16] = {
        '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
        'a', 'b', 'c', 'd', 'e', 'f'
    };

    std::ostringstream hexStream;
    for (char c : uidStr) {
        auto byte = (unsigned char)c;
        hexStream << hexdigits[byte >> 4] << hexdigits[byte & 0x0f];
    }

    return hexStream.str();
}

/**
 * Shuffles all data between the two file-descriptors until one of them fails or reaches EOF.
 * */
void shuffleBetweenStreams(int side1, int side2, bool fixSide1AuthToken)
{
    char buffer[1000];
    char buffer2[1000];

    // Set non-blocking mode
    int opts = fcntl(side1, F_GETFL);
    opts |= O_NONBLOCK;
    fcntl(side1, F_SETFL, opts);

    opts = fcntl(side2, F_GETFL);
    opts |= O_NONBLOCK;
    fcntl(side2, F_SETFL, opts);

    while (true) {
        int r1 = read(side1, buffer, 500); // We read less than 1000, so we have same additional space when changing the auth token
        int r2 = read(side2, buffer2, 500);

        if (r1 < -1 || r1 == 0) {
            if (debug)
                std::cerr << "stream 1 failed: " << r1 << std::endl;
            return;
        }
        if (r2 < -1 || r2 == 0) {
            if (debug)
                std::cerr << "stream 2 failed: " << r2 << std::endl;
            return;
        }

        if (r1 > 0) {
            if (debug)
                std::cerr << "transferring " << r1 << " from 1 to 2" << std::endl;

            if (fixSide1AuthToken) {
                if (r1 > 15 && memcmp(buffer, "\0AUTH EXTERNAL ", 15) == 0) {
                    int endPos = -1;
                    for (int i = 15; i < r1; ++i) {
                        if (buffer[i] == '\r') {
                            endPos = i;
                            break;
                        }
                    }

                    if (endPos != -1) {
                        std::string oldToken = std::string(buffer + 15, endPos - 15);
                        std::string newToken = getAuthToken();

                        int difference = newToken.size() - oldToken.size();
                        r1 += difference;
                        assert(r1 > 0 && r1 <= 1000);
                        memmove(buffer + endPos + difference, buffer + endPos, r1 - difference - endPos);
                        memcpy(buffer + 15, newToken.data(), newToken.size());
                        assert(buffer[endPos + difference] == '\r');
                        assert(buffer[endPos + difference - 1] == newToken[newToken.size() - 1]);
                    } else {
                        std::cout << "could not fix auth token, not enough data available" << std::endl;
                    }
                } else {
                    std::cout << "could not fix auth token" << std::endl;
                }
                fixSide1AuthToken = false;
            }

            opts = fcntl(side2, F_GETFL);
            opts ^= O_NONBLOCK;
            fcntl(side2, F_SETFL, opts);

            int w2 = send(side2, buffer, r1, MSG_NOSIGNAL);

            if (w2 < 0) {
                if (debug)
                    std::cerr << "writing to side 2 failed, ending: " << w2 << std::endl;
                return;
            }
            assert(w2 == r1);

            opts = fcntl(side2, F_GETFL);
            opts |= O_NONBLOCK;
            fcntl(side2, F_SETFL, opts);
        }

        if (r2 > 0) {
            if (debug)
                std::cerr << "transferring " << r2 << " from 2 to 1" << std::endl;
            opts = fcntl(side1, F_GETFL);
            opts ^= O_NONBLOCK;
            fcntl(side1, F_SETFL, opts);

            int w1 = send(side1, buffer2, r2, MSG_NOSIGNAL);

            if (w1 < 0) {
                if (debug)
                    std::cerr << "writing to side 1 failed, ending: " << w1 << std::endl;
                return;
            }
            assert(w1 == r2);

            opts = fcntl(side1, F_GETFL);
            opts |= O_NONBLOCK;
            fcntl(side1, F_SETFL, opts);
        }
        usleep(1000);
    }
}

int main(int argc, char** argv)
{
    int serverfd;

    if (argc < 2) {
        std::cerr << "need arguments:" << std::endl;
        std::cerr <<
        "[port]                    -   Open a server on this TCP port and forward connections to the local DBUS session"
            "                              (the DBUS_SESSION_BUS_ADDRESS environment variable must be set)";
        std::cerr <<
        "[port]  [fake dbus path]  -   Open a server on the fake dbus path and forward connections to the given local TCP port";
        std::cerr << ""
            "The last argument may be the --bind-only option, in which case the application only tries to"
            "open the server, but does not wait for clients to connect. This is useful to test whether the"
            "server port/path is available.";
        return 10;
    }

    bool waitForClients = true;

    if (std::string(argv[argc - 1]) == "--bind-only") {
        waitForClients = false;
        argc -= 1;
    }

    std::string dbusAddress(getenv("DBUS_SESSION_BUS_ADDRESS"));

    std::string path;

    if (argc == 2) {
        if (waitForClients && debug)
            std::cout << "forwarding from the local TCP port " << argv[1] << " to the local DBUS session at " <<
            dbusAddress.data() << std::endl;

        if (dbusAddress.empty()) {
            std::cerr << "The DBUS_SESSION_BUS_ADDRESS environment variable is not set" << std::endl;
            return 1;
        }

        // Open a TCP server

        std::string abstractPrefix("unix:abstract=");

        if (dbusAddress.substr(0, abstractPrefix.size()) != abstractPrefix) {
            std::cerr << "DBUS_SESSION_BUS_ADDRESS does not seem to use an abstract unix domain socket as expected" <<
            std::endl;
            return 2;
        }

        path = dbusAddress.substr(abstractPrefix.size(), dbusAddress.size() - abstractPrefix.size());
        if (path.find(",guid=") != std::string::npos)
            path = path.substr(0, path.find(",guid="));

        // Mark it as an abstract unix domain socket
        serverfd = socket(AF_INET, SOCK_STREAM, 0);

        if (serverfd < 0) {
            if (waitForClients)
                std::cerr << "ERROR opening server socket" << std::endl;
            return 3;
        }

        int portno = atoi(argv[1]);

        sockaddr_in server_addr;

        memset(&server_addr, 0, sizeof(server_addr));
        server_addr.sin_family = AF_INET;
        server_addr.sin_addr.s_addr = INADDR_ANY;
        server_addr.sin_port = htons(portno);

        if (bind(serverfd, ( struct sockaddr* ) &server_addr,
                 sizeof(server_addr)) < 0) {
            if (waitForClients)
                std::cerr << "ERROR opening the server" << std::endl;
            return 7;
        }
    } else if (argc == 3) {
        if (waitForClients && debug)
            std::cout << "forwarding from the local abstract unix domain socket " << argv[2] <<
            " to the local TCP port " << argv[1] << std::endl;
        // Open a unix domain socket server
        serverfd = socket(AF_UNIX, SOCK_STREAM, 0);

        if (serverfd < 0) {
            if (waitForClients)
                std::cerr << "ERROR opening server socket" << std::endl;
            return 3;
        }

        path = std::string(argv[2]);

        sockaddr_un serv_addr;
        memset(&serv_addr, 0, sizeof(serv_addr));
        serv_addr.sun_family = AF_UNIX;
        serv_addr.sun_path[0] = '\0'; // Mark as an abstract socket
        strcpy(serv_addr.sun_path + 1, path.data());

        if (debug)
            std::cout << "opening at " << path.data() << std::endl;

        if (bind(serverfd, ( sockaddr* ) &serv_addr, sizeof (serv_addr.sun_family) + 1 + path.length()) < 0) {
            if (waitForClients)
                std::cerr << "ERROR opening the server" << std::endl;
            return 7;
        }
    } else {
        std::cerr << "Wrong arguments";
        return 1;
    }

    listen(serverfd, 10);

    while (waitForClients) {
        if (debug)
            std::cerr << "waiting for client" << std::endl;
        sockaddr_in cli_addr;
        socklen_t clilen = sizeof(cli_addr);
        int connectedclientsockfd = accept(serverfd,
                                           ( struct sockaddr* ) &cli_addr,
                                           &clilen);

        if (connectedclientsockfd < 0) {
            std::cerr << "ERROR on accept" << std::endl;
            return 8;
        }

        if (debug)
            std::cerr << "got client" << std::endl;

        int sockfd;

        int addrSize;
        sockaddr* useAddr = nullptr;
        sockaddr_un serv_addru;
        sockaddr_in serv_addrin;

        if (argc == 2) {
            sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
            if (sockfd < 0) {
                std::cerr << "ERROR opening socket" << std::endl;
                return 3;
            }
            memset(&serv_addru, 0, sizeof(serv_addru));
            serv_addru.sun_family = AF_UNIX;
            serv_addru.sun_path[0] = '\0'; // Mark as an abstract socket
            strcpy(serv_addru.sun_path + 1, path.data());
            addrSize = sizeof (serv_addru.sun_family) + 1 + path.size();
            useAddr = ( sockaddr* )&serv_addru;

            if (debug)
                std::cout << "connecting to " << path.data() << std::endl;
        } else {
            sockfd = socket(AF_INET, SOCK_STREAM, 0);
            if (sockfd < 0) {
                std::cerr << "ERROR opening socket" << std::endl;
                return 3;
            }
            int port = atoi(argv[1]);
            hostent* server = gethostbyname("localhost");
            if (server == nullptr) {
                std::cerr << "failed to get server" << std::endl;
                return 5;
            }
            memset(&serv_addrin, 0, sizeof(serv_addrin));
            serv_addrin.sin_family = AF_INET;
            serv_addrin.sin_addr.s_addr = INADDR_ANY;
            serv_addrin.sin_port = htons(port);
            memcpy(&serv_addrin.sin_addr.s_addr, server->h_addr, server->h_length);
            addrSize = sizeof (serv_addrin);
            useAddr = ( sockaddr* )&serv_addrin;

            if (debug)
                std::cout << "connecting to port " << port << std::endl;
        }

        if (connect(sockfd, useAddr, addrSize) < 0) {
            int res = errno;
            if (res == ECONNREFUSED)
                std::cerr << "ERROR while connecting: connection refused" << std::endl;
            else if (res == ENOENT)
                std::cerr << "ERROR while connecting: no such file or directory" << std::endl;
            else
                std::cerr << "ERROR while connecting" << std::endl;
            return 5;
        }

        shuffleBetweenStreams(connectedclientsockfd, sockfd, argc == 2);
        close(sockfd);
        close(connectedclientsockfd);
    }
    return 0;
}