1 /*
2 *
3 * Copyright 2018 gRPC authors.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 *
17 */
18
19 #include <grpc/support/port_platform.h>
20
21 #include "src/core/lib/iomgr/resolve_address_custom.h"
22
23 #include <string.h>
24
25 #include <string>
26
27 #include "absl/strings/str_format.h"
28
29 #include <grpc/support/alloc.h>
30 #include <grpc/support/log.h>
31
32 #include "src/core/lib/gpr/string.h"
33 #include "src/core/lib/gpr/useful.h"
34 #include "src/core/lib/gprpp/host_port.h"
35 #include "src/core/lib/iomgr/iomgr_custom.h"
36 #include "src/core/lib/iomgr/port.h"
37 #include "src/core/lib/iomgr/sockaddr_utils.h"
38
39 struct grpc_custom_resolver {
40 grpc_closure* on_done = nullptr;
41 grpc_resolved_addresses** addresses = nullptr;
42 std::string host;
43 std::string port;
44 };
45
46 static grpc_custom_resolver_vtable* resolve_address_vtable = nullptr;
47
retry_named_port_failure(grpc_custom_resolver * r,grpc_resolved_addresses ** res)48 static int retry_named_port_failure(grpc_custom_resolver* r,
49 grpc_resolved_addresses** res) {
50 // This loop is copied from resolve_address_posix.c
51 const char* svc[][2] = {{"http", "80"}, {"https", "443"}};
52 for (size_t i = 0; i < GPR_ARRAY_SIZE(svc); i++) {
53 if (r->port == svc[i][0]) {
54 r->port = svc[i][1];
55 if (res) {
56 grpc_error* error = resolve_address_vtable->resolve(
57 r->host.c_str(), r->port.c_str(), res);
58 if (error != GRPC_ERROR_NONE) {
59 GRPC_ERROR_UNREF(error);
60 return 0;
61 }
62 } else {
63 resolve_address_vtable->resolve_async(r, r->host.c_str(),
64 r->port.c_str());
65 }
66 return 1;
67 }
68 }
69 return 0;
70 }
71
grpc_custom_resolve_callback(grpc_custom_resolver * r,grpc_resolved_addresses * result,grpc_error * error)72 void grpc_custom_resolve_callback(grpc_custom_resolver* r,
73 grpc_resolved_addresses* result,
74 grpc_error* error) {
75 GRPC_CUSTOM_IOMGR_ASSERT_SAME_THREAD();
76 grpc_core::ApplicationCallbackExecCtx callback_exec_ctx;
77 grpc_core::ExecCtx exec_ctx;
78 if (error == GRPC_ERROR_NONE) {
79 *r->addresses = result;
80 } else if (retry_named_port_failure(r, nullptr)) {
81 return;
82 }
83 if (r->on_done) {
84 grpc_core::ExecCtx::Run(DEBUG_LOCATION, r->on_done, error);
85 }
86 delete r;
87 }
88
try_split_host_port(const char * name,const char * default_port,std::string * host,std::string * port)89 static grpc_error* try_split_host_port(const char* name,
90 const char* default_port,
91 std::string* host, std::string* port) {
92 /* parse name, splitting it into host and port parts */
93 grpc_core::SplitHostPort(name, host, port);
94 if (host->empty()) {
95 return GRPC_ERROR_CREATE_FROM_COPIED_STRING(
96 absl::StrFormat("unparseable host:port: '%s'", name).c_str());
97 }
98 if (port->empty()) {
99 // TODO(murgatroid99): add tests for this case
100 if (default_port == nullptr) {
101 return GRPC_ERROR_CREATE_FROM_COPIED_STRING(
102 absl::StrFormat("no port in name '%s'", name).c_str());
103 }
104 *port = default_port;
105 }
106 return GRPC_ERROR_NONE;
107 }
108
blocking_resolve_address_impl(const char * name,const char * default_port,grpc_resolved_addresses ** addresses)109 static grpc_error* blocking_resolve_address_impl(
110 const char* name, const char* default_port,
111 grpc_resolved_addresses** addresses) {
112 GRPC_CUSTOM_IOMGR_ASSERT_SAME_THREAD();
113
114 grpc_custom_resolver resolver;
115 grpc_error* err =
116 try_split_host_port(name, default_port, &resolver.host, &resolver.port);
117 if (err != GRPC_ERROR_NONE) {
118 return err;
119 }
120
121 /* Call getaddrinfo */
122 grpc_resolved_addresses* addrs;
123 grpc_core::ExecCtx* curr = grpc_core::ExecCtx::Get();
124 grpc_core::ExecCtx::Set(nullptr);
125 err = resolve_address_vtable->resolve(resolver.host.c_str(),
126 resolver.port.c_str(), &addrs);
127 if (err != GRPC_ERROR_NONE) {
128 if (retry_named_port_failure(&resolver, &addrs)) {
129 GRPC_ERROR_UNREF(err);
130 err = GRPC_ERROR_NONE;
131 }
132 }
133 grpc_core::ExecCtx::Set(curr);
134 if (err == GRPC_ERROR_NONE) {
135 *addresses = addrs;
136 }
137 return err;
138 }
139
resolve_address_impl(const char * name,const char * default_port,grpc_pollset_set *,grpc_closure * on_done,grpc_resolved_addresses ** addrs)140 static void resolve_address_impl(const char* name, const char* default_port,
141 grpc_pollset_set* /*interested_parties*/,
142 grpc_closure* on_done,
143 grpc_resolved_addresses** addrs) {
144 GRPC_CUSTOM_IOMGR_ASSERT_SAME_THREAD();
145 std::string host;
146 std::string port;
147 grpc_error* err = try_split_host_port(name, default_port, &host, &port);
148 if (err != GRPC_ERROR_NONE) {
149 grpc_core::ExecCtx::Run(DEBUG_LOCATION, on_done, err);
150 return;
151 }
152 grpc_custom_resolver* r = new grpc_custom_resolver();
153 r->on_done = on_done;
154 r->addresses = addrs;
155 r->host = std::move(host);
156 r->port = std::move(port);
157
158 /* Call getaddrinfo */
159 resolve_address_vtable->resolve_async(r, r->host.c_str(), r->port.c_str());
160 }
161
162 static grpc_address_resolver_vtable custom_resolver_vtable = {
163 resolve_address_impl, blocking_resolve_address_impl};
164
grpc_custom_resolver_init(grpc_custom_resolver_vtable * impl)165 void grpc_custom_resolver_init(grpc_custom_resolver_vtable* impl) {
166 resolve_address_vtable = impl;
167 grpc_set_resolver_impl(&custom_resolver_vtable);
168 }
169