STATIC void write_webrepl(mp_obj_t websock, const void *buf, size_t len) { const mp_stream_p_t *sock_stream = mp_get_stream_raise(websock, MP_STREAM_OP_WRITE | MP_STREAM_OP_IOCTL); int err; int old_opts = sock_stream->ioctl(websock, MP_STREAM_SET_DATA_OPTS, FRAME_BIN, &err); sock_stream->write(websock, buf, len, &err); sock_stream->ioctl(websock, MP_STREAM_SET_DATA_OPTS, old_opts, &err); }
STATIC mp_obj_t stream_readinto(size_t n_args, const mp_obj_t *args) { mp_get_stream_raise(args[0], MP_STREAM_OP_READ); mp_buffer_info_t bufinfo; mp_get_buffer_raise(args[1], &bufinfo, MP_BUFFER_WRITE); // CPython extension: if 2nd arg is provided, that's max len to read, // instead of full buffer. Similar to // https://docs.python.org/3/library/socket.html#socket.socket.recv_into mp_uint_t len = bufinfo.len; if (n_args > 2) { len = mp_obj_get_int(args[2]); if (len > bufinfo.len) { len = bufinfo.len; } } int error; mp_uint_t out_sz = mp_stream_read_exactly(args[0], bufinfo.buf, len, &error); if (error != 0) { if (mp_is_nonblocking_error(error)) { return mp_const_none; } mp_raise_OSError(error); } else { return MP_OBJ_NEW_SMALL_INT(out_sz); } }
STATIC mp_obj_t stream_flush(mp_obj_t self) { const mp_stream_p_t *stream_p = mp_get_stream_raise(self, MP_STREAM_OP_IOCTL); int error; mp_uint_t res = stream_p->ioctl(self, MP_STREAM_FLUSH, 0, &error); if (res == MP_STREAM_ERROR) { mp_raise_OSError(error); } return mp_const_none; }
STATIC mp_uint_t webrepl_write(mp_obj_t self_in, const void *buf, mp_uint_t size, int *errcode) { mp_obj_webrepl_t *self = self_in; if (self->state == STATE_PASSWD) { // Don't forward output until passwd is entered return size; } const mp_stream_p_t *stream_p = mp_get_stream_raise(self->sock, MP_STREAM_OP_WRITE); return stream_p->write(self->sock, buf, size, errcode); }
// Unbuffered, inefficient implementation of readline() for raw I/O files. STATIC mp_obj_t stream_unbuffered_readline(size_t n_args, const mp_obj_t *args) { const mp_stream_p_t *stream_p = mp_get_stream_raise(args[0], MP_STREAM_OP_READ); mp_int_t max_size = -1; if (n_args > 1) { max_size = MP_OBJ_SMALL_INT_VALUE(args[1]); } vstr_t vstr; if (max_size != -1) { vstr_init(&vstr, max_size); } else { vstr_init(&vstr, 16); } while (max_size == -1 || max_size-- != 0) { char *p = vstr_add_len(&vstr, 1); if (p == NULL) { nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_MemoryError, "out of memory")); } int error; mp_uint_t out_sz = stream_p->read(args[0], p, 1, &error); if (out_sz == MP_STREAM_ERROR) { if (mp_is_nonblocking_error(error)) { if (vstr.len == 1) { // We just incremented it, but otherwise we read nothing // and immediately got EAGAIN. This case is not well // specified in // https://docs.python.org/3/library/io.html#io.IOBase.readline // unlike similar case for read(). But we follow the latter's // behavior - return None. vstr_clear(&vstr); return mp_const_none; } else { goto done; } } mp_raise_OSError(error); } if (out_sz == 0) { done: // Back out previously added byte // Consider, what's better - read a char and get OutOfMemory (so read // char is lost), or allocate first as we do. vstr_cut_tail_bytes(&vstr, 1); break; } if (*p == '\n') { break; } } return mp_obj_new_str_from_vstr(STREAM_CONTENT_TYPE(stream_p), &vstr); }
STATIC int get_fd(mp_obj_t fdlike) { if (mp_obj_is_obj(fdlike)) { const mp_stream_p_t *stream_p = mp_get_stream_raise(fdlike, MP_STREAM_OP_IOCTL); int err; mp_uint_t res = stream_p->ioctl(fdlike, MP_STREAM_GET_FILENO, 0, &err); if (res != MP_STREAM_ERROR) { return res; } } return mp_obj_get_int(fdlike); }
STATIC mp_obj_t webrepl_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { mp_arg_check_num(n_args, n_kw, 1, 2, false); mp_get_stream_raise(args[0], MP_STREAM_OP_READ | MP_STREAM_OP_WRITE | MP_STREAM_OP_IOCTL); DEBUG_printf("sizeof(struct webrepl_file) = %lu\n", sizeof(struct webrepl_file)); mp_obj_webrepl_t *o = m_new_obj(mp_obj_webrepl_t); o->base.type = type; o->sock = args[0]; o->hdr_to_recv = sizeof(struct webrepl_file); o->data_to_recv = 0; o->state = STATE_PASSWD; write_webrepl_str(args[0], SSTR(passwd_prompt)); return o; }
STATIC mp_obj_t ppp_make_new(mp_obj_t stream) { mp_get_stream_raise(stream, MP_STREAM_OP_READ | MP_STREAM_OP_WRITE); ppp_if_obj_t *self = m_new_obj_with_finaliser(ppp_if_obj_t); self->base.type = &ppp_if_type; self->stream = stream; self->active = false; self->connected = false; self->clean_close = false; self->client_task_handle = NULL; return MP_OBJ_FROM_PTR(self); }
STATIC int write_file_chunk(mp_obj_webrepl_t *self) { const mp_stream_p_t *file_stream = mp_get_stream_raise(self->cur_file, MP_STREAM_OP_READ | MP_STREAM_OP_WRITE | MP_STREAM_OP_IOCTL); byte readbuf[2 + 256]; int err; mp_uint_t out_sz = file_stream->read(self->cur_file, readbuf + 2, sizeof(readbuf) - 2, &err); if (out_sz == MP_STREAM_ERROR) { return out_sz; } readbuf[0] = out_sz; readbuf[1] = out_sz >> 8; DEBUG_printf("webrepl: Sending %d bytes of file\n", out_sz); write_webrepl(self->sock, readbuf, 2 + out_sz); return out_sz; }
STATIC unsigned char read_src_stream(TINF_DATA *data) { byte *p = (void*)data; p -= offsetof(mp_obj_decompio_t, decomp); mp_obj_decompio_t *self = (mp_obj_decompio_t*)p; const mp_stream_p_t *stream = mp_get_stream_raise(self->src_stream, MP_STREAM_OP_READ); int err; byte c; mp_uint_t out_sz = stream->read(self->src_stream, &c, 1, &err); if (out_sz == MP_STREAM_ERROR) { mp_raise_OSError(err); } if (out_sz == 0) { nlr_raise(mp_obj_new_exception(&mp_type_EOFError)); } return c; }
mp_obj_t mp_stream_write(mp_obj_t self_in, const void *buf, size_t len, byte flags) { mp_get_stream_raise(self_in, MP_STREAM_OP_WRITE); int error; mp_uint_t out_sz = mp_stream_rw(self_in, (void*)buf, len, &error, flags); if (error != 0) { if (mp_is_nonblocking_error(error)) { // http://docs.python.org/3/library/io.html#io.RawIOBase.write // "None is returned if the raw stream is set not to block and // no single byte could be readily written to it." return mp_const_none; } mp_raise_OSError(error); } else { return MP_OBJ_NEW_SMALL_INT(out_sz); } }
STATIC void handle_op(mp_obj_webrepl_t *self) { mp_obj_t open_args[2] = { mp_obj_new_str(self->hdr.fname, strlen(self->hdr.fname), false), MP_OBJ_NEW_QSTR(MP_QSTR_rb) }; if (self->hdr.type == PUT_FILE) { open_args[1] = MP_OBJ_NEW_QSTR(MP_QSTR_wb); } self->cur_file = mp_builtin_open(2, open_args, (mp_map_t*)&mp_const_empty_map); const mp_stream_p_t *file_stream = mp_get_stream_raise(self->cur_file, MP_STREAM_OP_READ | MP_STREAM_OP_WRITE | MP_STREAM_OP_IOCTL); #if 0 struct mp_stream_seek_t seek = { .offset = self->hdr.offset, .whence = 0 }; int err; mp_uint_t res = file_stream->ioctl(self->cur_file, MP_STREAM_SEEK, (uintptr_t)&seek, &err); assert(res != MP_STREAM_ERROR); #endif write_webrepl_resp(self->sock, 0); if (self->hdr.type == PUT_FILE) { self->data_to_recv = self->hdr.size; } else if (self->hdr.type == GET_FILE) { byte readbuf[2 + 256]; int err; // TODO: It's not ideal that we block connection while sending file // and don't process any input. while (1) { mp_uint_t out_sz = file_stream->read(self->cur_file, readbuf + 2, sizeof(readbuf) - 2, &err); assert(out_sz != MP_STREAM_ERROR); readbuf[0] = out_sz; readbuf[1] = out_sz >> 8; DEBUG_printf("webrepl: Sending %d bytes of file\n", out_sz); write_webrepl(self->sock, readbuf, 2 + out_sz); if (out_sz == 0) { break; } } write_webrepl_resp(self->sock, 0); self->hdr_to_recv = sizeof(struct webrepl_file); } }
STATIC mp_obj_t stream_seek(size_t n_args, const mp_obj_t *args) { const mp_stream_p_t *stream_p = mp_get_stream_raise(args[0], MP_STREAM_OP_IOCTL); struct mp_stream_seek_t seek_s; // TODO: Could be uint64 seek_s.offset = mp_obj_get_int(args[1]); seek_s.whence = 0; if (n_args == 3) { seek_s.whence = mp_obj_get_int(args[2]); } int error; mp_uint_t res = stream_p->ioctl(args[0], MP_STREAM_SEEK, (mp_uint_t)(uintptr_t)&seek_s, &error); if (res == MP_STREAM_ERROR) { mp_raise_OSError(error); } // TODO: Could be uint64 return mp_obj_new_int_from_uint(seek_s.offset); }
STATIC mp_obj_t stream_ioctl(size_t n_args, const mp_obj_t *args) { const mp_stream_p_t *stream_p = mp_get_stream_raise(args[0], MP_STREAM_OP_IOCTL); mp_buffer_info_t bufinfo; uintptr_t val = 0; if (n_args > 2) { if (mp_get_buffer(args[2], &bufinfo, MP_BUFFER_WRITE)) { val = (uintptr_t)bufinfo.buf; } else { val = mp_obj_get_int_truncated(args[2]); } } int error; mp_uint_t res = stream_p->ioctl(args[0], mp_obj_get_int(args[1]), val, &error); if (res == MP_STREAM_ERROR) { mp_raise_OSError(error); } return mp_obj_new_int(res); }
STATIC void poll_map_add(mp_map_t *poll_map, const mp_obj_t *obj, mp_uint_t obj_len, mp_uint_t flags, bool or_flags) { for (mp_uint_t i = 0; i < obj_len; i++) { mp_map_elem_t *elem = mp_map_lookup(poll_map, mp_obj_id(obj[i]), MP_MAP_LOOKUP_ADD_IF_NOT_FOUND); if (elem->value == NULL) { // object not found; get its ioctl and add it to the poll list const mp_stream_p_t *stream_p = mp_get_stream_raise(obj[i], MP_STREAM_OP_IOCTL); poll_obj_t *poll_obj = m_new_obj(poll_obj_t); poll_obj->obj = obj[i]; poll_obj->ioctl = stream_p->ioctl; poll_obj->flags = flags; poll_obj->flags_ret = 0; elem->value = poll_obj; } else { // object exists; update its flags if (or_flags) { ((poll_obj_t*)elem->value)->flags |= flags; } else { ((poll_obj_t*)elem->value)->flags = flags; } } } }
STATIC mp_obj_t stream_readall(mp_obj_t self_in) { const mp_stream_p_t *stream_p = mp_get_stream_raise(self_in, MP_STREAM_OP_READ); mp_uint_t total_size = 0; vstr_t vstr; vstr_init(&vstr, DEFAULT_BUFFER_SIZE); char *p = vstr.buf; mp_uint_t current_read = DEFAULT_BUFFER_SIZE; while (true) { int error; mp_uint_t out_sz = stream_p->read(self_in, p, current_read, &error); if (out_sz == MP_STREAM_ERROR) { if (mp_is_nonblocking_error(error)) { // With non-blocking streams, we read as much as we can. // If we read nothing, return None, just like read(). // Otherwise, return data read so far. if (total_size == 0) { return mp_const_none; } break; } mp_raise_OSError(error); } if (out_sz == 0) { break; } total_size += out_sz; if (out_sz < current_read) { current_read -= out_sz; p += out_sz; } else { p = vstr_extend(&vstr, DEFAULT_BUFFER_SIZE); current_read = DEFAULT_BUFFER_SIZE; } } vstr.len = total_size; return mp_obj_new_str_from_vstr(STREAM_CONTENT_TYPE(stream_p), &vstr); }
STATIC void write_webrepl_str(mp_obj_t websock, const char *str, int sz) { int err; const mp_stream_p_t *sock_stream = mp_get_stream_raise(websock, MP_STREAM_OP_WRITE | MP_STREAM_OP_IOCTL); sock_stream->write(websock, str, sz, &err); }
STATIC mp_obj_t mod_ujson_load(mp_obj_t stream_obj) { const mp_stream_p_t *stream_p = mp_get_stream_raise(stream_obj, MP_STREAM_OP_READ); ujson_stream_t s = {stream_obj, stream_p->read, 0, 0}; vstr_t vstr; vstr_init(&vstr, 8); mp_obj_list_t stack; // we use a list as a simple stack for nested JSON stack.len = 0; stack.items = NULL; mp_obj_t stack_top = MP_OBJ_NULL; mp_obj_type_t *stack_top_type = NULL; mp_obj_t stack_key = MP_OBJ_NULL; S_NEXT(s); for (;;) { cont: if (S_END(s)) { break; } mp_obj_t next = MP_OBJ_NULL; bool enter = false; byte cur = S_CUR(s); S_NEXT(s); switch (cur) { case ',': case ':': case ' ': case '\t': case '\n': case '\r': goto cont; case 'n': if (S_CUR(s) == 'u' && S_NEXT(s) == 'l' && S_NEXT(s) == 'l') { S_NEXT(s); next = mp_const_none; } else { goto fail; } break; case 'f': if (S_CUR(s) == 'a' && S_NEXT(s) == 'l' && S_NEXT(s) == 's' && S_NEXT(s) == 'e') { S_NEXT(s); next = mp_const_false; } else { goto fail; } break; case 't': if (S_CUR(s) == 'r' && S_NEXT(s) == 'u' && S_NEXT(s) == 'e') { S_NEXT(s); next = mp_const_true; } else { goto fail; } break; case '"': vstr_reset(&vstr); for (; !S_END(s) && S_CUR(s) != '"';) { byte c = S_CUR(s); if (c == '\\') { c = S_NEXT(s); switch (c) { case 'b': c = 0x08; break; case 'f': c = 0x0c; break; case 'n': c = 0x0a; break; case 'r': c = 0x0d; break; case 't': c = 0x09; break; case 'u': { mp_uint_t num = 0; for (int i = 0; i < 4; i++) { c = (S_NEXT(s) | 0x20) - '0'; if (c > 9) { c -= ('a' - ('9' + 1)); } num = (num << 4) | c; } vstr_add_char(&vstr, num); goto str_cont; } } } vstr_add_byte(&vstr, c); str_cont: S_NEXT(s); } if (S_END(s)) { goto fail; } S_NEXT(s); next = mp_obj_new_str(vstr.buf, vstr.len, false); break; case '-': case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': { bool flt = false; vstr_reset(&vstr); for (;;) { vstr_add_byte(&vstr, cur); cur = S_CUR(s); if (cur == '.' || cur == 'E' || cur == 'e') { flt = true; } else if (cur == '-' || unichar_isdigit(cur)) { // pass } else { break; } S_NEXT(s); } if (flt) { next = mp_parse_num_decimal(vstr.buf, vstr.len, false, false, NULL); } else { next = mp_parse_num_integer(vstr.buf, vstr.len, 10, NULL); } break; } case '[': next = mp_obj_new_list(0, NULL); enter = true; break; case '{': next = mp_obj_new_dict(0); enter = true; break; case '}': case ']': { if (stack_top == MP_OBJ_NULL) { // no object at all goto fail; } if (stack.len == 0) { // finished; compound object goto success; } stack.len -= 1; stack_top = stack.items[stack.len]; stack_top_type = mp_obj_get_type(stack_top); goto cont; } default: goto fail; } if (stack_top == MP_OBJ_NULL) { stack_top = next; stack_top_type = mp_obj_get_type(stack_top); if (!enter) { // finished; single primitive only goto success; } } else { // append to list or dict if (stack_top_type == &mp_type_list) { mp_obj_list_append(stack_top, next); } else { if (stack_key == MP_OBJ_NULL) { stack_key = next; if (enter) { goto fail; } } else { mp_obj_dict_store(stack_top, stack_key, next); stack_key = MP_OBJ_NULL; } } if (enter) { if (stack.items == NULL) { mp_obj_list_init(&stack, 1); stack.items[0] = stack_top; } else { mp_obj_list_append(MP_OBJ_FROM_PTR(&stack), stack_top); } stack_top = next; stack_top_type = mp_obj_get_type(stack_top); } } } success: // eat trailing whitespace while (unichar_isspace(S_CUR(s))) { S_NEXT(s); } if (!S_END(s)) { // unexpected chars goto fail; } if (stack_top == MP_OBJ_NULL || stack.len != 0) { // not exactly 1 object goto fail; } vstr_clear(&vstr); return stack_top; fail: nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, "syntax error in JSON")); }
STATIC mp_uint_t _webrepl_read(mp_obj_t self_in, void *buf, mp_uint_t size, int *errcode) { // We know that os.dupterm always calls with size = 1 assert(size == 1); mp_obj_webrepl_t *self = self_in; const mp_stream_p_t *sock_stream = mp_get_stream_raise(self->sock, MP_STREAM_OP_READ); mp_uint_t out_sz = sock_stream->read(self->sock, buf, size, errcode); //DEBUG_printf("webrepl: Read %d initial bytes from websocket\n", out_sz); if (out_sz == 0 || out_sz == MP_STREAM_ERROR) { return out_sz; } if (self->state == STATE_PASSWD) { char c = *(char*)buf; if (c == '\r' || c == '\n') { self->hdr.fname[self->data_to_recv] = 0; DEBUG_printf("webrepl: entered password: %s\n", self->hdr.fname); if (strcmp(self->hdr.fname, webrepl_passwd) != 0) { write_webrepl_str(self->sock, SSTR(denied_prompt)); return 0; } self->state = STATE_NORMAL; self->data_to_recv = 0; write_webrepl_str(self->sock, SSTR(connected_prompt)); } else if (self->data_to_recv < 10) { self->hdr.fname[self->data_to_recv++] = c; } return -2; } // If last read data belonged to text record (== REPL) int err; if (sock_stream->ioctl(self->sock, MP_STREAM_GET_DATA_OPTS, 0, &err) == 1) { return out_sz; } DEBUG_printf("webrepl: received bin data, hdr_to_recv: %d, data_to_recv=%d\n", self->hdr_to_recv, self->data_to_recv); if (self->hdr_to_recv != 0) { char *p = (char*)&self->hdr + sizeof(self->hdr) - self->hdr_to_recv; *p++ = *(char*)buf; if (--self->hdr_to_recv != 0) { mp_uint_t hdr_sz = sock_stream->read(self->sock, p, self->hdr_to_recv, errcode); if (hdr_sz == MP_STREAM_ERROR) { return hdr_sz; } self->hdr_to_recv -= hdr_sz; if (self->hdr_to_recv != 0) { return -2; } } DEBUG_printf("webrepl: op: %d, file: %s, chunk @%x, sz=%d\n", self->hdr.type, self->hdr.fname, (uint32_t)self->hdr.offset, self->hdr.size); handle_op(self); return -2; } if (self->data_to_recv != 0) { static byte filebuf[512]; filebuf[0] = *(byte*)buf; mp_uint_t buf_sz = 1; if (--self->data_to_recv != 0) { size_t to_read = MIN(sizeof(filebuf) - 1, self->data_to_recv); mp_uint_t sz = sock_stream->read(self->sock, filebuf + 1, to_read, errcode); if (sz == MP_STREAM_ERROR) { return sz; } self->data_to_recv -= sz; buf_sz += sz; } DEBUG_printf("webrepl: Writing %lu bytes to file\n", buf_sz); int err; mp_uint_t res = mp_stream_writeall(self->cur_file, filebuf, buf_sz, &err); if(res == MP_STREAM_ERROR) { assert(0); } if (self->data_to_recv == 0) { close_meth(self->cur_file); self->hdr_to_recv = sizeof(struct webrepl_file); DEBUG_printf("webrepl: Finished writing file\n"); write_webrepl_resp(self->sock, 0); } #ifdef MICROPY_PY_WEBREPL_DELAY // Some platforms may have broken drivers and easily gets // overloaded with modest traffic WebREPL file transfers // generate. The basic workaround is a crude rate control // done in such way. mp_hal_delay_ms(MICROPY_PY_WEBREPL_DELAY); #endif } return -2; }
STATIC mp_obj_ssl_socket_t *socket_new(mp_obj_t sock, struct ssl_args *args) { // Verify the socket object has the full stream protocol mp_get_stream_raise(sock, MP_STREAM_OP_READ | MP_STREAM_OP_WRITE | MP_STREAM_OP_IOCTL); #if MICROPY_PY_USSL_FINALISER mp_obj_ssl_socket_t *o = m_new_obj_with_finaliser(mp_obj_ssl_socket_t); #else mp_obj_ssl_socket_t *o = m_new_obj(mp_obj_ssl_socket_t); #endif o->base.type = &ussl_socket_type; o->sock = sock; int ret; mbedtls_ssl_init(&o->ssl); mbedtls_ssl_config_init(&o->conf); mbedtls_x509_crt_init(&o->cacert); mbedtls_x509_crt_init(&o->cert); mbedtls_pk_init(&o->pkey); mbedtls_ctr_drbg_init(&o->ctr_drbg); #ifdef MBEDTLS_DEBUG_C // Debug level (0-4) mbedtls_debug_set_threshold(0); #endif mbedtls_entropy_init(&o->entropy); const byte seed[] = "upy"; ret = mbedtls_ctr_drbg_seed(&o->ctr_drbg, mbedtls_entropy_func, &o->entropy, seed, sizeof(seed)); if (ret != 0) { goto cleanup; } ret = mbedtls_ssl_config_defaults(&o->conf, args->server_side.u_bool ? MBEDTLS_SSL_IS_SERVER : MBEDTLS_SSL_IS_CLIENT, MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT); if (ret != 0) { goto cleanup; } mbedtls_ssl_conf_authmode(&o->conf, MBEDTLS_SSL_VERIFY_NONE); mbedtls_ssl_conf_rng(&o->conf, mbedtls_ctr_drbg_random, &o->ctr_drbg); #ifdef MBEDTLS_DEBUG_C mbedtls_ssl_conf_dbg(&o->conf, mbedtls_debug, NULL); #endif ret = mbedtls_ssl_setup(&o->ssl, &o->conf); if (ret != 0) { goto cleanup; } if (args->server_hostname.u_obj != mp_const_none) { const char *sni = mp_obj_str_get_str(args->server_hostname.u_obj); ret = mbedtls_ssl_set_hostname(&o->ssl, sni); if (ret != 0) { goto cleanup; } } mbedtls_ssl_set_bio(&o->ssl, &o->sock, _mbedtls_ssl_send, _mbedtls_ssl_recv, NULL); if (args->key.u_obj != MP_OBJ_NULL) { size_t key_len; const byte *key = (const byte*)mp_obj_str_get_data(args->key.u_obj, &key_len); // len should include terminating null ret = mbedtls_pk_parse_key(&o->pkey, key, key_len + 1, NULL, 0); assert(ret == 0); size_t cert_len; const byte *cert = (const byte*)mp_obj_str_get_data(args->cert.u_obj, &cert_len); // len should include terminating null ret = mbedtls_x509_crt_parse(&o->cert, cert, cert_len + 1); assert(ret == 0); ret = mbedtls_ssl_conf_own_cert(&o->conf, &o->cert, &o->pkey); assert(ret == 0); } if (args->do_handshake.u_bool) { while ((ret = mbedtls_ssl_handshake(&o->ssl)) != 0) { if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) { printf("mbedtls_ssl_handshake error: -%x\n", -ret); goto cleanup; } } } return o; cleanup: mbedtls_pk_free(&o->pkey); mbedtls_x509_crt_free(&o->cert); mbedtls_x509_crt_free(&o->cacert); mbedtls_ssl_free(&o->ssl); mbedtls_ssl_config_free(&o->conf); mbedtls_ctr_drbg_free(&o->ctr_drbg); mbedtls_entropy_free(&o->entropy); if (ret == MBEDTLS_ERR_SSL_ALLOC_FAILED) { mp_raise_OSError(MP_ENOMEM); } else { mp_raise_OSError(MP_EIO); } }
STATIC mp_uint_t websocket_read(mp_obj_t self_in, void *buf, mp_uint_t size, int *errcode) { mp_obj_websocket_t *self = self_in; const mp_stream_p_t *stream_p = mp_get_stream_raise(self->sock, MP_STREAM_OP_READ); while (1) { if (self->to_recv != 0) { mp_uint_t out_sz = stream_p->read(self->sock, self->buf + self->buf_pos, self->to_recv, errcode); if (out_sz == 0 || out_sz == MP_STREAM_ERROR) { return out_sz; } self->buf_pos += out_sz; self->to_recv -= out_sz; if (self->to_recv != 0) { *errcode = EAGAIN; return MP_STREAM_ERROR; } } switch (self->state) { case FRAME_HEADER: { // TODO: Split frame handling below is untested so far, so conservatively disable it assert(self->buf[0] & 0x80); // "Control frames MAY be injected in the middle of a fragmented message." // So, they must be processed before data frames (and not alter // self->ws_flags) byte frame_type = self->buf[0]; self->last_flags = frame_type; frame_type &= FRAME_OPCODE_MASK; if ((self->buf[0] & FRAME_OPCODE_MASK) == FRAME_CONT) { // Preserve previous frame type self->ws_flags = (self->ws_flags & FRAME_OPCODE_MASK) | (self->buf[0] & ~FRAME_OPCODE_MASK); } else { self->ws_flags = self->buf[0]; } // Reset mask in case someone will use "simplified" protocol // without masks. memset(self->mask, 0, sizeof(self->mask)); int to_recv = 0; size_t sz = self->buf[1] & 0x7f; if (sz == 126) { // Msg size is next 2 bytes to_recv += 2; } else if (sz == 127) { // Msg size is next 8 bytes assert(0); } if (self->buf[1] & 0x80) { // Next 4 bytes is mask to_recv += 4; } self->buf_pos = 0; self->to_recv = to_recv; self->msg_sz = sz; // May be overriden by FRAME_OPT if (to_recv != 0) { self->state = FRAME_OPT; } else { if (frame_type >= FRAME_CLOSE) { self->state = CONTROL; } else { self->state = PAYLOAD; } } continue; } case FRAME_OPT: { if ((self->buf_pos & 3) == 2) { // First two bytes are message length self->msg_sz = (self->buf[0] << 8) | self->buf[1]; } if (self->buf_pos >= 4) { // Last 4 bytes is mask memcpy(self->mask, self->buf + self->buf_pos - 4, 4); } self->buf_pos = 0; if ((self->last_flags & FRAME_OPCODE_MASK) >= FRAME_CLOSE) { self->state = CONTROL; } else { self->state = PAYLOAD; } continue; } case PAYLOAD: case CONTROL: { mp_uint_t out_sz = 0; if (self->msg_sz == 0) { // In case message had zero payload goto no_payload; } size_t sz = MIN(size, self->msg_sz); out_sz = stream_p->read(self->sock, buf, sz, errcode); if (out_sz == 0 || out_sz == MP_STREAM_ERROR) { return out_sz; } sz = out_sz; for (byte *p = buf; sz--; p++) { *p ^= self->mask[self->mask_pos++ & 3]; } self->msg_sz -= out_sz; if (self->msg_sz == 0) { byte last_state; no_payload: last_state = self->state; self->state = FRAME_HEADER; self->to_recv = 2; self->mask_pos = 0; self->buf_pos = 0; // Handle control frame if (last_state == CONTROL) { byte frame_type = self->last_flags & FRAME_OPCODE_MASK; if (frame_type == FRAME_CLOSE) { static char close_resp[2] = {0x88, 0}; int err; websocket_write(self_in, close_resp, sizeof(close_resp), &err); return 0; } //DEBUG_printf("Finished receiving ctrl message %x, ignoring\n", self->last_flags); continue; } } if (out_sz != 0) { return out_sz; } // Empty (data) frame received is not EOF continue; } } } }
STATIC mp_obj_t stream_read_generic(size_t n_args, const mp_obj_t *args, byte flags) { const mp_stream_p_t *stream_p = mp_get_stream_raise(args[0], MP_STREAM_OP_READ); // What to do if sz < -1? Python docs don't specify this case. // CPython does a readall, but here we silently let negatives through, // and they will cause a MemoryError. mp_int_t sz; if (n_args == 1 || ((sz = mp_obj_get_int(args[1])) == -1)) { return stream_readall(args[0]); } #if MICROPY_PY_BUILTINS_STR_UNICODE if (stream_p->is_text) { // We need to read sz number of unicode characters. Because we don't have any // buffering, and because the stream API can only read bytes, we must read here // in units of bytes and must never over read. If we want sz chars, then reading // sz bytes will never over-read, so we follow this approach, in a loop to keep // reading until we have exactly enough chars. This will be 1 read for text // with ASCII-only chars, and about 2 reads for text with a couple of non-ASCII // chars. For text with lots of non-ASCII chars, it'll be pretty inefficient // in time and memory. vstr_t vstr; vstr_init(&vstr, sz); mp_uint_t more_bytes = sz; mp_uint_t last_buf_offset = 0; while (more_bytes > 0) { char *p = vstr_add_len(&vstr, more_bytes); if (p == NULL) { nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_MemoryError, "out of memory")); } int error; mp_uint_t out_sz = mp_stream_read_exactly(args[0], p, more_bytes, &error); if (error != 0) { vstr_cut_tail_bytes(&vstr, more_bytes); if (mp_is_nonblocking_error(error)) { // With non-blocking streams, we read as much as we can. // If we read nothing, return None, just like read(). // Otherwise, return data read so far. // TODO what if we have read only half a non-ASCII char? if (vstr.len == 0) { vstr_clear(&vstr); return mp_const_none; } break; } mp_raise_OSError(error); } if (out_sz < more_bytes) { // Finish reading. // TODO what if we have read only half a non-ASCII char? vstr_cut_tail_bytes(&vstr, more_bytes - out_sz); if (out_sz == 0) { break; } } // count chars from bytes just read for (mp_uint_t off = last_buf_offset;;) { byte b = vstr.buf[off]; int n; if (!UTF8_IS_NONASCII(b)) { // 1-byte ASCII char n = 1; } else if ((b & 0xe0) == 0xc0) { // 2-byte char n = 2; } else if ((b & 0xf0) == 0xe0) { // 3-byte char n = 3; } else if ((b & 0xf8) == 0xf0) { // 4-byte char n = 4; } else { // TODO n = 5; } if (off + n <= vstr.len) { // got a whole char in n bytes off += n; sz -= 1; last_buf_offset = off; if (off >= vstr.len) { more_bytes = sz; break; } } else { // didn't get a whole char, so work out how many extra bytes are needed for // this partial char, plus bytes for additional chars that we want more_bytes = (off + n - vstr.len) + (sz - 1); break; } } } return mp_obj_new_str_from_vstr(&mp_type_str, &vstr); } #endif vstr_t vstr; vstr_init_len(&vstr, sz); int error; mp_uint_t out_sz = mp_stream_rw(args[0], vstr.buf, sz, &error, flags); if (error != 0) { vstr_clear(&vstr); if (mp_is_nonblocking_error(error)) { // https://docs.python.org/3.4/library/io.html#io.RawIOBase.read // "If the object is in non-blocking mode and no bytes are available, // None is returned." // This is actually very weird, as naive truth check will treat // this as EOF. return mp_const_none; } mp_raise_OSError(error); } else { vstr.len = out_sz; return mp_obj_new_str_from_vstr(STREAM_CONTENT_TYPE(stream_p), &vstr); } }