Example #1
0
 //--------------------------------------------------------------
 void Connection::sendBinary( unsigned char * data, unsigned int size ){
     int n = -1;
     
     // TODO: when libwebsockets has an API supporting something this, we should use it
     // for now we are assuming that if you send binary your client supports it
     
     if ( supportsBinary ){
         if ( binaryBufsize < size ){
             cout<<"realloc from "<<binaryBufsize<<" to "<<size<<endl;
             binaryBufsize = size;
             binaryBuf = (unsigned char*)realloc(binaryBuf, LWS_SEND_BUFFER_PRE_PADDING+binaryBufsize+LWS_SEND_BUFFER_POST_PADDING * sizeof(unsigned char));
         }
         
         memcpy(&binaryBuf[LWS_SEND_BUFFER_PRE_PADDING], data, size );
         
         if ( ws != NULL && binaryBuf ){  
             n = libwebsocket_write(ws, &binaryBuf[LWS_SEND_BUFFER_PRE_PADDING], size, LWS_WRITE_BINARY);
         }
     } else {
         unsigned char *p = &buf[LWS_SEND_BUFFER_PRE_PADDING];
         int encoded_len = lws_b64_encode_string((char *) data, size, (char*)p, bufsize-LWS_SEND_BUFFER_PRE_PADDING-LWS_SEND_BUFFER_POST_PADDING);
         
         if (encoded_len > 0){
             n = libwebsocket_write(ws, p, encoded_len, LWS_WRITE_TEXT);
         }
     }
     
     if (n < 0){
         std::cout << "ERROR writing to socket" << std::endl;
     }
 }
Example #2
0
LWS_VISIBLE int
lws_set_proxy(struct lws_context *context, const char *proxy)
{
    char *p;
    char authstring[96];
    
    if (!proxy)
        return -1;

    p = strchr(proxy, '@');
    if (p) { /* auth is around */

        if ((unsigned int)(p - proxy) > sizeof(authstring) - 1)
            goto auth_too_long;

        strncpy(authstring, proxy, p - proxy);
        // null termination not needed on input
        if (lws_b64_encode_string(authstring, (p - proxy),
            context->proxy_basic_auth_token,
            sizeof context->proxy_basic_auth_token) < 0)
            goto auth_too_long;

        lwsl_notice(" Proxy auth in use\n");
            
        proxy = p + 1;
    } else
        context->proxy_basic_auth_token[0] = '\0';

    strncpy(context->http_proxy_address, proxy,
                sizeof(context->http_proxy_address) - 1);
    context->http_proxy_address[
                sizeof(context->http_proxy_address) - 1] = '\0';

    p = strchr(context->http_proxy_address, ':');
    if (!p && !context->http_proxy_port) {
        lwsl_err("http_proxy needs to be ads:port\n");

        return -1;
    } else {
        if (p) {
            *p = '\0';
            context->http_proxy_port = atoi(p + 1);
        }
    }

    lwsl_notice(" Proxy %s:%u\n", context->http_proxy_address,
                        context->http_proxy_port);

    return 0;

auth_too_long:
    lwsl_err("proxy auth too long\n");

    return -1;
}
//--------------------------------------------------------------
void
ofxWebSocketConnection::send(const std::string& message)
{
	int n = 0;
	if(message.size() > 4096){
		return;
	}
	if(message.size() > bufsize){
		bufsize = bufsize+1024;
		buf = (unsigned char*)realloc(buf, bufsize + LWS_SEND_BUFFER_PRE_PADDING + LWS_SEND_BUFFER_POST_PADDING*sizeof(unsigned char));
		cout << "ofxWebSocketConnection -- received large message, resizing buffer to " << bufsize << endl;
	}
	unsigned char *p = &buf[LWS_SEND_BUFFER_PRE_PADDING];
	
  if (binary)
  {
    //TODO: when libwebsockets has an API supporting something this, we should use it
    if (supportsBinary)
    {
      memcpy(p, message.c_str(), message.size());
      n = libwebsocket_write(ws, p, message.size(), LWS_WRITE_BINARY);
    }
    else {
      int encoded_len;
      //encoded_len = b64_encode_string(message.c_str(), message.size(), (char*)p, buf.size());
	  encoded_len = lws_b64_encode_string(message.c_str(), message.size(), (char*)p, bufsize-LWS_SEND_BUFFER_PRE_PADDING-LWS_SEND_BUFFER_POST_PADDING);
      if (encoded_len > 0)
        n = libwebsocket_write(ws, p, encoded_len, LWS_WRITE_TEXT);
    }
  }
  else {
	  memcpy(p, message.c_str(), message.size());
	  n = libwebsocket_write(ws, p, message.size(), LWS_WRITE_TEXT);
  }
  
  if (n < 0)
    std::cout << "ERROR writing to socket" << std::endl;
}
Example #4
0
File: client.c Project: 5ouya/raspC
char *
lws_generate_client_handshake(struct lws *wsi, char *pkt)
{
	char buf[128], hash[20], key_b64[40], *p = pkt;
	struct lws_context *context = wsi->context;
	int n;
#ifndef LWS_NO_EXTENSIONS
	const struct lws_extension *ext;
	int ext_count = 0;
#endif

	/*
	 * create the random key
	 */
	n = lws_get_random(context, hash, 16);
	if (n != 16) {
		lwsl_err("Unable to read from random dev %s\n",
			 SYSTEM_RANDOM_FILEPATH);
		lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS);
		return NULL;
	}

	lws_b64_encode_string(hash, 16, key_b64, sizeof(key_b64));

	/*
	 * 00 example client handshake
	 *
	 * GET /socket.io/websocket HTTP/1.1
	 * Upgrade: WebSocket
	 * Connection: Upgrade
	 * Host: 127.0.0.1:9999
	 * Origin: http://127.0.0.1
	 * Sec-WebSocket-Key1: 1 0 2#0W 9 89 7  92 ^
	 * Sec-WebSocket-Key2: 7 7Y 4328 B2v[8(z1
	 * Cookie: socketio=websocket
	 *
	 * (Á®Ä0¶†≥
	 *
	 * 04 example client handshake
	 *
	 * GET /chat HTTP/1.1
	 * Host: server.example.com
	 * Upgrade: websocket
	 * Connection: Upgrade
	 * Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
	 * Sec-WebSocket-Origin: http://example.com
	 * Sec-WebSocket-Protocol: chat, superchat
	 * Sec-WebSocket-Version: 4
	 */

	p += sprintf(p, "GET %s HTTP/1.1\x0d\x0a",
		     lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_URI));

	p += sprintf(p, "Pragma: no-cache\x0d\x0a"
			"Cache-Control: no-cache\x0d\x0a");

	p += sprintf(p, "Host: %s\x0d\x0a",
		     lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_HOST));
	p += sprintf(p, "Upgrade: websocket\x0d\x0a"
			"Connection: Upgrade\x0d\x0a"
			"Sec-WebSocket-Key: ");
	strcpy(p, key_b64);
	p += strlen(key_b64);
	p += sprintf(p, "\x0d\x0a");
	if (lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_ORIGIN))
		p += sprintf(p, "Origin: http://%s\x0d\x0a",
			     lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_ORIGIN));

	if (lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_SENT_PROTOCOLS))
		p += sprintf(p, "Sec-WebSocket-Protocol: %s\x0d\x0a",
		     lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_SENT_PROTOCOLS));

	/* tell the server what extensions we could support */

	p += sprintf(p, "Sec-WebSocket-Extensions: ");
#ifndef LWS_NO_EXTENSIONS
	ext = context->extensions;
	while (ext && ext->callback) {
		n = lws_ext_cb_all_exts(context, wsi,
			   LWS_EXT_CB_CHECK_OK_TO_PROPOSE_EXTENSION,
			   (char *)ext->name, 0);
		if (n) { /* an extension vetos us */
			lwsl_ext("ext %s vetoed\n", (char *)ext->name);
			ext++;
			continue;
		}
		n = context->protocols[0].callback(wsi,
			LWS_CALLBACK_CLIENT_CONFIRM_EXTENSION_SUPPORTED,
				wsi->user_space, (char *)ext->name, 0);

		/*
		 * zero return from callback means
		 * go ahead and allow the extension,
		 * it's what we get if the callback is
		 * unhandled
		 */

		if (n) {
			ext++;
			continue;
		}

		/* apply it */

		if (ext_count)
			*p++ = ',';
		p += sprintf(p, "%s", ext->client_offer);
		ext_count++;

		ext++;
	}
