static int lws_ssl_server_name_cb(SSL *ssl, int *ad, void *arg) { struct lws_context *context = (struct lws_context *)arg; struct lws_vhost *vhost, *vh; const char *servername; if (!ssl) return SSL_TLSEXT_ERR_NOACK; /* * We can only get ssl accepted connections by using a vhost's ssl_ctx * find out which listening one took us and only match vhosts on the * same port. */ vh = context->vhost_list; while (vh) { if (!vh->being_destroyed && vh->tls.ssl_ctx == SSL_get_SSL_CTX(ssl)) break; vh = vh->vhost_next; } if (!vh) { assert(vh); /* can't match the incoming vh? */ return SSL_TLSEXT_ERR_OK; } servername = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name); if (!servername) { /* the client doesn't know what hostname it wants */ lwsl_info("SNI: Unknown ServerName: %s\n", servername); return SSL_TLSEXT_ERR_OK; } vhost = lws_select_vhost(context, vh->listen_port, servername); if (!vhost) { lwsl_info("SNI: none: %s:%d\n", servername, vh->listen_port); return SSL_TLSEXT_ERR_OK; } lwsl_info("SNI: Found: %s:%d\n", servername, vh->listen_port); /* select the ssl ctx from the selected vhost for this conn */ SSL_set_SSL_CTX(ssl, vhost->tls.ssl_ctx); return SSL_TLSEXT_ERR_OK; }
static int lws_ssl_server_name_cb(SSL *ssl, int *ad, void *arg) { struct lws_context *context; struct lws_vhost *vhost, *vh; const char *servername; int port; if (!ssl) return SSL_TLSEXT_ERR_NOACK; context = (struct lws_context *)SSL_CTX_get_ex_data( SSL_get_SSL_CTX(ssl), openssl_SSL_CTX_private_data_index); /* * We can only get ssl accepted connections by using a vhost's ssl_ctx * find out which listening one took us and only match vhosts on the * same port. */ vh = context->vhost_list; while (vh) { if (!vh->being_destroyed && vh->ssl_ctx == SSL_get_SSL_CTX(ssl)) break; vh = vh->vhost_next; } assert(vh); /* we cannot get an ssl without using a vhost ssl_ctx */ port = vh->listen_port; servername = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name); if (servername) { vhost = lws_select_vhost(context, port, servername); if (vhost) { lwsl_debug("SNI: Found: %s (port %d)\n", servername, port); SSL_set_SSL_CTX(ssl, vhost->ssl_ctx); return SSL_TLSEXT_ERR_OK; } lwsl_err("SNI: Unknown ServerName: %s\n", servername); } return SSL_TLSEXT_ERR_OK; }
static int lws_mbedtls_sni_cb(void *arg, mbedtls_ssl_context *mbedtls_ctx, const unsigned char *servername, size_t len) { SSL *ssl = SSL_SSL_from_mbedtls_ssl_context(mbedtls_ctx); struct lws_context *context = (struct lws_context *)arg; struct lws_vhost *vhost, *vh; lwsl_notice("%s: %s\n", __func__, servername); /* * We can only get ssl accepted connections by using a vhost's ssl_ctx * find out which listening one took us and only match vhosts on the * same port. */ vh = context->vhost_list; while (vh) { if (!vh->being_destroyed && vh->ssl_ctx == SSL_get_SSL_CTX(ssl)) break; vh = vh->vhost_next; } if (!vh) { assert(vh); /* can't match the incoming vh? */ return 0; } vhost = lws_select_vhost(context, vh->listen_port, (const char *)servername); if (!vhost) { lwsl_info("SNI: none: %s:%d\n", servername, vh->listen_port); return 0; } lwsl_info("SNI: Found: %s:%d at vhost '%s'\n", servername, vh->listen_port, vhost->name); /* select the ssl ctx from the selected vhost for this conn */ SSL_set_SSL_CTX(ssl, vhost->ssl_ctx); return 0; }
int lws_handshake_server(struct lws *wsi, unsigned char **buf, size_t len) { struct lws_context *context = lws_get_context(wsi); struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; struct _lws_header_related hdr; struct allocated_headers *ah; int protocol_len, n, hit; char protocol_list[128]; char protocol_name[32]; char *p; assert(len < 10000000); assert(wsi->u.hdr.ah); while (len--) { wsi->more_rx_waiting = !!len; if (wsi->mode != LWSCM_HTTP_SERVING && wsi->mode != LWSCM_HTTP_SERVING_ACCEPTED) { lwsl_err("%s: bad wsi mode %d\n", __func__, wsi->mode); goto bail_nuke_ah; } if (lws_parse(wsi, *(*buf)++)) { lwsl_info("lws_parse failed\n"); goto bail_nuke_ah; } if (wsi->u.hdr.parser_state != WSI_PARSING_COMPLETE) continue; lwsl_parser("%s: lws_parse sees parsing complete\n", __func__); lwsl_debug("%s: wsi->more_rx_waiting=%d\n", __func__, wsi->more_rx_waiting); /* select vhost */ if (lws_hdr_total_length(wsi, WSI_TOKEN_HOST)) { struct lws_vhost *vhost = lws_select_vhost( context, wsi->vhost->listen_port, lws_hdr_simple_ptr(wsi, WSI_TOKEN_HOST)); if (vhost) wsi->vhost = vhost; } wsi->vhost->trans++; if (!wsi->conn_stat_done) { wsi->vhost->conn++; wsi->conn_stat_done = 1; } wsi->mode = LWSCM_PRE_WS_SERVING_ACCEPT; lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0); /* is this websocket protocol or normal http 1.0? */ if (lws_hdr_total_length(wsi, WSI_TOKEN_UPGRADE)) { if (!strcasecmp(lws_hdr_simple_ptr(wsi, WSI_TOKEN_UPGRADE), "websocket")) { wsi->vhost->ws_upgrades++; lwsl_info("Upgrade to ws\n"); goto upgrade_ws; } #ifdef LWS_USE_HTTP2 if (!strcasecmp(lws_hdr_simple_ptr(wsi, WSI_TOKEN_UPGRADE), "h2c")) { wsi->vhost->http2_upgrades++; lwsl_info("Upgrade to h2c\n"); goto upgrade_h2c; } #endif lwsl_err("Unknown upgrade\n"); /* dunno what he wanted to upgrade to */ goto bail_nuke_ah; } /* no upgrade ack... he remained as HTTP */ lwsl_info("No upgrade\n"); ah = wsi->u.hdr.ah; lws_union_transition(wsi, LWSCM_HTTP_SERVING_ACCEPTED); wsi->state = LWSS_HTTP; wsi->u.http.fd = LWS_INVALID_FILE; /* expose it at the same offset as u.hdr */ wsi->u.http.ah = ah; lwsl_debug("%s: wsi %p: ah %p\n", __func__, (void *)wsi, (void *)wsi->u.hdr.ah); n = lws_http_action(wsi); return n; #ifdef LWS_USE_HTTP2 upgrade_h2c: if (!lws_hdr_total_length(wsi, WSI_TOKEN_HTTP2_SETTINGS)) { lwsl_err("missing http2_settings\n"); goto bail_nuke_ah; } lwsl_err("h2c upgrade...\n"); p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP2_SETTINGS); /* convert the peer's HTTP-Settings */ n = lws_b64_decode_string(p, protocol_list, sizeof(protocol_list)); if (n < 0) { lwsl_parser("HTTP2_SETTINGS too long\n"); return 1; } /* adopt the header info */ ah = wsi->u.hdr.ah; lws_union_transition(wsi, LWSCM_HTTP2_SERVING); /* http2 union member has http union struct at start */ wsi->u.http.ah = ah; lws_http2_init(&wsi->u.http2.peer_settings); lws_http2_init(&wsi->u.http2.my_settings); /* HTTP2 union */ lws_http2_interpret_settings_payload(&wsi->u.http2.peer_settings, (unsigned char *)protocol_list, n); strcpy(protocol_list, "HTTP/1.1 101 Switching Protocols\x0d\x0a" "Connection: Upgrade\x0d\x0a" "Upgrade: h2c\x0d\x0a\x0d\x0a"); n = lws_issue_raw(wsi, (unsigned char *)protocol_list, strlen(protocol_list)); if (n != strlen(protocol_list)) { lwsl_debug("http2 switch: ERROR writing to socket\n"); return 1; } wsi->state = LWSS_HTTP2_AWAIT_CLIENT_PREFACE; return 0; #endif upgrade_ws: if (!wsi->protocol) lwsl_err("NULL protocol at lws_read\n"); /* * It's websocket * * Select the first protocol we support from the list * the client sent us. * * Copy it to remove header fragmentation */ if (lws_hdr_copy(wsi, protocol_list, sizeof(protocol_list) - 1, WSI_TOKEN_PROTOCOL) < 0) { lwsl_err("protocol list too long"); goto bail_nuke_ah; } protocol_len = lws_hdr_total_length(wsi, WSI_TOKEN_PROTOCOL); protocol_list[protocol_len] = '\0'; p = protocol_list; hit = 0; while (*p && !hit) { n = 0; while (n < sizeof(protocol_name) - 1 && *p && *p !=',') protocol_name[n++] = *p++; protocol_name[n] = '\0'; if (*p) p++; lwsl_info("checking %s\n", protocol_name); n = 0; while (wsi->vhost->protocols[n].callback) { if (wsi->vhost->protocols[n].name && !strcmp(wsi->vhost->protocols[n].name, protocol_name)) { wsi->protocol = &wsi->vhost->protocols[n]; hit = 1; break; } n++; } } /* we didn't find a protocol he wanted? */ if (!hit) { if (lws_hdr_simple_ptr(wsi, WSI_TOKEN_PROTOCOL)) { lwsl_err("No protocol from \"%s\" supported\n", protocol_list); goto bail_nuke_ah; } /* * some clients only have one protocol and * do not sent the protocol list header... * allow it and match to protocol 0 */ lwsl_info("defaulting to prot 0 handler\n"); n = 0; wsi->protocol = &wsi->vhost->protocols[0]; } /* allocate wsi->user storage */ if (lws_ensure_user_space(wsi)) goto bail_nuke_ah; /* * Give the user code a chance to study the request and * have the opportunity to deny it */ if ((wsi->protocol->callback)(wsi, LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION, wsi->user_space, lws_hdr_simple_ptr(wsi, WSI_TOKEN_PROTOCOL), 0)) { lwsl_warn("User code denied connection\n"); goto bail_nuke_ah; } /* * stitch protocol choice into the vh protocol linked list */ wsi->same_vh_protocol_prev = /* guy who points to us */ &wsi->vhost->same_vh_protocol_list[n]; wsi->same_vh_protocol_next = /* old first guy is our next */ wsi->vhost->same_vh_protocol_list[n]; /* we become the new first guy */ wsi->vhost->same_vh_protocol_list[n] = wsi; /* * Perform the handshake according to the protocol version the * client announced */ switch (wsi->ietf_spec_revision) { case 13: lwsl_parser("lws_parse calling handshake_04\n"); if (handshake_0405(context, wsi)) { lwsl_info("hs0405 has failed the connection\n"); goto bail_nuke_ah; } break; default: lwsl_warn("Unknown client spec version %d\n", wsi->ietf_spec_revision); goto bail_nuke_ah; } /* we are upgrading to ws, so http/1.1 and keepalive + * pipelined header considerations about keeping the ah around * no longer apply. However it's common for the first ws * protocol data to have been coalesced with the browser * upgrade request and to already be in the ah rx buffer. */ lwsl_info("%s: %p: inheriting ah in ws mode (rxpos:%d, rxlen:%d)\n", __func__, wsi, wsi->u.hdr.ah->rxpos, wsi->u.hdr.ah->rxlen); lws_pt_lock(pt); hdr = wsi->u.hdr; lws_union_transition(wsi, LWSCM_WS_SERVING); /* * first service is WS mode will notice this, use the RX and * then detach the ah (caution: we are not in u.hdr union * mode any more then... ah_temp member is at start the same * though) * * Because rxpos/rxlen shows something in the ah, we will get * service guaranteed next time around the event loop * * All union members begin with hdr, so we can use it even * though we transitioned to ws union mode (the ah detach * code uses it anyway). */ wsi->u.hdr = hdr; lws_pt_unlock(pt); /* * create the frame buffer for this connection according to the * size mentioned in the protocol definition. If 0 there, use * a big default for compatibility */ n = wsi->protocol->rx_buffer_size; if (!n) n = LWS_MAX_SOCKET_IO_BUF; n += LWS_PRE; wsi->u.ws.rx_ubuf = lws_malloc(n + 4 /* 0x0000ffff zlib */); if (!wsi->u.ws.rx_ubuf) { lwsl_err("Out of Mem allocating rx buffer %d\n", n); return 1; } wsi->u.ws.rx_ubuf_alloc = n; lwsl_info("Allocating RX buffer %d\n", n); #if LWS_POSIX if (setsockopt(wsi->sock, SOL_SOCKET, SO_SNDBUF, (const char *)&n, sizeof n)) { lwsl_warn("Failed to set SNDBUF to %d", n); return 1; } #endif lwsl_parser("accepted v%02d connection\n", wsi->ietf_spec_revision); return 0; } /* while all chars are handled */ return 0; bail_nuke_ah: /* drop the header info */ /* we're closing, losing some rx is OK */ wsi->u.hdr.ah->rxpos = wsi->u.hdr.ah->rxlen; lws_header_table_detach(wsi, 1); return 1; }