File: network.cpp

package info (click to toggle)
android-platform-tools 29.0.6-28
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 365,224 kB
  • sloc: cpp: 1,049,638; java: 460,532; ansic: 375,452; asm: 301,257; xml: 134,509; python: 92,731; perl: 62,008; sh: 26,753; makefile: 3,210; javascript: 3,172; yacc: 1,403; lex: 455; awk: 368; ruby: 183; sql: 140
file content (153 lines) | stat: -rw-r--r-- 4,815 bytes parent folder | download
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
/*
 * Copyright (C) 2017 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "sysdeps/network.h"

#include <errno.h>
#include <netdb.h>
#include <netinet/in.h>
#include <sys/socket.h>

#include <string>
#include <cstring>

#include <android-base/logging.h>
#include <android-base/stringprintf.h>
#include <cutils/sockets.h>

#include "adb_unique_fd.h"

static void set_error(std::string* error) {
    if (error) {
        *error = strerror(errno);
    }
}

static sockaddr* loopback_addr4(sockaddr_storage* addr, socklen_t* addrlen, int port) {
    struct sockaddr_in* addr4 = reinterpret_cast<sockaddr_in*>(addr);
    *addrlen = sizeof(*addr4);

    addr4->sin_family = AF_INET;
    addr4->sin_addr.s_addr = htonl(INADDR_LOOPBACK);
    addr4->sin_port = htons(port);
    return reinterpret_cast<sockaddr*>(addr);
}

static sockaddr* loopback_addr6(sockaddr_storage* addr, socklen_t* addrlen, int port) {
    struct sockaddr_in6* addr6 = reinterpret_cast<sockaddr_in6*>(addr);
    *addrlen = sizeof(*addr6);

    addr6->sin6_family = AF_INET6;
    addr6->sin6_addr = in6addr_loopback;
    addr6->sin6_port = htons(port);
    return reinterpret_cast<sockaddr*>(addr);
}

static int _network_loopback_client(bool ipv6, int port, int type, std::string* error) {
    unique_fd s(socket(ipv6 ? AF_INET6 : AF_INET, type, 0));
    if (s == -1) {
        set_error(error);
        return -1;
    }

    struct sockaddr_storage addr_storage = {};
    socklen_t addrlen = sizeof(addr_storage);
    sockaddr* addr = (ipv6 ? loopback_addr6 : loopback_addr4)(&addr_storage, &addrlen, 0);

    if (bind(s.get(), addr, addrlen) != 0) {
        set_error(error);
        return -1;
    }

    addr = (ipv6 ? loopback_addr6 : loopback_addr4)(&addr_storage, &addrlen, port);

    if (connect(s.get(), addr, addrlen) != 0) {
        set_error(error);
        return -1;
    }

    return s.release();
}

int network_loopback_client(int port, int type, std::string* error) {
    // Try IPv4 first, use IPv6 as a fallback.
    int rc = _network_loopback_client(false, port, type, error);
    if (rc == -1) {
        return _network_loopback_client(true, port, type, error);
    }
    return rc;
}

static int _network_loopback_server(bool ipv6, int port, int type, std::string* error) {
    unique_fd s(socket(ipv6 ? AF_INET6 : AF_INET, type, 0));
    if (s == -1) {
        set_error(error);
        return -1;
    }

    int n = 1;
    setsockopt(s.get(), SOL_SOCKET, SO_REUSEADDR, &n, sizeof(n));

    struct sockaddr_storage addr_storage = {};
    socklen_t addrlen = sizeof(addr_storage);
    sockaddr* addr = (ipv6 ? loopback_addr6 : loopback_addr4)(&addr_storage, &addrlen, port);

    if (bind(s.get(), addr, addrlen) != 0) {
        set_error(error);
        return -1;
    }

    if (type == SOCK_STREAM || type == SOCK_SEQPACKET) {
        if (listen(s.get(), SOMAXCONN) != 0) {
            set_error(error);
            return -1;
        }
    }

    return s.release();
}

int network_loopback_server(int port, int type, std::string* error, bool prefer_ipv4) {
    int rc = -1;
    if (prefer_ipv4) {
        rc = _network_loopback_server(false, port, type, error);
    }

    // Only attempt to listen on IPv6 if IPv4 is unavailable or prefer_ipv4 is false
    // We don't want to start an IPv6 server if there's already an IPv4 one running.
    if (rc == -1 && (errno == EADDRNOTAVAIL || errno == EAFNOSUPPORT || !prefer_ipv4)) {
        return _network_loopback_server(true, port, type, error);
    }
    return rc;
}

int network_connect(const std::string& host, int port, int type, int timeout, std::string* error) {
    int getaddrinfo_error = 0;
    int fd = socket_network_client_timeout(host.c_str(), port, type, timeout, &getaddrinfo_error);
    if (fd != -1) {
        return fd;
    }
    if (getaddrinfo_error != 0) {
        *error = android::base::StringPrintf("failed to resolve host: '%s': %s", host.c_str(),
                                             gai_strerror(getaddrinfo_error));
        LOG(WARNING) << *error;
    } else {
        *error = android::base::StringPrintf("failed to connect to '%s:%d': %s", host.c_str(), port,
                                             strerror(errno));
        LOG(WARNING) << *error;
    }
    return -1;
}