#endif
	p += sprintf(p, "\x0d\x0a");

	if (wsi->ietf_spec_revision)
		p += sprintf(p, "Sec-WebSocket-Version: %d\x0d\x0a",
			     wsi->ietf_spec_revision);

	/* give userland a chance to append, eg, cookies */

	context->protocols[0].callback(wsi, LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER,
				       NULL, &p, (pkt + LWS_MAX_SOCKET_IO_BUF) - p - 12);

	p += sprintf(p, "\x0d\x0a");

	/* prepare the expected server accept response */

	key_b64[39] = '\0'; /* enforce composed length below buf sizeof */
	n = sprintf(buf, "%s258EAFA5-E914-47DA-95CA-C5AB0DC85B11", key_b64);

	lws_SHA1((unsigned char *)buf, n, (unsigned char *)hash);

	lws_b64_encode_string(hash, 20,
			      wsi->u.hdr.ah->initial_handshake_hash_base64,
			      sizeof(wsi->u.hdr.ah->initial_handshake_hash_base64));

	return p;
}
Example #5
0
static int
handshake_0405(struct libwebsocket_context *context, struct libwebsocket *wsi)
{
    static const char *websocket_magic_guid_04 =
                     "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
    static const char *websocket_magic_guid_04_masking =
                     "61AC5F19-FBBA-4540-B96F-6561F1AB40A8";
    char accept_buf[MAX_WEBSOCKET_04_KEY_LEN + 37];
    char nonce_buf[256];
    char mask_summing_buf[256 + MAX_WEBSOCKET_04_KEY_LEN + 37];
    unsigned char hash[20];
    int n;
    char *response;
    char *p;
    char *m = mask_summing_buf;
    int nonce_len = 0;
    int accept_len;
    char *c;
    char ext_name[128];
    struct libwebsocket_extension * ext;
    int ext_count = 0;
    int more = 1;

    if (!wsi->utf8_token[WSI_TOKEN_HOST].token_len ||
        !wsi->utf8_token[WSI_TOKEN_KEY].token_len) {
        debug("handshake_04 missing pieces\n");
        /* completed header processing, but missing some bits */
        goto bail;
    }

    if (wsi->utf8_token[WSI_TOKEN_KEY].token_len >=
                             MAX_WEBSOCKET_04_KEY_LEN) {
        fprintf(stderr, "Client sent handshake key longer "
               "than max supported %d\n", MAX_WEBSOCKET_04_KEY_LEN);
        goto bail;
    }

    strcpy(accept_buf, wsi->utf8_token[WSI_TOKEN_KEY].token);
    strcpy(accept_buf + wsi->utf8_token[WSI_TOKEN_KEY].token_len,
                               websocket_magic_guid_04);

    SHA1((unsigned char *)accept_buf,
            wsi->utf8_token[WSI_TOKEN_KEY].token_len +
                     strlen(websocket_magic_guid_04), hash);

    accept_len = lws_b64_encode_string((char *)hash, 20, accept_buf,
                                 sizeof accept_buf);
    if (accept_len < 0) {
        fprintf(stderr, "Base64 encoded hash too long\n");
        goto bail;
    }

    /* allocate the per-connection user memory (if any) */

    if (wsi->protocol->per_session_data_size) {
        wsi->user_space = malloc(
                  wsi->protocol->per_session_data_size);
        if (wsi->user_space  == NULL) {
            fprintf(stderr, "Out of memory for "
                           "conn user space\n");
            goto bail;
        }
    } else
        wsi->user_space = NULL;

    /* create the response packet */

    /* make a buffer big enough for everything */

    response = malloc(256 +
        wsi->utf8_token[WSI_TOKEN_UPGRADE].token_len +
        wsi->utf8_token[WSI_TOKEN_CONNECTION].token_len +
        wsi->utf8_token[WSI_TOKEN_PROTOCOL].token_len);
    if (!response) {
        fprintf(stderr, "Out of memory for response buffer\n");
        goto bail;
    }

    p = response;
    strcpy(p,   "HTTP/1.1 101 Switching Protocols\x0d\x0a"
                      "Upgrade: WebSocket\x0d\x0a");
    p += strlen("HTTP/1.1 101 Switching Protocols\x0d\x0a"
                      "Upgrade: WebSocket\x0d\x0a");
    strcpy(p,   "Connection: Upgrade\x0d\x0a"
            "Sec-WebSocket-Accept: ");
    p += strlen("Connection: Upgrade\x0d\x0a"
            "Sec-WebSocket-Accept: ");
    strcpy(p, accept_buf);
    p += accept_len;

    if (wsi->ietf_spec_revision == 4) {
        strcpy(p,   "\x0d\x0aSec-WebSocket-Nonce: ");
        p += strlen("\x0d\x0aSec-WebSocket-Nonce: ");

        /* select the nonce */

        n = libwebsockets_get_random(wsi->protocol->owning_server,
                                      hash, 16);
        if (n != 16) {
            fprintf(stderr, "Unable to read random device %s %d\n",
                             SYSTEM_RANDOM_FILEPATH, n);
            if (wsi->user_space)
                free(wsi->user_space);
            goto bail;
        }

        /* encode the nonce */

        nonce_len = lws_b64_encode_string((const char *)hash, 16,
                           nonce_buf, sizeof nonce_buf);
        if (nonce_len < 0) {
            fprintf(stderr, "Failed to base 64 encode the nonce\n");
            if (wsi->user_space)
                free(wsi->user_space);
            goto bail;
        }

        /* apply the nonce */

        strcpy(p, nonce_buf);
        p += nonce_len;
    }

    if (wsi->utf8_token[WSI_TOKEN_PROTOCOL].token) {
        strcpy(p,   "\x0d\x0aSec-WebSocket-Protocol: ");
        p += strlen("\x0d\x0aSec-WebSocket-Protocol: ");
        strcpy(p, wsi->utf8_token[WSI_TOKEN_PROTOCOL].token);
        p += wsi->utf8_token[WSI_TOKEN_PROTOCOL].token_len;
    }

    /*
     * Figure out which extensions the client has that we want to
     * enable on this connection, and give him back the list
     */

    if (wsi->utf8_token[WSI_TOKEN_EXTENSIONS].token_len) {
        strcpy(p,   "\x0d\x0aSec-WebSocket-Extensions: ");
        p += strlen("\x0d\x0aSec-WebSocket-Extensions: ");

        /*
         * break down the list of client extensions
         * and go through them
         */

        c = wsi->utf8_token[WSI_TOKEN_EXTENSIONS].token;
        fprintf(stderr, "wsi->utf8_token[WSI_TOKEN_EXTENSIONS].token = %s\n", wsi->utf8_token[WSI_TOKEN_EXTENSIONS].token);
        wsi->count_active_extensions = 0;
        n = 0;
        while (more) {

            if (*c && (*c != ',' && *c != ' ' && *c != '\t')) {
                ext_name[n] = *c++;
                if (n < sizeof(ext_name) - 1)
                    n++;
                continue;
            }
            ext_name[n] = '\0';
            if (!*c)
                more = 0;
            else {
                c++;
                if (!n)
                    continue;
            }

            /* check a client's extension against our support */

            ext = wsi->protocol->owning_server->extensions;

            while (ext && ext->callback) {

                if (strcmp(ext_name, ext->name)) {
                    ext++;
                    continue;
                }

                /*
                 * oh, we do support this one he
                 * asked for... but let's ask user
                 * code if it's OK to apply it on this
                 * particular connection + protocol
                 */

                n = wsi->protocol->owning_server->
                    protocols[0].callback(
                        wsi->protocol->owning_server,
                        wsi,
                      LWS_CALLBACK_CONFIRM_EXTENSION_OKAY,
                          wsi->user_space, ext_name, 0);

                /*
                 * zero return from callback means
                 * go ahead and allow the extension,
                 * it's what we get if the callback is
                 * unhandled
                 */

                if (n) {
                    ext++;
                    continue;
                }

                /* apply it */

                if (ext_count)
                    *p++ = ',';
                p += sprintf(p, "%s", ext_name);
                ext_count++;

                /* instantiate the extension on this conn */

                wsi->active_extensions_user[
                    wsi->count_active_extensions] =
                         malloc(ext->per_session_data_size);
                memset(wsi->active_extensions_user[
                    wsi->count_active_extensions], 0,
                            ext->per_session_data_size);

                wsi->active_extensions[
                      wsi->count_active_extensions] = ext;

                /* allow him to construct his context */

                ext->callback(wsi->protocol->owning_server,
                        ext, wsi,
                        LWS_EXT_CALLBACK_CONSTRUCT,
                        wsi->active_extensions_user[
                    wsi->count_active_extensions], NULL, 0);

                wsi->count_active_extensions++;
                fprintf(stderr, "wsi->count_active_extensions <- %d", wsi->count_active_extensions);

                ext++;
            }

            n = 0;
        }
    }


    /* end of response packet */

    strcpy(p,   "\x0d\x0a\x0d\x0a");
    p += strlen("\x0d\x0a\x0d\x0a");

    if (wsi->ietf_spec_revision == 4) {

        /*
         * precompute the masking key the client will use from the SHA1
         * hash of ( base 64 client key we were sent, concatenated with
         * the bse 64 nonce we sent, concatenated with a magic constant
         * guid specified by the 04 standard )
         *
         * We store the hash in the connection's wsi ready to use with
         * undoing the masking the client has done on framed data it
         * sends (we send our data to the client in clear).
         */

        strcpy(mask_summing_buf, wsi->utf8_token[WSI_TOKEN_KEY].token);
        m += wsi->utf8_token[WSI_TOKEN_KEY].token_len;
        strcpy(m, nonce_buf);
        m += nonce_len;
        strcpy(m, websocket_magic_guid_04_masking);
        m += strlen(websocket_magic_guid_04_masking);

        SHA1((unsigned char *)mask_summing_buf, m - mask_summing_buf,
                               wsi->masking_key_04);
    }

    if (!lws_any_extension_handled(context, wsi,
            LWS_EXT_CALLBACK_HANDSHAKE_REPLY_TX,
                                response, p - response)) {

        /* okay send the handshake response accepting the connection */

        debug("issuing response packet %d len\n", (int)(p - response));
    #ifdef DEBUG
        fwrite(response, 1,  p - response, stderr);
    #endif
        n = libwebsocket_write(wsi, (unsigned char *)response,
                          p - response, LWS_WRITE_HTTP);
        if (n < 0) {
            fprintf(stderr, "ERROR writing to socket");
            goto bail;
        }

    }

    /* alright clean up and set ourselves into established state */

    free(response);
    wsi->state = WSI_STATE_ESTABLISHED;
    wsi->lws_rx_parse_state = LWS_RXPS_NEW;
    wsi->rx_packet_length = 0;

    /* notify user code that we're ready to roll */

    if (wsi->protocol->callback)
        wsi->protocol->callback(wsi->protocol->owning_server,
                wsi, LWS_CALLBACK_ESTABLISHED,
                      wsi->user_space, NULL, 0);

    return 0;


bail:
    return -1;
}
static int
callback_acme_client(struct lws *wsi, enum lws_callback_reasons reason,
		     void *user, void *in, size_t len)
{
	struct per_vhost_data__lws_acme_client *vhd =
			(struct per_vhost_data__lws_acme_client *)
			lws_protocol_vh_priv_get(lws_get_vhost(wsi),
					lws_get_protocol(wsi));
	char buf[LWS_PRE + 2536], *start = buf + LWS_PRE, *p = start,
	     *end = buf + sizeof(buf) - 1, digest[32], *failreason = NULL;
	unsigned char **pp, *pend;
	const char *content_type;
	const struct lws_protocol_vhost_options *pvo;
	struct lws_acme_cert_aging_args *caa;
	struct acme_connection *ac = NULL;
	struct lws_genhash_ctx hctx;
	struct lws *cwsi;
	int n, m;

	if (vhd)
		ac = vhd->ac;

	switch ((int)reason) {
	case LWS_CALLBACK_PROTOCOL_INIT:
		vhd = lws_protocol_vh_priv_zalloc(lws_get_vhost(wsi),
				lws_get_protocol(wsi),
				sizeof(struct per_vhost_data__lws_acme_client));
		vhd->context = lws_get_context(wsi);
		vhd->protocol = lws_get_protocol(wsi);
		vhd->vhost = lws_get_vhost(wsi);

		/* compute how much we need to hold all the pvo payloads */
		m = 0;
		pvo = (const struct lws_protocol_vhost_options *)in;
		while (pvo) {
			m += strlen(pvo->value) + 1;
			pvo = pvo->next;
		}
		p = vhd->pvo_data = malloc(m);
		if (!p)
			return -1;

		pvo = (const struct lws_protocol_vhost_options *)in;
		while (pvo) {
			start = p;
			n = strlen(pvo->value) + 1;
			memcpy(start, pvo->value, n);
			p += n;

			for (m = 0; m < (int)LWS_ARRAY_SIZE(pvo_names); m++)
				if (!strcmp(pvo->name, pvo_names[m]))
					vhd->pvop[m] = start;

			pvo = pvo->next;
		}

		n = 0;
		for (m = 0; m < (int)LWS_ARRAY_SIZE(pvo_names); m++)
			if (!vhd->pvop[m] && m >= LWS_TLS_REQ_ELEMENT_COMMON_NAME) {
				lwsl_notice("%s: require pvo '%s'\n", __func__,
						pvo_names[m]);
				n |= 1;
			} else
				if (vhd->pvop[m])
					lwsl_info("  %s: %s\n", pvo_names[m],
							vhd->pvop[m]);
		if (n) {
			free(vhd->pvo_data);
			vhd->pvo_data = NULL;

			return -1;
		}

#if !defined(LWS_WITH_ESP32)
		/*
		 * load (or create) the registration keypair while we
		 * still have root
		 */
		if (lws_acme_load_create_auth_keys(vhd, 4096))
			return 1;

		/*
		 * in case we do an update, open the update files while we
		 * still have root
		 */
		lws_snprintf(buf, sizeof(buf) - 1, "%s.upd",
			     vhd->pvop[LWS_TLS_SET_CERT_PATH]);
		vhd->fd_updated_cert = lws_open(buf, LWS_O_WRONLY | LWS_O_CREAT |
						 LWS_O_TRUNC, 0600);
		if (vhd->fd_updated_cert < 0) {
			lwsl_err("unable to create update cert file %s\n", buf);
			return -1;
		}
		lws_snprintf(buf, sizeof(buf) - 1, "%s.upd",
			     vhd->pvop[LWS_TLS_SET_KEY_PATH]);
		vhd->fd_updated_key = lws_open(buf, LWS_O_WRONLY | LWS_O_CREAT |
						LWS_O_TRUNC, 0600);
		if (vhd->fd_updated_key < 0) {
			lwsl_err("unable to create update key file %s\n", buf);
			return -1;
		}
#endif
		break;

	case LWS_CALLBACK_PROTOCOL_DESTROY:
		if (vhd && vhd->pvo_data) {
			free(vhd->pvo_data);
			vhd->pvo_data = NULL;
		}
		if (vhd)
			lws_acme_finished(vhd);
		break;

	case LWS_CALLBACK_VHOST_CERT_AGING:
		if (!vhd)
			break;

		caa = (struct lws_acme_cert_aging_args *)in;
		/*
		 * Somebody is telling us about a cert some vhost is using.
		 *
		 * First see if the cert is getting close enough to expiry that
		 * we *want* to do something about it.
		 */
		if ((int)(ssize_t)len > 14)
			break;

		/*
		 * ...is this a vhost we were configured on?
		 */
		if (vhd->vhost != caa->vh)
			return 1;

		for (n = 0; n < (int)LWS_ARRAY_SIZE(vhd->pvop);n++)
			if (caa->element_overrides[n])
				vhd->pvop_active[n] = caa->element_overrides[n];
			else
				vhd->pvop_active[n] = vhd->pvop[n];

		lwsl_notice("starting acme acquisition on %s: %s\n",
				lws_get_vhost_name(caa->vh), vhd->pvop_active[LWS_TLS_SET_DIR_URL]);

		lws_acme_start_acquisition(vhd, caa->vh);
		break;

	/*
	 * Client
	 */

	case LWS_CALLBACK_CLIENT_ESTABLISHED:
		lwsl_notice("%s: CLIENT_ESTABLISHED\n", __func__);
		break;

	case LWS_CALLBACK_CLIENT_CONNECTION_ERROR:
		lwsl_notice("%s: CLIENT_CONNECTION_ERROR: %p\n", __func__, wsi);
		break;

	case LWS_CALLBACK_CLOSED_CLIENT_HTTP:
		lwsl_notice("%s: CLOSED_CLIENT_HTTP: %p\n", __func__, wsi);
		break;

	case LWS_CALLBACK_CLOSED:
		lwsl_notice("%s: CLOSED: %p\n", __func__, wsi);
		break;

	case LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP:
		lwsl_notice("lws_http_client_http_response %d\n",
			    lws_http_client_http_response(wsi));
		if (!ac)
			break;
		ac->resp = lws_http_client_http_response(wsi);
		/* we get a new nonce each time */
		if (lws_hdr_total_length(wsi, WSI_TOKEN_REPLAY_NONCE) &&
		    lws_hdr_copy(wsi, ac->replay_nonce,
				 sizeof(ac->replay_nonce),
				 WSI_TOKEN_REPLAY_NONCE) < 0) {
			lwsl_notice("%s: nonce too large\n", __func__);

			goto failed;
		}

		switch (ac->state) {
		case ACME_STATE_DIRECTORY:
			lejp_construct(&ac->jctx, cb_dir, vhd, jdir_tok,
				       LWS_ARRAY_SIZE(jdir_tok));
			break;
		case ACME_STATE_NEW_REG:
			break;
		case ACME_STATE_NEW_AUTH:
			lejp_construct(&ac->jctx, cb_authz, ac, jauthz_tok,
					LWS_ARRAY_SIZE(jauthz_tok));
			break;

		case ACME_STATE_POLLING:
		case ACME_STATE_ACCEPT_CHALL:
			lejp_construct(&ac->jctx, cb_chac, ac, jchac_tok,
					LWS_ARRAY_SIZE(jchac_tok));
			break;

		case ACME_STATE_POLLING_CSR:
			ac->cpos = 0;
			if (ac->resp != 201)
				break;
			/*
			 * He acknowledges he will create the cert...
			 * get the URL to GET it from in the Location
			 * header.
			 */
			if (lws_hdr_copy(wsi, ac->challenge_uri,
					 sizeof(ac->challenge_uri),
					 WSI_TOKEN_HTTP_LOCATION) < 0) {
				lwsl_notice("%s: missing cert location:\n",
					    __func__);

				goto failed;
			}

			lwsl_notice("told to fetch cert from %s\n",
					ac->challenge_uri);
			break;

		default:
			break;
		}
		break;

	case LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER:
		if (!ac)
			break;
		switch (ac->state) {

		case ACME_STATE_DIRECTORY:
			break;
		case ACME_STATE_NEW_REG:
			p += lws_snprintf(p, end - p, "{"
					  "\"resource\":\"new-reg\","
					  "\"contact\":["
					  "\"mailto:%s\""
					  "],\"agreement\":\"%s\""
					  "}",
					  vhd->pvop_active[LWS_TLS_REQ_ELEMENT_EMAIL],
					  ac->urls[JAD_TOS_URL]);

			puts(start);
pkt_add_hdrs:
			ac->len = lws_jws_create_packet(&vhd->jwk,
							start, p - start,
							ac->replay_nonce,
							&ac->buf[LWS_PRE],
							sizeof(ac->buf) -
								 LWS_PRE);
			if (ac->len < 0) {
				ac->len = 0;
				lwsl_notice("lws_jws_create_packet failed\n");
				goto failed;
			}

			pp = (unsigned char **)in;
			pend = (*pp) + len;

			ac->pos = 0;
			content_type =         "application/jose+json";
			if (ac->state == ACME_STATE_POLLING_CSR)
				content_type = "application/pkix-cert";

			if (lws_add_http_header_by_token(wsi,
				    WSI_TOKEN_HTTP_CONTENT_TYPE,
					(uint8_t *)content_type, 21, pp, pend)) {
				lwsl_notice("could not add content type\n");
				goto failed;
			}

			n = sprintf(buf, "%d", ac->len);
			if (lws_add_http_header_by_token(wsi,
					WSI_TOKEN_HTTP_CONTENT_LENGTH,
					(uint8_t *)buf, n, pp, pend)) {
				lwsl_notice("could not add content length\n");
				goto failed;
			}

			lws_client_http_body_pending(wsi, 1);
			lws_callback_on_writable(wsi);
			lwsl_notice("prepare to send ACME_STATE_NEW_REG\n");
			break;
		case ACME_STATE_NEW_AUTH:
			p += lws_snprintf(p, end - p,
					"{"
					 "\"resource\":\"new-authz\","
					 "\"identifier\":{"
					  "\"type\":\"http-01\","
					  "\"value\":\"%s\""
					 "}"
					"}", vhd->pvop_active[LWS_TLS_REQ_ELEMENT_COMMON_NAME]);
			goto pkt_add_hdrs;

		case ACME_STATE_ACCEPT_CHALL:
			/*
			 * Several of the challenges in this document makes use
			 * of a key authorization string.  A key authorization
			 * expresses a domain holder's authorization for a
			 * specified key to satisfy a specified challenge, by
			 * concatenating the token for the challenge with a key
			 * fingerprint, separated by a "." character:
			 *
			 * key-authz = token || '.' ||
			 * 	       base64(JWK_Thumbprint(accountKey))
			 *
			 * The "JWK_Thumbprint" step indicates the computation
			 * specified in [RFC7638], using the SHA-256 digest.  As
			 * specified in the individual challenges below, the
			 * token for a challenge is a JSON string comprised
			 * entirely of characters in the base64 alphabet.
			 * The "||" operator indicates concatenation of strings.
			 *
			 *    keyAuthorization (required, string):  The key
			 *  authorization for this challenge.  This value MUST
			 *  match the token from the challenge and the client's
			 *  account key.
			 *
			 * draft acme-01 tls-sni-01:
			 *
			 *    {
			 *         "keyAuthorization": "evaGxfADs...62jcerQ",
			 *    }   (Signed as JWS)
			 *
			 * draft acme-07 tls-sni-02:
			 *
			 * POST /acme/authz/1234/1
			 * Host: example.com
			 * Content-Type: application/jose+json
			 *
			 * {
			 *  "protected": base64url({
			 *    "alg": "ES256",
			 *    "kid": "https://example.com/acme/acct/1",
			 *    "nonce": "JHb54aT_KTXBWQOzGYkt9A",
			 *    "url": "https://example.com/acme/authz/1234/1"
			 *  }),
			 *  "payload": base64url({
			 *     "keyAuthorization": "evaGxfADs...62jcerQ"
			 *  }),
			 * "signature": "Q1bURgJoEslbD1c5...3pYdSMLio57mQNN4"
			 * }
			 *
			 * On receiving a response, the server MUST verify that
			 * the key authorization in the response matches the
			 * "token" value in the challenge and the client's
			 * account key.  If they do not match, then the server
			 * MUST return an HTTP error in response to the POST
			 * request in which the client sent the challenge.
			 */

			lws_jwk_rfc7638_fingerprint(&vhd->jwk, digest);
			p = start;
			end = &buf[sizeof(buf) - 1];

			p += lws_snprintf(p, end - p,
					  "{\"resource\":\"challenge\","
					  "\"type\":\"tls-sni-0%d\","
					  "\"keyAuthorization\":\"%s.",
					  1 + ac->is_sni_02,
					  ac->chall_token);
			n = lws_jws_base64_enc(digest, 32, p, end - p);
			if (n < 0)
				goto failed;
			p += n;
			p += lws_snprintf(p, end - p, "\"}");
			puts(start);
			goto pkt_add_hdrs;

		case ACME_STATE_POLLING:
			break;

		case ACME_STATE_POLLING_CSR:
			/*
			 * "To obtain a certificate for the domain, the agent
			 * constructs a PKCS#10 Certificate Signing Request that
			 * asks the Let’s Encrypt CA to issue a certificate for
			 * example.com with a specified public key. As usual,
			 * the CSR includes a signature by the private key
			 * corresponding to the public key in the CSR. The agent
			 * also signs the whole CSR with the authorized
			 * key for example.com so that the Let’s Encrypt CA
			 * knows it’s authorized."
			 *
			 * IOW we must create a new RSA keypair which will be
			 * the cert public + private key, and put the public
			 * key in the CSR.  The CSR, just for transport, is also
			 * signed with our JWK, showing that as the owner of the
			 * authorized JWK, the request should be allowed.
			 *
			 * The cert comes back with our public key in it showing
			 * that the owner of the matching private key (we
			 * created that keypair) is the owner of the cert.
			 *
			 * We feed the CSR the elements we want in the cert,
			 * like the CN etc, and it gives us the b64URL-encoded
			 * CSR and the PEM-encoded (public +)private key in
			 * memory buffers.
			 */
			if (ac->goes_around)
				break;

			p += lws_snprintf(p, end - p,
					  "{\"resource\":\"new-cert\","
					  "\"csr\":\"");
			n = lws_tls_acme_sni_csr_create(vhd->context,
							&vhd->pvop_active[0],
							(uint8_t *)p, end - p,
							&ac->alloc_privkey_pem,
							&ac->len_privkey_pem);
			if (n < 0) {
				lwsl_notice("CSR generation failed\n");
				goto failed;
			}
			p += n;
			p += lws_snprintf(p, end - p, "\"}");
			puts(start);
			goto pkt_add_hdrs;

		default:
			break;
		}
		break;

	case LWS_CALLBACK_CLIENT_HTTP_WRITEABLE:
		lwsl_notice("LWS_CALLBACK_CLIENT_HTTP_WRITEABLE\n");
		if (!ac)
			break;
		if (ac->pos == ac->len)
			break;

		ac->buf[LWS_PRE + ac->len] = '\0';
		if (lws_write(wsi, (uint8_t *)ac->buf + LWS_PRE,
			      ac->len, LWS_WRITE_HTTP_FINAL) < 0)
			return -1;
		lwsl_notice("wrote %d\n", ac->len);
		ac->pos = ac->len;
		lws_client_http_body_pending(wsi, 0);
		break;

	/* chunked content */
	case LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ:
		if (!ac)
			return -1;
		switch (ac->state) {
		case ACME_STATE_POLLING:
		case ACME_STATE_ACCEPT_CHALL:
		case ACME_STATE_NEW_AUTH:
		case ACME_STATE_DIRECTORY:
			((char *)in)[len] = '\0';
			puts(in);
			m = (int)(signed char)lejp_parse(&ac->jctx,
							 (uint8_t *)in, len);
			if (m < 0 && m != LEJP_CONTINUE) {
				lwsl_notice("lejp parse failed %d\n", m);
				goto failed;
			}
			break;
		case ACME_STATE_NEW_REG:
			((char *)in)[len] = '\0';
			puts(in);
			break;
		case ACME_STATE_POLLING_CSR:
			/* it should be the DER cert! */
			if (ac->cpos + len > sizeof(ac->buf)) {
				lwsl_notice("Incoming cert is too large!\n");
				goto failed;
			}
			memcpy(&ac->buf[ac->cpos], in, len);
			ac->cpos += len;
			break;
		default:
			break;
		}
		break;

	/* unchunked content */
	case LWS_CALLBACK_RECEIVE_CLIENT_HTTP:
		lwsl_notice("%s: LWS_CALLBACK_RECEIVE_CLIENT_HTTP\n", __func__);
		{
			char buffer[2048 + LWS_PRE];
			char *px = buffer + LWS_PRE;
			int lenx = sizeof(buffer) - LWS_PRE;

			if (lws_http_client_read(wsi, &px, &lenx) < 0)
				return -1;
		}
		break;

	case LWS_CALLBACK_COMPLETED_CLIENT_HTTP:
		lwsl_notice("%s: COMPLETED_CLIENT_HTTP\n", __func__);

		if (!ac)
			return -1;
		switch (ac->state) {
		case ACME_STATE_DIRECTORY:
			lejp_destruct(&ac->jctx);

			/* check dir validity */

			for (n = 0; n < 6; n++)
				lwsl_notice("   %d: %s\n", n, ac->urls[n]);

			/*
			 * So... having the directory now... we try to
			 * register our keys next.  It's OK if it ends up
			 * they're already registered... this eliminates any
			 * gaps where we stored the key but registration did
			 * not complete for some reason...
			 */
			ac->state = ACME_STATE_NEW_REG;
			lws_acme_report_status(vhd->vhost, LWS_CUS_REG, NULL);

			strcpy(buf, ac->urls[JAD_NEW_REG_URL]);
			cwsi = lws_acme_client_connect(vhd->context, vhd->vhost,
						       &ac->cwsi, &ac->i, buf,
						       "POST");
			if (!cwsi) {
				lwsl_notice("%s: failed to connect to acme\n",
					    __func__);
				goto failed;
			}
			return -1; /* close the completed client connection */

		case ACME_STATE_NEW_REG:
			if ((ac->resp >= 200 && ac->resp < 299) ||
			     ac->resp == 409) {
				/*
				 * Our account already existed, or exists now.
				 *
				 * Move on to requesting a cert auth.
				 */
				ac->state = ACME_STATE_NEW_AUTH;
				lws_acme_report_status(vhd->vhost, LWS_CUS_AUTH,
							NULL);

				strcpy(buf, ac->urls[JAD_NEW_AUTHZ_URL]);
				cwsi = lws_acme_client_connect(vhd->context,
							vhd->vhost, &ac->cwsi,
							&ac->i, buf, "POST");
				if (!cwsi)
					lwsl_notice("%s: failed to connect\n",
						    __func__);
				return -1; /* close the completed client connection */
			} else {
				lwsl_notice("new-reg replied %d\n", ac->resp);
				goto failed;
			}
			return -1; /* close the completed client connection */

		case ACME_STATE_NEW_AUTH:
			lejp_destruct(&ac->jctx);
			if (ac->resp / 100 == 4) {
				lws_snprintf(buf, sizeof(buf),
					     "Auth failed: %s", ac->detail);
				failreason = buf;
				lwsl_notice("auth failed\n");
				goto failed;
			}
			lwsl_notice("chall: %s (%d)\n", ac->chall_token, ac->resp);
			if (!ac->chall_token[0]) {
				lwsl_notice("no challenge\n");
				goto failed;
			}


			ac->state = ACME_STATE_ACCEPT_CHALL;
			lws_acme_report_status(vhd->vhost, LWS_CUS_CHALLENGE,
						NULL);

			/* tls-sni-01 ... what a mess.
			 * The stuff in
			 * https://tools.ietf.org/html/
			 * 		draft-ietf-acme-acme-01#section-7.3
			 * "requires" n but it's missing from let's encrypt
			 * tls-sni-01 challenge.  The go docs say that they just
			 * implement one hashing round regardless
			 * https://godoc.org/golang.org/x/crypto/acme
			 *
			 * The go way is what is actually implemented today by
			 * letsencrypt
			 *
			 * "A client responds to this challenge by constructing
			 * a key authorization from the "token" value provided
			 * in the challenge and the client's account key.  The
			 * client first computes the SHA-256 digest Z0 of the
			 * UTF8-encoded key authorization, and encodes Z0 in
			 * UTF-8 lower-case hexadecimal form."
			 */

			/* tls-sni-02
			 *
			 * SAN A MUST be constructed as follows: compute the
			 * SHA-256 digest of the UTF-8-encoded challenge token
			 * and encode it in lowercase hexadecimal form.  The
			 * dNSName is "x.y.token.acme.invalid", where x
			 * is the first half of the hexadecimal representation
			 * and y is the second half.
			 */

			memset(&ac->ci, 0, sizeof(ac->ci));

			/* first compute the key authorization */

			lws_jwk_rfc7638_fingerprint(&vhd->jwk, digest);
			p = start;
			end = &buf[sizeof(buf) - 1];

			p += lws_snprintf(p, end - p, "%s.", ac->chall_token);
			n = lws_jws_base64_enc(digest, 32, p, end - p);
			if (n < 0)
				goto failed;
			p += n;

			if (lws_genhash_init(&hctx, LWS_GENHASH_TYPE_SHA256))
				return -1;

			if (lws_genhash_update(&hctx, (uint8_t *)start,
						lws_ptr_diff(p, start))) {
				lws_genhash_destroy(&hctx, NULL);

				return -1;
			}
			if (lws_genhash_destroy(&hctx, digest))
				return -1;

			p = buf;
			for (n = 0; n < 32; n++) {
				p += lws_snprintf(p, end - p, "%02x",
						  digest[n] & 0xff);
				if (n == (32 / 2) - 1)
					p = buf + 64;
			}

			p = ac->san_a;
			if (ac->is_sni_02) {
				lws_snprintf(p, sizeof(ac->san_a),
					     "%s.%s.token.acme.invalid",
					     buf, buf + 64);

				/*
				 * SAN B MUST be constructed as follows: compute
				 * the SHA-256 digest of the UTF-8 encoded key
				 * authorization and encode it in lowercase
				 * hexadecimal form.  The dNSName is
				 * "x.y.ka.acme.invalid" where x is the first
				 * half of the hexadecimal representation and y
				 * is the second half.
				 */
				lws_jwk_rfc7638_fingerprint(&vhd->jwk,
							    (char *)digest);

				p = buf;
				for (n = 0; n < 32; n++) {
					p += lws_snprintf(p, end - p, "%02x",
							  digest[n] & 0xff);
					if (n == (32 / 2) - 1)
						p = buf + 64;
				}

				p = ac->san_b;
				lws_snprintf(p, sizeof(ac->san_b),
					     "%s.%s.ka.acme.invalid",
					     buf, buf + 64);
			} else {
				lws_snprintf(p, sizeof(ac->san_a),
				     "%s.%s.acme.invalid", buf, buf + 64);
				ac->san_b[0] = '\0';
			}

			lwsl_notice("san_a: '%s'\n", ac->san_a);
			lwsl_notice("san_b: '%s'\n", ac->san_b);

			/*
			 * tls-sni-01:
			 *
			 * The client then configures the TLS server at the
			 * domain such that when a handshake is initiated with
			 * the Server Name Indication extension set to
			 * "<Zi[0:32]>.<Zi[32:64]>.acme.invalid", the
			 * corresponding generated certificate is presented.
			 *
			 * tls-sni-02:
			 *
			 *  The client MUST ensure that the certificate is
			 *  served to TLS connections specifying a Server Name
			 *  Indication (SNI) value of SAN A.
			 */
			ac->ci.vhost_name = ac->san_a;

			/*
			 * we bind to exact iface of real vhost, so we can
			 * share the listen socket by SNI
			 */
			ac->ci.iface = ac->real_vh_iface;

			/* listen on the same port as the vhost that triggered
			 * us */
			ac->ci.port = ac->real_vh_port;
			/* Skip filling in any x509 info into the ssl_ctx.
			 * It will be done at the callback
			 * LWS_CALLBACK_OPENSSL_LOAD_EXTRA_SERVER_VERIFY_CERTS
			 * in this callback handler (below)
			 */
			ac->ci.options = LWS_SERVER_OPTION_CREATE_VHOST_SSL_CTX |
					 LWS_SERVER_OPTION_SKIP_PROTOCOL_INIT |
					 LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
			/* make ourselves protocols[0] for the new vhost */
			ac->ci.protocols = acme_protocols;
			/*
			 * vhost .user points to the ac associated with the
			 * temporary vhost
			 */
			ac->ci.user = ac;

			ac->vhost = lws_create_vhost(lws_get_context(wsi),
						     &ac->ci);
			if (!ac->vhost)
				goto failed;

			/*
			 * The challenge-specific vhost is up... let the ACME
			 * server know we are ready to roll...
			 */

			ac->goes_around = 0;
			cwsi = lws_acme_client_connect(vhd->context, vhd->vhost,
						       &ac->cwsi, &ac->i,
						       ac->challenge_uri,
						       "POST");
			if (!cwsi) {
				lwsl_notice("%s: failed to connect\n",
					    __func__);
				goto failed;
			}
			return -1; /* close the completed client connection */

		case ACME_STATE_ACCEPT_CHALL:
			/*
			 * he returned something like this (which we parsed)
			 *
			 * {
			 *   "type": "tls-sni-01",
			 *   "status": "pending",
			 *   "uri": "https://acme-staging.api.letsencrypt.org/
			 *   		acme/challenge/xCt7bT3FaxoIQU3Qry87t5h
			 *   		uKDcC-L-0ERcD5DLAZts/71100507",
			 *   "token": "j2Vs-vLI_dsza4A35SFHIU03aIe2PzFRijbqCY
			 *   		dIVeE",
			 *   "keyAuthorization": "j2Vs-vLI_dsza4A35SFHIU03aIe2
			 *   		PzFRijbqCYdIVeE.nmOtdFd8Jikn6K8NnYYmT5
			 *   		vCM_PwSDT8nLdOYoFXhRU"
			 * }
			 *
			 */
			lwsl_notice("%s: COMPLETED accept chall: %s\n",
					__func__, ac->challenge_uri);
poll_again:
			ac->state = ACME_STATE_POLLING;
			lws_acme_report_status(vhd->vhost, LWS_CUS_CHALLENGE, NULL);

			if (ac->goes_around++ == 20) {
				lwsl_notice("%s: too many chall retries\n",
					    __func__);

				goto failed;
			}

			lws_timed_callback_vh_protocol(vhd->vhost, vhd->protocol,
					LWS_CALLBACK_USER + 0xac33, ac->goes_around == 1 ? 10 : 2);
			return -1; /* close the completed client connection */

		case ACME_STATE_POLLING:

			if (ac->resp == 202 &&
			    strcmp(ac->status, "invalid") &&
			    strcmp(ac->status, "valid")) {
				lwsl_notice("status: %s\n", ac->status);
				goto poll_again;
			}

			if (!strcmp(ac->status, "invalid")) {
				lwsl_notice("%s: polling failed\n", __func__);
				lws_snprintf(buf, sizeof(buf),
					     "Challenge Invalid: %s", ac->detail);
				failreason = buf;
				goto failed;
			}

			lwsl_notice("Challenge passed\n");

			/*
			 * The challenge was validated... so delete the
			 * temp SNI vhost now its job is done
			 */
			if (ac->vhost)
				lws_vhost_destroy(ac->vhost);
			ac->vhost = NULL;

			/*
			 * now our JWK is accepted as authorized to make
			 * requests for the domain, next move is create the
			 * CSR signed with the JWK, and send it to the ACME
			 * server to request the actual certs.
			 */
			ac->state = ACME_STATE_POLLING_CSR;
			lws_acme_report_status(vhd->vhost, LWS_CUS_REQ, NULL);
			ac->goes_around = 0;

			strcpy(buf, ac->urls[JAD_NEW_CERT_URL]);
			cwsi = lws_acme_client_connect(vhd->context, vhd->vhost,
						       &ac->cwsi, &ac->i, buf,
						       "POST");
			if (!cwsi) {
				lwsl_notice("%s: failed to connect to acme\n",
					    __func__);

				goto failed;
			}
			return -1; /* close the completed client connection */

		case ACME_STATE_POLLING_CSR:
			/*
			 * (after POSTing the CSR)...
			 *
			 * If the CA decides to issue a certificate, then the
			 * server creates a new certificate resource and
			 * returns a URI for it in the Location header field
			 * of a 201 (Created) response.
			 *
			 * HTTP/1.1 201 Created
			 * Location: https://example.com/acme/cert/asdf
			 *
			 * If the certificate is available at the time of the
			 * response, it is provided in the body of the response.
			 * If the CA has not yet issued the certificate, the
			 * body of this response will be empty.  The client
			 * should then send a GET request to the certificate URI
			 * to poll for the certificate.  As long as the
			 * certificate is unavailable, the server MUST provide a
			 * 202 (Accepted) response and include a Retry-After
			 * header to indicate when the server believes the
			 * certificate will be issued.
			 */
			if (ac->resp < 200 || ac->resp > 202) {
				lwsl_notice("CSR poll failed on resp %d\n",
					    ac->resp);
				goto failed;
			}

			if (ac->resp == 200) {
				char *pp;
				int max;

				lwsl_notice("The cert was sent..\n");

				lws_acme_report_status(vhd->vhost,
						LWS_CUS_ISSUE, NULL);

				/*
				 * That means we have the issued cert DER in
				 * ac->buf, length in ac->cpos; and the key in
				 * ac->alloc_privkey_pem, length in
				 * ac->len_privkey_pem.
				 *
				 * We write out a PEM copy of the cert, and a
				 * PEM copy of the private key, using the
				 * write-only fds we opened while we still
				 * had root.
				 *
				 * Estimate the size of the PEM version of the
				 * cert and allocate a temp buffer for it.
				 *
				 * This is a bit complicated because first we
				 * drop the b64url version into the buffer at
				 * +384, then we add the header at 0 and move
				 * lines of it back + '\n' to make PEM.
				 *
				 * This avoids the need for two fullsize
				 * allocations.
				 */

				max = (ac->cpos * 4) / 3 + 16 + 384;

				start = p = malloc(max);
				if (!p)
					goto failed;

				n = lws_b64_encode_string(ac->buf, ac->cpos,
							  start + 384, max - 384);
				if (n < 0) {
					free(start);
					goto failed;
				}

				pp = start + 384;
				p += lws_snprintf(start, 64, "%s",
						"-----BEGIN CERTIFICATE-----\n");

				while (n) {
					m = 65;
					if (n < m)
						m = n;
					memcpy(p, pp, m);
					n -= m;
					p += m;
					pp += m;
					if (n)
						*p++ = '\n';
				}
				p += lws_snprintf(p,
						  max - lws_ptr_diff(p, start),
						  "%s",
						  "\n-----END CERTIFICATE-----\n");

				n = lws_plat_write_cert(vhd->vhost, 0,
						vhd->fd_updated_cert, start,
						lws_ptr_diff(p, start));
				free(start);
				if (n) {
					lwsl_err("unable to write ACME cert! %d\n", n);
					goto failed;
				}
				/*
				 * don't close it... we may update the certs
				 * again
				 */

				if (lws_plat_write_cert(vhd->vhost, 1,
							vhd->fd_updated_key,
							ac->alloc_privkey_pem,
							ac->len_privkey_pem)) {
					lwsl_err("unable to write ACME key!\n");
					goto failed;
				}

				/*
				 * we have written the persistent copies
				 */

				lwsl_notice("%s: Updated certs written for %s "
					    "to %s.upd and %s.upd\n", __func__,
					    vhd->pvop_active[LWS_TLS_REQ_ELEMENT_COMMON_NAME],
					    vhd->pvop_active[LWS_TLS_SET_CERT_PATH],
					    vhd->pvop_active[LWS_TLS_SET_KEY_PATH]);

				/* notify lws there was a cert update */

				if (lws_tls_cert_updated(vhd->context,
					vhd->pvop_active[LWS_TLS_SET_CERT_PATH],
					vhd->pvop_active[LWS_TLS_SET_KEY_PATH],
					ac->buf, ac->cpos,
					ac->alloc_privkey_pem,
					ac->len_privkey_pem)) {
					lwsl_notice("problem setting certs\n");
				}

				lws_acme_finished(vhd);
				lws_acme_report_status(vhd->vhost,
							LWS_CUS_SUCCESS, NULL);

				return 0;
			}

			lws_acme_report_status(vhd->vhost, LWS_CUS_CONFIRM, NULL);

			/* he is preparing the cert, go again with a GET */

			if (ac->goes_around++ == 30) {
				lwsl_notice("%s: too many retries\n",
					    __func__);

				goto failed;
			}

			strcpy(buf, ac->challenge_uri);
			cwsi = lws_acme_client_connect(vhd->context, vhd->vhost,
						       &ac->cwsi, &ac->i, buf,
						       "GET");
			if (!cwsi) {
				lwsl_notice("%s: failed to connect to acme\n",
					    __func__);

				goto failed;
			}
			return -1; /* close the completed client connection */

		default:
			break;
		}
		break;

		case LWS_CALLBACK_USER + 0xac33:
			if (!vhd)
				break;
			cwsi = lws_acme_client_connect(vhd->context, vhd->vhost,
						       &ac->cwsi, &ac->i,
						       ac->challenge_uri,
						       "GET");
			if (!cwsi) {
				lwsl_notice("%s: failed to connect\n", __func__);
				goto failed;
			}
			break;

	case LWS_CALLBACK_OPENSSL_LOAD_EXTRA_SERVER_VERIFY_CERTS:
		/*
		 * This goes to vhost->protocols[0], but for our temp certs
		 * vhost we created, we have arranged that to be our protocol,
		 * so the callback will come here.
		 *
		 * When we created the temp vhost, we set its pvo to point
		 * to the ac associated with the temp vhost.
		 */
		lwsl_debug("LWS_CALLBACK_OPENSSL_LOAD_EXTRA_SERVER_VERIFY_CERTS\n");
		ac = (struct acme_connection *)lws_get_vhost_user(
							(struct lws_vhost *)in);

		lws_acme_report_status((struct lws_vhost *)in,
				        LWS_CUS_CREATE_REQ,
				        "creating challenge cert");

		if (lws_tls_acme_sni_cert_create((struct lws_vhost *)in,
						 ac->san_a, ac->san_b)) {
			lwsl_err("%s: creating the sni test cert failed\n", __func__);

			return -1;
		}
		break;

	default:
		break;
	}

	return 0;

failed:
	lwsl_err("%s: failed out\n", __func__);
	lws_acme_report_status(vhd->vhost, LWS_CUS_FAILED, failreason);
	lws_acme_finished(vhd);

	return -1;
}
int
handshake_0405(struct lws_context *context, struct lws *wsi)
{
	unsigned char hash[20];
	int n;
	char *response;
	char *p;
	int accept_len;

	if (!lws_hdr_total_length(wsi, WSI_TOKEN_HOST) ||
				!lws_hdr_total_length(wsi, WSI_TOKEN_KEY)) {
		lwsl_parser("handshake_04 missing pieces\n");
		/* completed header processing, but missing some bits */
		goto bail;
	}

	if (lws_hdr_total_length(wsi, WSI_TOKEN_KEY) >=
						     MAX_WEBSOCKET_04_KEY_LEN) {
		lwsl_warn("Client key too long %d\n", MAX_WEBSOCKET_04_KEY_LEN);
		goto bail;
	}

	/*
	 * since key length is restricted above (currently 128), cannot
	 * overflow
	 */
	n = sprintf((char *)context->serv_buf,
				"%s258EAFA5-E914-47DA-95CA-C5AB0DC85B11",
				lws_hdr_simple_ptr(wsi, WSI_TOKEN_KEY));

	lws_SHA1(context->serv_buf, n, hash);

	accept_len = lws_b64_encode_string((char *)hash, 20,
			(char *)context->serv_buf,
			sizeof(context->serv_buf));
	if (accept_len < 0) {
		lwsl_warn("Base64 encoded hash too long\n");
		goto bail;
	}

	/* allocate the per-connection user memory (if any) */
	if (lws_ensure_user_space(wsi))
		goto bail;

	/* create the response packet */

	/* make a buffer big enough for everything */

	response = (char *)context->serv_buf + MAX_WEBSOCKET_04_KEY_LEN + LWS_SEND_BUFFER_PRE_PADDING;
	p = response;
	LWS_CPYAPP(p, "HTTP/1.1 101 Switching Protocols\x0d\x0a"
		      "Upgrade: WebSocket\x0d\x0a"
		      "Connection: Upgrade\x0d\x0a"
		      "Sec-WebSocket-Accept: ");
	strcpy(p, (char *)context->serv_buf);
	p += accept_len;

	if (lws_hdr_total_length(wsi, WSI_TOKEN_PROTOCOL)) {
		LWS_CPYAPP(p, "\x0d\x0aSec-WebSocket-Protocol: ");
		n = lws_hdr_copy(wsi, p, 128, WSI_TOKEN_PROTOCOL);
		if (n < 0)
			goto bail;
		p += n;
	}

#ifndef LWS_NO_EXTENSIONS
	/*
	 * Figure out which extensions the client has that we want to
	 * enable on this connection, and give him back the list
	 */
	if (lws_extension_server_handshake(wsi, &p))
		goto bail;
#endif

	//LWS_CPYAPP(p, "\x0d\x0a""An-unknown-header: blah");

	/* end of response packet */

	LWS_CPYAPP(p, "\x0d\x0a\x0d\x0a");

	if (!lws_any_extension_handled(wsi, LWS_EXT_CALLBACK_HANDSHAKE_REPLY_TX,
				       response, p - response)) {

		/* okay send the handshake response accepting the connection */

		lwsl_parser("issuing resp pkt %d len\n", (int)(p - response));
#ifdef DEBUG
		fwrite(response, 1,  p - response, stderr);
#endif
		n = lws_write(wsi, (unsigned char *)response,
			      p - response, LWS_WRITE_HTTP_HEADERS);
		if (n != (p - response)) {
			lwsl_debug("handshake_0405: ERROR writing to socket\n");
			goto bail;
		}

	}

	/* alright clean up and set ourselves into established state */

	wsi->state = LWSS_ESTABLISHED;
	wsi->lws_rx_parse_state = LWS_RXPS_NEW;

	/* notify user code that we're ready to roll */

	if (wsi->protocol->callback)
		if (wsi->protocol->callback(wsi, LWS_CALLBACK_ESTABLISHED,
					    wsi->user_space,
#ifdef LWS_OPENSSL_SUPPORT
					    wsi->ssl,
#else
					    NULL,
#endif
					    0))
			goto bail;

	return 0;


bail:
	/* free up his parsing allocations */
	lws_free_header_table(wsi);
	return -1;
}
int
handshake_0405(struct lws_context *context, struct lws *wsi)
{
	struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
	unsigned char hash[20];
	int n, accept_len;
	char *response;
	char *p;

	if (!lws_hdr_total_length(wsi, WSI_TOKEN_HOST) ||
	    !lws_hdr_total_length(wsi, WSI_TOKEN_KEY)) {
		lwsl_parser("handshake_04 missing pieces\n");
		/* completed header processing, but missing some bits */
		goto bail;
	}

	if (lws_hdr_total_length(wsi, WSI_TOKEN_KEY) >= MAX_WEBSOCKET_04_KEY_LEN) {
		lwsl_warn("Client key too long %d\n", MAX_WEBSOCKET_04_KEY_LEN);
		goto bail;
	}

	/*
	 * since key length is restricted above (currently 128), cannot
	 * overflow
	 */
	n = sprintf((char *)pt->serv_buf,
		    "%s258EAFA5-E914-47DA-95CA-C5AB0DC85B11",
		    lws_hdr_simple_ptr(wsi, WSI_TOKEN_KEY));

	lws_SHA1(pt->serv_buf, n, hash);

	accept_len = lws_b64_encode_string((char *)hash, 20,
			(char *)pt->serv_buf, context->pt_serv_buf_size);
	if (accept_len < 0) {
		lwsl_warn("Base64 encoded hash too long\n");
		goto bail;
	}

	/* allocate the per-connection user memory (if any) */
	if (lws_ensure_user_space(wsi))
		goto bail;

	/* create the response packet */

	/* make a buffer big enough for everything */

	response = (char *)pt->serv_buf + MAX_WEBSOCKET_04_KEY_LEN + LWS_PRE;
	p = response;
	LWS_CPYAPP(p, "HTTP/1.1 101 Switching Protocols\x0d\x0a"
		      "Upgrade: WebSocket\x0d\x0a"
		      "Connection: Upgrade\x0d\x0a"
		      "Sec-WebSocket-Accept: ");
	strcpy(p, (char *)pt->serv_buf);
	p += accept_len;

	/* we can only return the protocol header if:
	 *  - one came in, and ... */
	if (lws_hdr_total_length(wsi, WSI_TOKEN_PROTOCOL) &&
	    /*  - it is not an empty string */
	    wsi->protocol->name &&
	    wsi->protocol->name[0]) {
		LWS_CPYAPP(p, "\x0d\x0aSec-WebSocket-Protocol: ");
		p += lws_snprintf(p, 128, "%s", wsi->protocol->name);
	}

#ifndef LWS_NO_EXTENSIONS
	/*
	 * Figure out which extensions the client has that we want to
	 * enable on this connection, and give him back the list.
	 *
	 * Give him a limited write bugdet
	 */
	if (lws_extension_server_handshake(wsi, &p, 192))
		goto bail;
#endif

	//LWS_CPYAPP(p, "\x0d\x0a""An-unknown-header: blah");

	/* end of response packet */

	LWS_CPYAPP(p, "\x0d\x0a\x0d\x0a");

	if (!lws_any_extension_handled(wsi, LWS_EXT_CB_HANDSHAKE_REPLY_TX,
				       response, p - response)) {

		/* okay send the handshake response accepting the connection */

		lwsl_parser("issuing resp pkt %d len\n", (int)(p - response));
#if defined(DEBUG) && ! defined(LWS_WITH_ESP8266)
		fwrite(response, 1,  p - response, stderr);
#endif
		n = lws_write(wsi, (unsigned char *)response,
			      p - response, LWS_WRITE_HTTP_HEADERS);
		if (n != (p - response)) {
			lwsl_debug("handshake_0405: ERROR writing to socket\n");
			goto bail;
		}

	}

	/* alright clean up and set ourselves into established state */

	wsi->state = LWSS_ESTABLISHED;
	wsi->lws_rx_parse_state = LWS_RXPS_NEW;

	{
		const char * uri_ptr =
			lws_hdr_simple_ptr(wsi, WSI_TOKEN_GET_URI);
		int uri_len = lws_hdr_total_length(wsi, WSI_TOKEN_GET_URI);
		const struct lws_http_mount *hit =
			lws_find_mount(wsi, uri_ptr, uri_len);
		if (hit && hit->cgienv &&
		    wsi->protocol->callback(wsi, LWS_CALLBACK_HTTP_PMO,
			wsi->user_space, (void *)hit->cgienv, 0))
			return 1;
	}

	return 0;


bail:
	/* caller will free up his parsing allocations */
	return -1;
}
int
handshake_0405(struct libwebsocket_context *context, struct libwebsocket *wsi)
{
	unsigned char hash[20];
	int n;
	char *response;
	char *p;
	int accept_len;
#ifndef LWS_NO_EXTENSIONS
	char *c;
	char ext_name[128];
	struct libwebsocket_extension *ext;
	int ext_count = 0;
	int more = 1;
#endif

	if (!lws_hdr_total_length(wsi, WSI_TOKEN_HOST) ||
				!lws_hdr_total_length(wsi, WSI_TOKEN_KEY)) {
		lwsl_parser("handshake_04 missing pieces\n");
		/* completed header processing, but missing some bits */
		goto bail;
	}

	if (lws_hdr_total_length(wsi, WSI_TOKEN_KEY) >=
						     MAX_WEBSOCKET_04_KEY_LEN) {
		lwsl_warn("Client key too long %d\n", MAX_WEBSOCKET_04_KEY_LEN);
		goto bail;
	}

	/*
	 * since key length is restricted above (currently 128), cannot
	 * overflow
	 */
	n = sprintf((char *)context->service_buffer,
				"%s258EAFA5-E914-47DA-95CA-C5AB0DC85B11",
				lws_hdr_simple_ptr(wsi, WSI_TOKEN_KEY));

	SHA1(context->service_buffer, n, hash);

	accept_len = lws_b64_encode_string((char *)hash, 20,
			(char *)context->service_buffer,
			sizeof(context->service_buffer));
	if (accept_len < 0) {
		lwsl_warn("Base64 encoded hash too long\n");
		goto bail;
	}

	/* allocate the per-connection user memory (if any) */
	if (libwebsocket_ensure_user_space(wsi))
		goto bail;

	/* create the response packet */

	/* make a buffer big enough for everything */

	response = (char *)context->service_buffer + MAX_WEBSOCKET_04_KEY_LEN;
	p = response;
	LWS_CPYAPP(p, "HTTP/1.1 101 Switching Protocols\x0d\x0a"
		      "Upgrade: WebSocket\x0d\x0a"
		      "Connection: Upgrade\x0d\x0a"
		      "Sec-WebSocket-Accept: ");
	strcpy(p, (char *)context->service_buffer);
	p += accept_len;

	if (lws_hdr_total_length(wsi, WSI_TOKEN_PROTOCOL)) {
		LWS_CPYAPP(p, "\x0d\x0aSec-WebSocket-Protocol: ");
		n = lws_hdr_copy(wsi, p, 128, WSI_TOKEN_PROTOCOL);
		if (n < 0)
			goto bail;
		p += n;
	}

#ifndef LWS_NO_EXTENSIONS
	/*
	 * Figure out which extensions the client has that we want to
	 * enable on this connection, and give him back the list
	 */

	if (lws_hdr_total_length(wsi, WSI_TOKEN_EXTENSIONS)) {

		/*
		 * break down the list of client extensions
		 * and go through them
		 */

		if (lws_hdr_copy(wsi, (char *)context->service_buffer,
				sizeof(context->service_buffer),
						      WSI_TOKEN_EXTENSIONS) < 0)
			goto bail;

		c = (char *)context->service_buffer;
		lwsl_parser("WSI_TOKEN_EXTENSIONS = '%s'\n", c);
		wsi->count_active_extensions = 0;
		n = 0;
		while (more) {

			if (*c && (*c != ',' && *c != ' ' && *c != '\t')) {
				ext_name[n] = *c++;
				if (n < sizeof(ext_name) - 1)
					n++;
				continue;
			}
			ext_name[n] = '\0';
			if (!*c)
				more = 0;
			else {
				c++;
				if (!n)
					continue;
			}

			/* check a client's extension against our support */

			ext = wsi->protocol->owning_server->extensions;

			while (ext && ext->callback) {

				if (strcmp(ext_name, ext->name)) {
					ext++;
					continue;
				}

				/*
				 * oh, we do support this one he
				 * asked for... but let's ask user
				 * code if it's OK to apply it on this
				 * particular connection + protocol
				 */

				n = wsi->protocol->owning_server->
					protocols[0].callback(
						wsi->protocol->owning_server,
						wsi,
					  LWS_CALLBACK_CONFIRM_EXTENSION_OKAY,
						  wsi->user_space, ext_name, 0);

				/*
				 * zero return from callback means
				 * go ahead and allow the extension,
				 * it's what we get if the callback is
				 * unhandled
				 */

				if (n) {
					ext++;
					continue;
				}

				/* apply it */

				if (ext_count)
					*p++ = ',';
				else
					LWS_CPYAPP(p,
					 "\x0d\x0aSec-WebSocket-Extensions: ");
				p += sprintf(p, "%s", ext_name);
				ext_count++;

				/* instantiate the extension on this conn */

				wsi->active_extensions_user[
					wsi->count_active_extensions] =
					     malloc(ext->per_session_data_size);
				if (wsi->active_extensions_user[
				     wsi->count_active_extensions] == NULL) {
					lwsl_err("Out of mem\n");
					free(response);
					goto bail;
				}
				memset(wsi->active_extensions_user[
					wsi->count_active_extensions], 0,
						    ext->per_session_data_size);

				wsi->active_extensions[
					  wsi->count_active_extensions] = ext;

				/* allow him to construct his context */

				ext->callback(wsi->protocol->owning_server,
						ext, wsi,
						LWS_EXT_CALLBACK_CONSTRUCT,
						wsi->active_extensions_user[
					wsi->count_active_extensions], NULL, 0);

				wsi->count_active_extensions++;
				lwsl_parser("count_active_extensions <- %d\n",
						  wsi->count_active_extensions);

				ext++;
			}

			n = 0;
		}
	}
#endif
	/* end of response packet */

	LWS_CPYAPP(p, "\x0d\x0a\x0d\x0a");
	
	if (!lws_any_extension_handled(context, wsi,
			LWS_EXT_CALLBACK_HANDSHAKE_REPLY_TX,
						     response, p - response)) {

		/* okay send the handshake response accepting the connection */

		lwsl_parser("issuing resp pkt %d len\n", (int)(p - response));
	#ifdef DEBUG
		fwrite(response, 1,  p - response, stderr);
	#endif
		n = libwebsocket_write(wsi, (unsigned char *)response,
						  p - response, LWS_WRITE_HTTP);
		if (n != (p - response)) {
			lwsl_debug("handshake_0405: ERROR writing to socket\n");
			goto bail;
		}

	}

	/* alright clean up and set ourselves into established state */

	wsi->state = WSI_STATE_ESTABLISHED;
	wsi->lws_rx_parse_state = LWS_RXPS_NEW;

	/* notify user code that we're ready to roll */

	if (wsi->protocol->callback)
		wsi->protocol->callback(wsi->protocol->owning_server,
				wsi, LWS_CALLBACK_ESTABLISHED,
					  wsi->user_space, NULL, 0);

	return 0;


bail:
	/* free up his parsing allocations */

	if (wsi->u.hdr.ah)
		free(wsi->u.hdr.ah);

	return -1;
}
Example #10
0
char *
lws_generate_client_handshake(struct lws *wsi, char *pkt)
{
	char buf[128], hash[20], key_b64[40], *p = pkt;
	struct lws_context *context = wsi->context;
	const char *meth;
	int n;
#ifndef LWS_NO_EXTENSIONS
	const struct lws_extension *ext;
	int ext_count = 0;
#endif
	const char *pp = lws_hdr_simple_ptr(wsi,
				_WSI_TOKEN_CLIENT_SENT_PROTOCOLS);

	meth = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_METHOD);
	if (!meth) {
		meth = "GET";
		wsi->do_ws = 1;
	} else {
		wsi->do_ws = 0;
	}

	if (!strcmp(meth, "RAW")) {
		lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);
		lwsl_notice("client transition to raw\n");

		if (pp) {
			const struct lws_protocols *pr;

			pr = lws_vhost_name_to_protocol(wsi->vhost, pp);

			if (!pr) {
				lwsl_err("protocol %s not enabled on vhost\n",
					 pp);
				return NULL;
			}

			lws_bind_protocol(wsi, pr);
		}

		if ((wsi->protocol->callback)(wsi,
				LWS_CALLBACK_RAW_ADOPT,
				wsi->user_space, NULL, 0))
			return NULL;

		lws_header_table_force_to_detachable_state(wsi);
		lws_union_transition(wsi, LWSCM_RAW);
		lws_header_table_detach(wsi, 1);

		return NULL;
	}

	if (wsi->do_ws) {
		/*
		 * create the random key
		 */
		n = lws_get_random(context, hash, 16);
		if (n != 16) {
			lwsl_err("Unable to read from random dev %s\n",
				 SYSTEM_RANDOM_FILEPATH);
			lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS);
			return NULL;
		}

		lws_b64_encode_string(hash, 16, key_b64, sizeof(key_b64));
	}

	/*
	 * 04 example client handshake
	 *
	 * GET /chat HTTP/1.1
	 * Host: server.example.com
	 * Upgrade: websocket
	 * Connection: Upgrade
	 * Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
	 * Sec-WebSocket-Origin: http://example.com
	 * Sec-WebSocket-Protocol: chat, superchat
	 * Sec-WebSocket-Version: 4
	 */

	p += sprintf(p, "%s %s HTTP/1.1\x0d\x0a", meth,
		     lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_URI));

	p += sprintf(p, "Pragma: no-cache\x0d\x0a"
			"Cache-Control: no-cache\x0d\x0a");

	p += sprintf(p, "Host: %s\x0d\x0a",
		     lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_HOST));

	if (lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_ORIGIN)) {
		if (lws_check_opt(context->options, LWS_SERVER_OPTION_JUST_USE_RAW_ORIGIN))
			p += sprintf(p, "Origin: %s\x0d\x0a",
				     lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_ORIGIN));
		else
			p += sprintf(p, "Origin: http://%s\x0d\x0a",
				     lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_ORIGIN));
	}

	if (wsi->do_ws) {
		p += sprintf(p, "Upgrade: websocket\x0d\x0a"
				"Connection: Upgrade\x0d\x0a"
				"Sec-WebSocket-Key: ");
		strcpy(p, key_b64);
		p += strlen(key_b64);
		p += sprintf(p, "\x0d\x0a");
		if (lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_SENT_PROTOCOLS))
			p += sprintf(p, "Sec-WebSocket-Protocol: %s\x0d\x0a",
			     lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_SENT_PROTOCOLS));

		/* tell the server what extensions we could support */

