LWS_VISIBLE struct lws * lws_client_connect_via_info(struct lws_client_connect_info *i) { struct lws *wsi; int v = SPEC_LATEST_SUPPORTED; const struct lws_protocols *p; if (i->context->requested_kill) return NULL; if (!i->context->protocol_init_done) lws_protocol_init(i->context); wsi = lws_zalloc(sizeof(struct lws)); if (wsi == NULL) goto bail; wsi->context = i->context; /* assert the mode and union status (hdr) clearly */ lws_union_transition(wsi, LWSCM_HTTP_CLIENT); wsi->desc.sockfd = LWS_SOCK_INVALID; /* 1) fill up the wsi with stuff from the connect_info as far as it * can go. It's because not only is our connection async, we might * not even be able to get ahold of an ah at this point. */ /* -1 means just use latest supported */ if (i->ietf_version_or_minus_one != -1 && i->ietf_version_or_minus_one) v = i->ietf_version_or_minus_one; wsi->ietf_spec_revision = v; wsi->user_space = NULL; wsi->state = LWSS_CLIENT_UNCONNECTED; wsi->pending_timeout = NO_PENDING_TIMEOUT; wsi->position_in_fds_table = -1; wsi->c_port = i->port; wsi->vhost = i->vhost; if (!wsi->vhost) wsi->vhost = i->context->vhost_list; wsi->protocol = &wsi->vhost->protocols[0]; /* for http[s] connection, allow protocol selection by name */ if (i->method && i->vhost && i->protocol) { p = lws_vhost_name_to_protocol(i->vhost, i->protocol); if (p) wsi->protocol = p; } if (wsi && !wsi->user_space && i->userdata) { wsi->user_space_externally_allocated = 1; wsi->user_space = i->userdata; } else /* if we stay in http, we can assign the user space now, * otherwise do it after the protocol negotiated */ if (i->method) if (lws_ensure_user_space(wsi)) goto bail; #ifdef LWS_OPENSSL_SUPPORT wsi->use_ssl = i->ssl_connection; #else if (i->ssl_connection) { lwsl_err("libwebsockets not configured for ssl\n"); goto bail; } #endif /* 2) stash the things from connect_info that we can't process without * an ah. Because if no ah, we will go on the ah waiting list and * process those things later (after the connect_info and maybe the * things pointed to have gone out of scope. */ wsi->u.hdr.stash = lws_malloc(sizeof(*wsi->u.hdr.stash)); if (!wsi->u.hdr.stash) { lwsl_err("%s: OOM\n", __func__); goto bail; } wsi->u.hdr.stash->origin[0] = '\0'; wsi->u.hdr.stash->protocol[0] = '\0'; wsi->u.hdr.stash->method[0] = '\0'; wsi->u.hdr.stash->iface[0] = '\0'; strncpy(wsi->u.hdr.stash->address, i->address, sizeof(wsi->u.hdr.stash->address) - 1); strncpy(wsi->u.hdr.stash->path, i->path, sizeof(wsi->u.hdr.stash->path) - 1); strncpy(wsi->u.hdr.stash->host, i->host, sizeof(wsi->u.hdr.stash->host) - 1); if (i->origin) strncpy(wsi->u.hdr.stash->origin, i->origin, sizeof(wsi->u.hdr.stash->origin) - 1); if (i->protocol) strncpy(wsi->u.hdr.stash->protocol, i->protocol, sizeof(wsi->u.hdr.stash->protocol) - 1); if (i->method) strncpy(wsi->u.hdr.stash->method, i->method, sizeof(wsi->u.hdr.stash->method) - 1); if (i->iface) strncpy(wsi->u.hdr.stash->iface, i->iface, sizeof(wsi->u.hdr.stash->iface) - 1); wsi->u.hdr.stash->address[sizeof(wsi->u.hdr.stash->address) - 1] = '\0'; wsi->u.hdr.stash->path[sizeof(wsi->u.hdr.stash->path) - 1] = '\0'; wsi->u.hdr.stash->host[sizeof(wsi->u.hdr.stash->host) - 1] = '\0'; wsi->u.hdr.stash->origin[sizeof(wsi->u.hdr.stash->origin) - 1] = '\0'; wsi->u.hdr.stash->protocol[sizeof(wsi->u.hdr.stash->protocol) - 1] = '\0'; wsi->u.hdr.stash->method[sizeof(wsi->u.hdr.stash->method) - 1] = '\0'; wsi->u.hdr.stash->iface[sizeof(wsi->u.hdr.stash->iface) - 1] = '\0'; if (i->pwsi) *i->pwsi = wsi; /* if we went on the waiting list, no probs just return the wsi * when we get the ah, now or later, he will call * lws_client_connect_via_info2() below. */ if (lws_header_table_attach(wsi, 0) < 0) { /* * if we failed here, the connection is already closed * and freed. */ goto bail1; } if (i->parent_wsi) { lwsl_info("%s: created child %p of parent %p\n", __func__, wsi, i->parent_wsi); wsi->parent = i->parent_wsi; wsi->sibling_list = i->parent_wsi->child_list; i->parent_wsi->child_list = wsi; } #ifdef LWS_WITH_HTTP_PROXY if (i->uri_replace_to) wsi->rw = lws_rewrite_create(wsi, html_parser_cb, i->uri_replace_from, i->uri_replace_to); #endif return wsi; bail: lws_free(wsi); bail1: if (i->pwsi) *i->pwsi = NULL; return NULL; }
static int callback_messageboard(struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len) { struct per_session_data__gs_mb *pss = (struct per_session_data__gs_mb *)user; const struct lws_protocol_vhost_options *pvo; struct per_vhost_data__gs_mb *vhd = (struct per_vhost_data__gs_mb *) lws_protocol_vh_priv_get(lws_get_vhost(wsi), lws_get_protocol(wsi)); unsigned char *p, *start, *end, buffer[LWS_PRE + 256]; char s[512]; int n; switch (reason) { case LWS_CALLBACK_PROTOCOL_INIT: /* per vhost */ vhd = lws_protocol_vh_priv_zalloc(lws_get_vhost(wsi), lws_get_protocol(wsi), sizeof(struct per_vhost_data__gs_mb)); if (!vhd) return 1; vhd->vh = lws_get_vhost(wsi); vhd->gsp = lws_vhost_name_to_protocol(vhd->vh, "protocol-generic-sessions"); if (!vhd->gsp) { lwsl_err("messageboard: requires generic-sessions\n"); return 1; } pvo = (const struct lws_protocol_vhost_options *)in; while (pvo) { if (!strcmp(pvo->name, "message-db")) strncpy(vhd->message_db, pvo->value, sizeof(vhd->message_db) - 1); pvo = pvo->next; } if (!vhd->message_db[0]) { lwsl_err("messageboard: \"message-db\" pvo missing\n"); return 1; } if (sqlite3_open_v2(vhd->message_db, &vhd->pdb, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL) != SQLITE_OK) { lwsl_err("Unable to open message db %s: %s\n", vhd->message_db, sqlite3_errmsg(vhd->pdb)); return 1; } if (sqlite3_exec(vhd->pdb, "create table if not exists msg (" " idx integer primary key, time integer," " username varchar(32), email varchar(100)," " ip varchar(80), content blob);", NULL, NULL, NULL) != SQLITE_OK) { lwsl_err("Unable to create msg table: %s\n", sqlite3_errmsg(vhd->pdb)); return 1; } vhd->last_idx = get_last_idx(vhd); break; case LWS_CALLBACK_PROTOCOL_DESTROY: if (vhd->pdb) sqlite3_close(vhd->pdb); goto passthru; case LWS_CALLBACK_ESTABLISHED: vhd->gsp->callback(wsi, LWS_CALLBACK_SESSION_INFO, pss->pss_gs, &pss->sinfo, 0); if (!pss->sinfo.username[0]) { lwsl_notice("messageboard ws attempt with no session\n"); return -1; } lws_callback_on_writable(wsi); break; case LWS_CALLBACK_SERVER_WRITEABLE: { struct message m; char j[MAX_MSG_LEN + 512], e[MAX_MSG_LEN + 512], *p = j + LWS_PRE, *start = p, *end = j + sizeof(j) - LWS_PRE; if (pss->last_idx == vhd->last_idx) break; /* restrict to last 10 */ if (!pss->last_idx) if (vhd->last_idx >= 10) pss->last_idx = vhd->last_idx - 10; sprintf(s, "select idx, time, username, email, ip, content " "from msg where idx > %lu order by idx limit 1;", pss->last_idx); if (sqlite3_exec(vhd->pdb, s, lookup_cb, &m, NULL) != SQLITE_OK) { lwsl_err("Unable to lookup msg: %s\n", sqlite3_errmsg(vhd->pdb)); return 0; } /* format in JSON */ p += snprintf(p, end - p, "{\"idx\":\"%lu\",\"time\":\"%lu\",", m.idx, m.time); p += snprintf(p, end - p, " \"username\":\"%s\",", lws_json_purify(e, m.username, sizeof(e))); p += snprintf(p, end - p, " \"email\":\"%s\",", lws_json_purify(e, m.email, sizeof(e))); p += snprintf(p, end - p, " \"ip\":\"%s\",", lws_json_purify(e, m.ip, sizeof(e))); p += snprintf(p, end - p, " \"content\":\"%s\"}", lws_json_purify(e, m.content, sizeof(e))); if (lws_write(wsi, (unsigned char *)start, p - start, LWS_WRITE_TEXT) < 0) return -1; pss->last_idx = m.idx; if (pss->last_idx == vhd->last_idx) break; lws_callback_on_writable(wsi); /* more to do */ } break; case LWS_CALLBACK_HTTP: pss->our_form = 0; /* ie, it's our messageboard new message form */ if (!strcmp((const char *)in, "/msg")) { pss->our_form = 1; break; } goto passthru; case LWS_CALLBACK_HTTP_BODY: if (!pss->our_form) goto passthru; if (len < 2) break; if (!pss->spa) { pss->spa = lws_spa_create(wsi, param_names, ARRAY_SIZE(param_names), MAX_MSG_LEN + 1024, NULL, NULL); if (!pss->spa) return -1; } if (lws_spa_process(pss->spa, in, len)) { lwsl_notice("spa process blew\n"); return -1; } break; case LWS_CALLBACK_HTTP_BODY_COMPLETION: if (!pss->our_form) goto passthru; if (post_message(wsi, vhd, pss)) return -1; p = buffer + LWS_PRE; start = p; end = p + sizeof(buffer) - LWS_PRE; if (lws_add_http_header_status(wsi, 200, &p, end)) return -1; if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE, (unsigned char *)"text/plain", 10, &p, end)) return -1; if (lws_add_http_header_content_length(wsi, 1, &p, end)) return -1; if (lws_finalize_http_header(wsi, &p, end)) return -1; n = lws_write(wsi, start, p - start, LWS_WRITE_HTTP_HEADERS); if (n != (p - start)) { lwsl_err("_write returned %d from %d\n", n, (p - start)); return -1; } s[0] = '0'; n = lws_write(wsi, (unsigned char *)s, 1, LWS_WRITE_HTTP); if (n != 1) return -1; goto try_to_reuse; case LWS_CALLBACK_HTTP_BIND_PROTOCOL: if (!pss || pss->pss_gs) break; pss->pss_gs = malloc(vhd->gsp->per_session_data_size); if (!pss->pss_gs) return -1; memset(pss->pss_gs, 0, vhd->gsp->per_session_data_size); break; case LWS_CALLBACK_HTTP_DROP_PROTOCOL: if (vhd->gsp->callback(wsi, reason, pss ? pss->pss_gs : NULL, in, len)) return -1; if (pss && pss->spa) { lws_spa_destroy(pss->spa); pss->spa = NULL; } if (pss && pss->pss_gs) { free(pss->pss_gs); pss->pss_gs = NULL; } break; default: passthru: return vhd->gsp->callback(wsi, reason, pss ? pss->pss_gs : NULL, in, len); } return 0; try_to_reuse: if (lws_http_transaction_completed(wsi)) return -1; return 0; }
char * lws_generate_client_handshake(struct lws *wsi, char *pkt) { char *p = pkt; const char *meth; 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_role_transition(wsi, 0, LRS_ESTABLISHED, &role_ops_raw_skt); lws_header_table_detach(wsi, 1); return NULL; } /* * 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(wsi->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 defined(LWS_ROLE_WS) if (wsi->do_ws) p = lws_generate_client_ws_handshake(wsi, p); #endif /* give userland a chance to append, eg, cookies */ if (wsi->protocol->callback(wsi, LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER, wsi->user_space, &p, (pkt + wsi->context->pt_serv_buf_size) - p - 12)) return NULL; p += sprintf(p, "\x0d\x0a"); return p; }
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; }