예제 #1
0
static int
libwebsocket_0405_frame_mask_generate(struct libwebsocket *wsi)
{
	int n;

	/* fetch the per-frame nonce */

	n = libwebsockets_get_random(wsi->protocol->owning_server,
					   wsi->u.ws.frame_masking_nonce_04, 4);
	if (n != 4) {
		lwsl_parser("Unable to read from random device %s %d\n",
						     SYSTEM_RANDOM_FILEPATH, n);
		return 1;
	}

	/* start masking from first byte of masking key buffer */
	wsi->u.ws.frame_mask_index = 0;

	return 0;
}
예제 #2
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;
}
예제 #3
0
char *
libwebsockets_generate_client_handshake(struct libwebsocket_context *context,
		struct libwebsocket *wsi, char *pkt)
{
	char buf[128];
	char hash[20];
	char key_b64[40];
	char *p = pkt;
	int n;
#ifndef LWS_NO_EXTENSIONS
	struct libwebsocket_extension *ext;
	int ext_count = 0;
#endif

	/*
	 * create the random key
	 */

	n = libwebsockets_get_random(context, hash, 16);
	if (n != 16) {
		lwsl_err("Unable to read from random dev %s\n",
						SYSTEM_RANDOM_FILEPATH);
		libwebsocket_close_and_free_session(context, 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_callback_for_each_extension_type(context, wsi,
			   LWS_EXT_CALLBACK_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(context, 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->name);
		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(context, wsi,
		LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER,
		NULL, &p, (pkt + sizeof(context->service_buffer)) - 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);

	libwebsockets_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;
}