#ifndef LWS_NO_EXTENSIONS
		ext = wsi->vhost->extensions;
		while (ext && ext->callback) {
			n = lws_ext_cb_all_exts(context, wsi,
				   LWS_EXT_CB_CHECK_OK_TO_PROPOSE_EXTENSION,
				   (char *)ext->name, 0);
			if (n) { /* an extension vetos us */
				lwsl_ext("ext %s vetoed\n", (char *)ext->name);
				ext++;
				continue;
			}
			n = wsi->vhost->protocols[0].callback(wsi,
				LWS_CALLBACK_CLIENT_CONFIRM_EXTENSION_SUPPORTED,
					wsi->user_space, (char *)ext->name, 0);

			/*
			 * zero return from callback means
			 * go ahead and allow the extension,
			 * it's what we get if the callback is
			 * unhandled
			 */

			if (n) {
				ext++;
				continue;
			}

			/* apply it */

			if (ext_count)
				*p++ = ',';
			else
				p += sprintf(p, "Sec-WebSocket-Extensions: ");
			p += sprintf(p, "%s", ext->client_offer);
			ext_count++;

			ext++;
		}
		if (ext_count)
			p += sprintf(p, "\x0d\x0a");
#endif

		if (wsi->ietf_spec_revision)
			p += sprintf(p, "Sec-WebSocket-Version: %d\x0d\x0a",
				     wsi->ietf_spec_revision);

		/* prepare the expected server accept response */

		key_b64[39] = '\0'; /* enforce composed length below buf sizeof */
		n = sprintf(buf, "%s258EAFA5-E914-47DA-95CA-C5AB0DC85B11", key_b64);

		lws_SHA1((unsigned char *)buf, n, (unsigned char *)hash);

		lws_b64_encode_string(hash, 20,
				      wsi->u.hdr.ah->initial_handshake_hash_base64,
				      sizeof(wsi->u.hdr.ah->initial_handshake_hash_base64));
	}

	/* give userland a chance to append, eg, cookies */

	wsi->protocol->callback(wsi, LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER,
				wsi->user_space, &p, (pkt + context->pt_serv_buf_size) - p - 12);

	p += sprintf(p, "\x0d\x0a");

	return p;
}
Example #11
0
char *
lws_generate_client_ws_handshake(struct lws *wsi, char *p, const char *conn1)
{
	char buf[128], hash[20], key_b64[40];
	int n;
#if !defined(LWS_WITHOUT_EXTENSIONS)
	const struct lws_extension *ext;
	int ext_count = 0;
#endif

	/*
	 * create the random key
	 */
	n = lws_get_random(wsi->context, hash, 16);
	if (n != 16) {
		lwsl_err("Unable to read from random dev %s\n",
			 SYSTEM_RANDOM_FILEPATH);
		return NULL;
	}

	lws_b64_encode_string(hash, 16, key_b64, sizeof(key_b64));

	p += sprintf(p, "Upgrade: websocket\x0d\x0a"
			"Connection: %sUpgrade\x0d\x0a"
			"Sec-WebSocket-Key: ", conn1);
	strcpy(p, key_b64);
	p += strlen(key_b64);
	p += sprintf(p, "\x0d\x0a");
	if (lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_SENT_PROTOCOLS))
		p += sprintf(p, "Sec-WebSocket-Protocol: %s\x0d\x0a",
		     lws_hdr_simple_ptr(wsi,
				     _WSI_TOKEN_CLIENT_SENT_PROTOCOLS));

	/* tell the server what extensions we could support */

