1/* 2 * libwebsockets - small server side websockets and web server implementation 3 * 4 * Copyright (C) 2010 - 2020 Andy Green <andy@warmcat.com> 5 * 6 * Permission is hereby granted, free of charge, to any person obtaining a copy 7 * of this software and associated documentation files (the "Software"), to 8 * deal in the Software without restriction, including without limitation the 9 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 * sell copies of the Software, and to permit persons to whom the Software is 11 * furnished to do so, subject to the following conditions: 12 * 13 * The above copyright notice and this permission notice shall be included in 14 * all copies or substantial portions of the Software. 15 * 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 * IN THE SOFTWARE. 23 */ 24 25#include "private-lib-core.h" 26 27#if !defined(WIN32) 28#include <netdb.h> 29#endif 30 31#if !defined(LWS_WITH_SYS_ASYNC_DNS) 32static int 33lws_getaddrinfo46(struct lws *wsi, const char *ads, struct addrinfo **result) 34{ 35 lws_metrics_caliper_declare(cal, wsi->a.context->mt_conn_dns); 36 struct addrinfo hints; 37#if defined(LWS_WITH_SYS_METRICS) 38 char buckname[32]; 39#endif 40 int n; 41 42 memset(&hints, 0, sizeof(hints)); 43 *result = NULL; 44 45 hints.ai_socktype = SOCK_STREAM; 46 47#ifdef LWS_WITH_IPV6 48 if (wsi->ipv6) { 49 50#if !defined(__ANDROID__) 51 hints.ai_family = AF_UNSPEC; 52#if !defined(__OpenBSD__) && !defined(__OPENBSD) 53 hints.ai_flags = AI_V4MAPPED; 54#endif 55#endif 56 } else 57#endif 58 { 59 hints.ai_family = PF_UNSPEC; 60 } 61 62#if defined(LWS_WITH_CONMON) 63 wsi->conmon_datum = lws_now_usecs(); 64#endif 65 66 wsi->dns_reachability = 0; 67 if (lws_fi(&wsi->fic, "dnsfail")) 68 n = EAI_FAIL; 69 else 70 n = getaddrinfo(ads, NULL, &hints, result); 71 72#if defined(LWS_WITH_CONMON) 73 wsi->conmon.ciu_dns = (lws_conmon_interval_us_t) 74 (lws_now_usecs() - wsi->conmon_datum); 75#endif 76 77 /* 78 * Which EAI_* are available and the meanings are highly platform- 79 * dependent, even different linux distros differ. 80 */ 81 82 if (0 83#if defined(EAI_SYSTEM) 84 || n == EAI_SYSTEM 85#endif 86#if defined(EAI_NODATA) 87 || n == EAI_NODATA 88#endif 89#if defined(EAI_FAIL) 90 || n == EAI_FAIL 91#endif 92#if defined(EAI_AGAIN) 93 || n == EAI_AGAIN 94#endif 95 ) { 96 wsi->dns_reachability = 1; 97 lws_metrics_caliper_report(cal, METRES_NOGO); 98#if defined(LWS_WITH_SYS_METRICS) 99 lws_snprintf(buckname, sizeof(buckname), "dns=\"unreachable %d\"", n); 100 lws_metrics_hist_bump_priv_wsi(wsi, mth_conn_failures, buckname); 101#endif 102 103#if defined(LWS_WITH_CONMON) 104 wsi->conmon.dns_disposition = LWSCONMON_DNS_SERVER_UNREACHABLE; 105#endif 106 107#if 0 108 lwsl_wsi_debug(wsi, "asking to recheck CPD in 1s"); 109 lws_system_cpd_start_defer(wsi->a.context, LWS_US_PER_SEC); 110#endif 111 } 112 113 lwsl_wsi_info(wsi, "getaddrinfo '%s' says %d", ads, n); 114 115#if defined(LWS_WITH_SYS_METRICS) 116 if (n < 0) { 117 lws_snprintf(buckname, sizeof(buckname), "dns=\"nores %d\"", n); 118 lws_metrics_hist_bump_priv_wsi(wsi, mth_conn_failures, buckname); 119 } 120#endif 121#if defined(LWS_WITH_CONMON) 122 wsi->conmon.dns_disposition = n < 0 ? LWSCONMON_DNS_NO_RESULT : 123 LWSCONMON_DNS_OK; 124#endif 125 126 lws_metrics_caliper_report(cal, n >= 0 ? METRES_GO : METRES_NOGO); 127 128 return n; 129} 130#endif 131 132#if !defined(LWS_WITH_SYS_ASYNC_DNS) && defined(EAI_NONAME) 133static const char * const dns_nxdomain = "DNS NXDOMAIN"; 134#endif 135 136struct lws * 137lws_client_connect_2_dnsreq(struct lws *wsi) 138{ 139 struct addrinfo *result = NULL; 140 const char *meth = NULL; 141#if defined(LWS_WITH_IPV6) 142 struct sockaddr_in addr; 143 const char *iface; 144#endif 145 const char *adsin; 146 int n, port = 0; 147 struct lws *w; 148 149 if (lwsi_state(wsi) == LRS_WAITING_DNS || 150 lwsi_state(wsi) == LRS_WAITING_CONNECT) { 151 lwsl_wsi_info(wsi, "LRS_WAITING_DNS / CONNECT"); 152 153 return wsi; 154 } 155 156 /* 157 * clients who will create their own fresh connection keep a copy of 158 * the hostname they originally connected to, in case other connections 159 * want to use it too 160 */ 161 162 if (!wsi->cli_hostname_copy) { 163 const char *pa = lws_wsi_client_stash_item(wsi, CIS_HOST, 164 _WSI_TOKEN_CLIENT_PEER_ADDRESS); 165 166 if (pa) 167 wsi->cli_hostname_copy = lws_strdup(pa); 168 } 169 170 /* 171 * The first job is figure out if we want to pipeline on or just join 172 * an existing "active connection" to the same place 173 */ 174 175 meth = lws_wsi_client_stash_item(wsi, CIS_METHOD, 176 _WSI_TOKEN_CLIENT_METHOD); 177 /* consult active connections to find out disposition */ 178 179 adsin = lws_wsi_client_stash_item(wsi, CIS_ADDRESS, 180 _WSI_TOKEN_CLIENT_PEER_ADDRESS); 181 182 /* we only pipeline connections that said it was okay */ 183 184 if (!wsi->client_pipeline) { 185 lwsl_wsi_debug(wsi, "new conn on no pipeline flag"); 186 187 goto solo; 188 } 189 190 /* only pipeline things we associate with being a stream */ 191 192 if (meth && strcmp(meth, "RAW") && strcmp(meth, "GET") && 193 strcmp(meth, "POST") && strcmp(meth, "PUT") && 194 strcmp(meth, "UDP") && strcmp(meth, "MQTT")) 195 goto solo; 196 197 if (!adsin) 198 /* 199 * This cannot happen since user code must provide the client 200 * address to get this far, it's here to satisfy Coverity 201 */ 202 return NULL; 203 204 switch (lws_vhost_active_conns(wsi, &w, adsin)) { 205 case ACTIVE_CONNS_SOLO: 206 break; 207 case ACTIVE_CONNS_MUXED: 208 lwsl_wsi_notice(wsi, "ACTIVE_CONNS_MUXED"); 209 if (lwsi_role_h2(wsi)) { 210 211 if (wsi->a.protocol->callback(wsi, 212 LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP, 213 wsi->user_space, NULL, 0)) 214 goto failed1; 215 216 //lwsi_set_state(wsi, LRS_H1C_ISSUE_HANDSHAKE2); 217 //lwsi_set_state(w, LRS_ESTABLISHED); 218 lws_callback_on_writable(wsi); 219 } 220 221 return wsi; 222 case ACTIVE_CONNS_QUEUED: 223 lwsl_wsi_debug(wsi, "ACTIVE_CONNS_QUEUED st 0x%x: ", 224 lwsi_state(wsi)); 225 226 if (lwsi_state(wsi) == LRS_UNCONNECTED) { 227 if (lwsi_role_h2(w)) 228 lwsi_set_state(wsi, 229 LRS_H2_WAITING_TO_SEND_HEADERS); 230 else 231 lwsi_set_state(wsi, LRS_H1C_ISSUE_HANDSHAKE2); 232 } 233 234 return lws_client_connect_4_established(wsi, w, 0); 235 } 236 237solo: 238 239 /* 240 * If we made our own connection, and we're doing a method that can 241 * take a pipeline, we are an "active client connection". 242 * 243 * Add ourselves to the vhost list of those so that others can 244 * piggyback on our transaction queue 245 */ 246 247 if (meth && (!strcmp(meth, "RAW") || !strcmp(meth, "GET") || 248 !strcmp(meth, "POST") || !strcmp(meth, "PUT") || 249 !strcmp(meth, "MQTT")) && 250 lws_dll2_is_detached(&wsi->dll2_cli_txn_queue) && 251 lws_dll2_is_detached(&wsi->dll_cli_active_conns)) { 252 lws_context_lock(wsi->a.context, __func__); 253 lws_vhost_lock(wsi->a.vhost); 254 lwsl_wsi_info(wsi, "adding as active conn"); 255 /* caution... we will have to unpick this on oom4 path */ 256 lws_dll2_add_head(&wsi->dll_cli_active_conns, 257 &wsi->a.vhost->dll_cli_active_conns_owner); 258 lws_vhost_unlock(wsi->a.vhost); 259 lws_context_unlock(wsi->a.context); 260 } 261 262 /* 263 * Since address must be given at client creation, should not be 264 * possible, but necessary to satisfy coverity 265 */ 266 if (!adsin) 267 return NULL; 268 269#if defined(LWS_WITH_UNIX_SOCK) 270 /* 271 * unix socket destination? 272 */ 273 274 if (*adsin == '+') { 275 wsi->unix_skt = 1; 276 n = 0; 277 goto next_step; 278 } 279#endif 280 281 /* 282 * start off allowing ipv6 on connection if vhost allows it 283 */ 284 wsi->ipv6 = LWS_IPV6_ENABLED(wsi->a.vhost); 285#ifdef LWS_WITH_IPV6 286 if (wsi->stash) 287 iface = wsi->stash->cis[CIS_IFACE]; 288 else 289 iface = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_IFACE); 290 291 if (wsi->ipv6 && iface && 292 inet_pton(AF_INET, iface, &addr.sin_addr) == 1) { 293 lwsl_wsi_notice(wsi, "client connection forced to IPv4"); 294 wsi->ipv6 = 0; 295 } 296#endif 297 298#if defined(LWS_CLIENT_HTTP_PROXYING) && \ 299 (defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)) 300 301 /* Decide what it is we need to connect to: 302 * 303 * Priority 1: connect to http proxy */ 304 305 if (wsi->a.vhost->http.http_proxy_port) { 306 adsin = wsi->a.vhost->http.http_proxy_address; 307 port = (int)wsi->a.vhost->http.http_proxy_port; 308#else 309 if (0) { 310#endif 311 312#if defined(LWS_WITH_SOCKS5) 313 314 /* Priority 2: Connect to SOCK5 Proxy */ 315 316 } else if (wsi->a.vhost->socks_proxy_port) { 317 lwsl_wsi_client(wsi, "Sending SOCKS Greeting"); 318 adsin = wsi->a.vhost->socks_proxy_address; 319 port = (int)wsi->a.vhost->socks_proxy_port; 320#endif 321 } else { 322 323 /* Priority 3: Connect directly */ 324 325 /* ads already set */ 326 port = wsi->c_port; 327 } 328 329 /* 330 * prepare the actual connection 331 * to whatever we decided to connect to 332 */ 333 lwsi_set_state(wsi, LRS_WAITING_DNS); 334 335 lwsl_wsi_info(wsi, "lookup %s:%u", adsin, port); 336 wsi->conn_port = (uint16_t)port; 337 338#if !defined(LWS_WITH_SYS_ASYNC_DNS) 339 n = 0; 340 if (!wsi->dns_sorted_list.count) { 341 /* 342 * blocking dns resolution 343 */ 344 n = lws_getaddrinfo46(wsi, adsin, &result); 345#if defined(EAI_NONAME) 346 if (n == EAI_NONAME) { 347 /* 348 * The DNS server responded with NXDOMAIN... even 349 * though this is still in the client creation call, 350 * we need to make a CCE, otherwise there won't be 351 * any user indication of what went wrong 352 */ 353 wsi->client_suppress_CONNECTION_ERROR = 0; 354 lws_inform_client_conn_fail(wsi, (void *)dns_nxdomain, 355 strlen(dns_nxdomain)); 356 goto failed1; 357 } 358#endif 359 } 360#else 361 /* this is either FAILED, CONTINUING, or already called connect_4 */ 362 363 if (lws_fi(&wsi->fic, "dnsfail")) 364 return lws_client_connect_3_connect(wsi, NULL, NULL, -4, NULL); 365 else 366 n = lws_async_dns_query(wsi->a.context, wsi->tsi, adsin, 367 LWS_ADNS_RECORD_A, lws_client_connect_3_connect, 368 wsi, NULL); 369 370 if (n == LADNS_RET_FAILED_WSI_CLOSED) 371 return NULL; 372 373 if (n == LADNS_RET_FAILED) 374 goto failed1; 375 376 return wsi; 377#endif 378 379#if defined(LWS_WITH_UNIX_SOCK) 380next_step: 381#endif 382 return lws_client_connect_3_connect(wsi, adsin, result, n, NULL); 383 384//#if defined(LWS_WITH_SYS_ASYNC_DNS) 385failed1: 386 lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, "client_connect2"); 387 388 return NULL; 389//#endif 390} 391