#if !defined(LWS_WITHOUT_EXTENSIONS)
	ext = wsi->vhost->ws.extensions;
	while (ext && ext->callback) {

		n = wsi->vhost->protocols[0].callback(wsi,
			LWS_CALLBACK_CLIENT_CONFIRM_EXTENSION_SUPPORTED,
				wsi->user_space, (char *)ext->name, 0);

		/*
		 * zero return from callback means go ahead and allow
		 * the extension, it's what we get if the callback is
		 * unhandled
		 */

		if (n) {
			ext++;
			continue;
		}

		/* apply it */

		if (ext_count)
			*p++ = ',';
		else
			p += sprintf(p, "Sec-WebSocket-Extensions: ");
		p += sprintf(p, "%s", ext->client_offer);
		ext_count++;

		ext++;
	}
	if (ext_count)
		p += sprintf(p, "\x0d\x0a");
#endif

	if (wsi->ws->ietf_spec_revision)
		p += sprintf(p, "Sec-WebSocket-Version: %d\x0d\x0a",
			     wsi->ws->ietf_spec_revision);

	/* prepare the expected server accept response */

	key_b64[39] = '\0'; /* enforce composed length below buf sizeof */
	n = sprintf(buf, "%s258EAFA5-E914-47DA-95CA-C5AB0DC85B11",
			  key_b64);

	lws_SHA1((unsigned char *)buf, n, (unsigned char *)hash);

	lws_b64_encode_string(hash, 20,
		  wsi->http.ah->initial_handshake_hash_base64,
		  sizeof(wsi->http.ah->initial_handshake_hash_base64));

	return p;
}
Example #12
0
int
handshake_0405(struct libwebsocket_context *context, struct libwebsocket *wsi)
{
	static const char *websocket_magic_guid_04 =
					 "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
	char accept_buf[MAX_WEBSOCKET_04_KEY_LEN + 37];
	unsigned char hash[20];
	int n;
	char *response;
	char *p;
	int accept_len;
#ifndef LWS_NO_EXTENSIONS
	char *c;
	char ext_name[128];
	struct libwebsocket_extension *ext;
	int ext_count = 0;
	int more = 1;
#endif

	if (!wsi->utf8_token[WSI_TOKEN_HOST].token_len ||
	    !wsi->utf8_token[WSI_TOKEN_KEY].token_len) {
		lwsl_parser("handshake_04 missing pieces\n");
		/* completed header processing, but missing some bits */
		goto bail;
	}

	if (wsi->utf8_token[WSI_TOKEN_KEY].token_len >=
						     MAX_WEBSOCKET_04_KEY_LEN) {
		lwsl_warn("Client sent handshake key longer "
			   "than max supported %d\n", MAX_WEBSOCKET_04_KEY_LEN);
		goto bail;
	}

	strcpy(accept_buf, wsi->utf8_token[WSI_TOKEN_KEY].token);
	strcpy(accept_buf + wsi->utf8_token[WSI_TOKEN_KEY].token_len,
						       websocket_magic_guid_04);

	SHA1((unsigned char *)accept_buf,
			wsi->utf8_token[WSI_TOKEN_KEY].token_len +
					 strlen(websocket_magic_guid_04), hash);

	accept_len = lws_b64_encode_string((char *)hash, 20, accept_buf,
							     sizeof accept_buf);
	if (accept_len < 0) {
		lwsl_warn("Base64 encoded hash too long\n");
		goto bail;
	}

	/* allocate the per-connection user memory (if any) */
	if (wsi->protocol->per_session_data_size &&
					  !libwebsocket_ensure_user_space(wsi))
		goto bail;

	/* create the response packet */

	/* make a buffer big enough for everything */

	response = (char *)malloc(256 +
		wsi->utf8_token[WSI_TOKEN_UPGRADE].token_len +
		wsi->utf8_token[WSI_TOKEN_CONNECTION].token_len +
		wsi->utf8_token[WSI_TOKEN_PROTOCOL].token_len);
	if (!response) {
		lwsl_err("Out of memory for response buffer\n");
		goto bail;
	}

	p = response;
	LWS_CPYAPP(p, "HTTP/1.1 101 Switching Protocols\x0d\x0a"
		      "Upgrade: WebSocket\x0d\x0a"
		      "Connection: Upgrade\x0d\x0a"
		      "Sec-WebSocket-Accept: ");
	strcpy(p, accept_buf);
	p += accept_len;

	if (wsi->utf8_token[WSI_TOKEN_PROTOCOL].token) {
		LWS_CPYAPP(p, "\x0d\x0aSec-WebSocket-Protocol: ");
		LWS_CPYAPP_TOKEN(p, WSI_TOKEN_PROTOCOL);
	}

#ifndef LWS_NO_EXTENSIONS
	/*
	 * Figure out which extensions the client has that we want to
	 * enable on this connection, and give him back the list
	 */

	if (wsi->utf8_token[WSI_TOKEN_EXTENSIONS].token_len) {

		/*
		 * break down the list of client extensions
		 * and go through them
		 */

		c = wsi->utf8_token[WSI_TOKEN_EXTENSIONS].token;
		lwsl_parser("wsi->utf8_token[WSI_TOKEN_EXTENSIONS].token = %s\n",
				  wsi->utf8_token[WSI_TOKEN_EXTENSIONS].token);
		wsi->count_active_extensions = 0;
		n = 0;
		while (more) {

			if (*c && (*c != ',' && *c != ' ' && *c != '\t')) {
				ext_name[n] = *c++;
				if (n < sizeof(ext_name) - 1)
					n++;
				continue;
			}
			ext_name[n] = '\0';
			if (!*c)
				more = 0;
			else {
				c++;
				if (!n)
					continue;
			}

			/* check a client's extension against our support */

			ext = wsi->protocol->owning_server->extensions;

			while (ext && ext->callback) {

				if (strcmp(ext_name, ext->name)) {
					ext++;
					continue;
				}

				/*
				 * oh, we do support this one he
				 * asked for... but let's ask user
				 * code if it's OK to apply it on this
				 * particular connection + protocol
				 */

				n = wsi->protocol->owning_server->
					protocols[0].callback(
						wsi->protocol->owning_server,
						wsi,
					  LWS_CALLBACK_CONFIRM_EXTENSION_OKAY,
						  wsi->user_space, ext_name, 0);

				/*
				 * zero return from callback means
				 * go ahead and allow the extension,
				 * it's what we get if the callback is
				 * unhandled
				 */

				if (n) {
					ext++;
					continue;
				}

				/* apply it */

				if (ext_count)
					*p++ = ',';
				else
					LWS_CPYAPP(p,
					 "\x0d\x0aSec-WebSocket-Extensions: ");
				p += sprintf(p, "%s", ext_name);
				ext_count++;

				/* instantiate the extension on this conn */

				wsi->active_extensions_user[
					wsi->count_active_extensions] =
					     malloc(ext->per_session_data_size);
				if (wsi->active_extensions_user[
					 wsi->count_active_extensions] == NULL) {
					lwsl_err("Out of mem\n");
					free(response);
					goto bail;
				}
				memset(wsi->active_extensions_user[
					wsi->count_active_extensions], 0,
						    ext->per_session_data_size);

				wsi->active_extensions[
					  wsi->count_active_extensions] = ext;

				/* allow him to construct his context */

				ext->callback(wsi->protocol->owning_server,
						ext, wsi,
						LWS_EXT_CALLBACK_CONSTRUCT,
						wsi->active_extensions_user[
					wsi->count_active_extensions], NULL, 0);

				wsi->count_active_extensions++;
				lwsl_parser("wsi->count_active_extensions <- %d\n",
						  wsi->count_active_extensions);

				ext++;
			}

			n = 0;
		}
	}
#endif
	/* end of response packet */

	LWS_CPYAPP(p, "\x0d\x0a\x0d\x0a");

#ifndef LWS_NO_EXTENSIONS
	if (!lws_any_extension_handled(context, wsi,
			LWS_EXT_CALLBACK_HANDSHAKE_REPLY_TX,
						     response, p - response))
#endif
	{
		/* okay send the handshake response accepting the connection */

		lwsl_parser("issuing response packet %d len\n", (int)(p - response));
	#ifdef DEBUG
//		fwrite(response, 1,  p - response, stderr);
	#endif
		n = libwebsocket_write(wsi, (unsigned char *)response,
						  p - response, LWS_WRITE_HTTP);
		if (n < 0) {
			lwsl_debug("handshake_0405: ERROR writing to socket\n");
			goto bail;
		}

	}

	/* alright clean up and set ourselves into established state */

	free(response);
	wsi->state = WSI_STATE_ESTABLISHED;
	wsi->lws_rx_parse_state = LWS_RXPS_NEW;
	wsi->u.ws.rx_packet_length = 0;

	/* notify user code that we're ready to roll */

	if (wsi->protocol->callback)
		wsi->protocol->callback(wsi->protocol->owning_server,
				wsi, LWS_CALLBACK_ESTABLISHED,
					  wsi->user_space, NULL, 0);

	return 0;


bail:
	return -1;
}