Device management with online dashboard See https://freshen.cc for full description
freshen.h@0:b21e585b3aa1, 2018-04-10 (annotated)
- Committer:
- lsm
- Date:
- Tue Apr 10 06:50:09 2018 +0000
- Revision:
- 0:b21e585b3aa1
Version 1.3
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
lsm | 0:b21e585b3aa1 | 1 | /* |
lsm | 0:b21e585b3aa1 | 2 | * Copyright (c) 2014-2018 Cesanta Software Limited |
lsm | 0:b21e585b3aa1 | 3 | * All rights reserved |
lsm | 0:b21e585b3aa1 | 4 | * |
lsm | 0:b21e585b3aa1 | 5 | * Licensed to the Apache Software Foundation (ASF) under one |
lsm | 0:b21e585b3aa1 | 6 | * or more contributor license agreements. See the NOTICE file |
lsm | 0:b21e585b3aa1 | 7 | * distributed with this work for additional information |
lsm | 0:b21e585b3aa1 | 8 | * regarding copyright ownership. The ASF licenses this file |
lsm | 0:b21e585b3aa1 | 9 | * to you under the Apache License, Version 2.0 (the |
lsm | 0:b21e585b3aa1 | 10 | * "License"); you may not use this file except in compliance |
lsm | 0:b21e585b3aa1 | 11 | * with the License. You may obtain a copy of the License at |
lsm | 0:b21e585b3aa1 | 12 | * |
lsm | 0:b21e585b3aa1 | 13 | * http://www.apache.org/licenses/LICENSE-2.0 |
lsm | 0:b21e585b3aa1 | 14 | * |
lsm | 0:b21e585b3aa1 | 15 | * Unless required by applicable law or agreed to in writing, |
lsm | 0:b21e585b3aa1 | 16 | * software distributed under the License is distributed on an |
lsm | 0:b21e585b3aa1 | 17 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
lsm | 0:b21e585b3aa1 | 18 | * KIND, either express or implied. See the License for the |
lsm | 0:b21e585b3aa1 | 19 | * specific language governing permissions and limitations |
lsm | 0:b21e585b3aa1 | 20 | * under the License. |
lsm | 0:b21e585b3aa1 | 21 | */ |
lsm | 0:b21e585b3aa1 | 22 | |
lsm | 0:b21e585b3aa1 | 23 | #define FRESHEN_VERSION "1.3" |
lsm | 0:b21e585b3aa1 | 24 | |
lsm | 0:b21e585b3aa1 | 25 | #ifndef FRESHEN_H |
lsm | 0:b21e585b3aa1 | 26 | #define FRESHEN_H |
lsm | 0:b21e585b3aa1 | 27 | |
lsm | 0:b21e585b3aa1 | 28 | #ifdef __cplusplus |
lsm | 0:b21e585b3aa1 | 29 | extern "C" { |
lsm | 0:b21e585b3aa1 | 30 | #endif /* __cplusplus */ |
lsm | 0:b21e585b3aa1 | 31 | |
lsm | 0:b21e585b3aa1 | 32 | #include <stddef.h> |
lsm | 0:b21e585b3aa1 | 33 | |
lsm | 0:b21e585b3aa1 | 34 | #define FRESHEN_API extern |
lsm | 0:b21e585b3aa1 | 35 | |
lsm | 0:b21e585b3aa1 | 36 | /* Common JSON-RPC error codes */ |
lsm | 0:b21e585b3aa1 | 37 | #define FRESHEN_ERROR_INVALID -32700 /* Invalid JSON was received */ |
lsm | 0:b21e585b3aa1 | 38 | #define FRESHEN_ERROR_NOT_FOUND -32601 /* The method does not exist */ |
lsm | 0:b21e585b3aa1 | 39 | #define FRESHEN_ERROR_INTERNAL -32603 /* Internal JSON-RPC error */ |
lsm | 0:b21e585b3aa1 | 40 | |
lsm | 0:b21e585b3aa1 | 41 | #if __STDC_VERSION__ >= 201112L |
lsm | 0:b21e585b3aa1 | 42 | #include <stdatomic.h> |
lsm | 0:b21e585b3aa1 | 43 | typedef atomic_flag freshen_spinlock_t; |
lsm | 0:b21e585b3aa1 | 44 | #define FRESHEN_SPINLOCK_INIT ATOMIC_FLAG_INIT |
lsm | 0:b21e585b3aa1 | 45 | static void freshen_spinlock_lock(freshen_spinlock_t *lock) { |
lsm | 0:b21e585b3aa1 | 46 | while (atomic_flag_test_and_set(lock)) |
lsm | 0:b21e585b3aa1 | 47 | ; |
lsm | 0:b21e585b3aa1 | 48 | } |
lsm | 0:b21e585b3aa1 | 49 | static void freshen_spinlock_unlock(freshen_spinlock_t *lock) { |
lsm | 0:b21e585b3aa1 | 50 | atomic_flag_clear(lock); |
lsm | 0:b21e585b3aa1 | 51 | } |
lsm | 0:b21e585b3aa1 | 52 | #else |
lsm | 0:b21e585b3aa1 | 53 | typedef int freshen_spinlock_t; |
lsm | 0:b21e585b3aa1 | 54 | #define FRESHEN_SPINLOCK_INIT 0 |
lsm | 0:b21e585b3aa1 | 55 | static void freshen_spinlock_lock(freshen_spinlock_t *lock) { |
lsm | 0:b21e585b3aa1 | 56 | } |
lsm | 0:b21e585b3aa1 | 57 | static void freshen_spinlock_unlock(freshen_spinlock_t *lock) { |
lsm | 0:b21e585b3aa1 | 58 | } |
lsm | 0:b21e585b3aa1 | 59 | #endif /* __STDC_VERSION__ */ |
lsm | 0:b21e585b3aa1 | 60 | |
lsm | 0:b21e585b3aa1 | 61 | /* Append-only buffer of limited capacity */ |
lsm | 0:b21e585b3aa1 | 62 | struct freshen_buf { |
lsm | 0:b21e585b3aa1 | 63 | void *ptr; |
lsm | 0:b21e585b3aa1 | 64 | size_t len; |
lsm | 0:b21e585b3aa1 | 65 | size_t cap; |
lsm | 0:b21e585b3aa1 | 66 | int overflow; |
lsm | 0:b21e585b3aa1 | 67 | }; |
lsm | 0:b21e585b3aa1 | 68 | |
lsm | 0:b21e585b3aa1 | 69 | struct freshen_queue { |
lsm | 0:b21e585b3aa1 | 70 | void *buf; |
lsm | 0:b21e585b3aa1 | 71 | size_t buf_sz; |
lsm | 0:b21e585b3aa1 | 72 | size_t tail; |
lsm | 0:b21e585b3aa1 | 73 | freshen_spinlock_t lock; |
lsm | 0:b21e585b3aa1 | 74 | }; |
lsm | 0:b21e585b3aa1 | 75 | |
lsm | 0:b21e585b3aa1 | 76 | struct freshen_method { |
lsm | 0:b21e585b3aa1 | 77 | const char *method; |
lsm | 0:b21e585b3aa1 | 78 | size_t method_sz; |
lsm | 0:b21e585b3aa1 | 79 | int (*func)(const char *, char *, size_t, void *); |
lsm | 0:b21e585b3aa1 | 80 | void *userdata; |
lsm | 0:b21e585b3aa1 | 81 | struct freshen_method *next; |
lsm | 0:b21e585b3aa1 | 82 | }; |
lsm | 0:b21e585b3aa1 | 83 | |
lsm | 0:b21e585b3aa1 | 84 | /* |
lsm | 0:b21e585b3aa1 | 85 | * Main Freshen context, stores current request information and a list of |
lsm | 0:b21e585b3aa1 | 86 | * exported RPC methods. |
lsm | 0:b21e585b3aa1 | 87 | */ |
lsm | 0:b21e585b3aa1 | 88 | struct freshen_ctx { |
lsm | 0:b21e585b3aa1 | 89 | struct freshen_queue queue; |
lsm | 0:b21e585b3aa1 | 90 | struct freshen_method *methods; |
lsm | 0:b21e585b3aa1 | 91 | void *privdata; |
lsm | 0:b21e585b3aa1 | 92 | }; |
lsm | 0:b21e585b3aa1 | 93 | |
lsm | 0:b21e585b3aa1 | 94 | #define FRESHEN_CTX_INTIALIZER \ |
lsm | 0:b21e585b3aa1 | 95 | { {NULL, 0, 0, FRESHEN_SPINLOCK_INIT}, NULL, NULL } |
lsm | 0:b21e585b3aa1 | 96 | |
lsm | 0:b21e585b3aa1 | 97 | FRESHEN_API struct freshen_ctx freshen_default_context; |
lsm | 0:b21e585b3aa1 | 98 | |
lsm | 0:b21e585b3aa1 | 99 | /* Registers function fn under the given name within the given RPC context */ |
lsm | 0:b21e585b3aa1 | 100 | #define freshen_core_export(freshen, name, fn, ud) \ |
lsm | 0:b21e585b3aa1 | 101 | do { \ |
lsm | 0:b21e585b3aa1 | 102 | static struct freshen_method m = {(name), sizeof(name) - 1, (fn), NULL, \ |
lsm | 0:b21e585b3aa1 | 103 | NULL}; \ |
lsm | 0:b21e585b3aa1 | 104 | m.userdata = (ud); \ |
lsm | 0:b21e585b3aa1 | 105 | m.next = (freshen)->methods; \ |
lsm | 0:b21e585b3aa1 | 106 | (freshen)->methods = &m; \ |
lsm | 0:b21e585b3aa1 | 107 | } while (0) |
lsm | 0:b21e585b3aa1 | 108 | |
lsm | 0:b21e585b3aa1 | 109 | FRESHEN_API int freshen_core_publish(struct freshen_ctx *freshen, |
lsm | 0:b21e585b3aa1 | 110 | const char *event, const char *data); |
lsm | 0:b21e585b3aa1 | 111 | |
lsm | 0:b21e585b3aa1 | 112 | FRESHEN_API int freshen_core_process(struct freshen_ctx *freshen, void *input, |
lsm | 0:b21e585b3aa1 | 113 | size_t input_sz, void *output, |
lsm | 0:b21e585b3aa1 | 114 | size_t output_sz); |
lsm | 0:b21e585b3aa1 | 115 | |
lsm | 0:b21e585b3aa1 | 116 | #if !defined(FRESHEN_DECLARATIONS_ONLY) |
lsm | 0:b21e585b3aa1 | 117 | #include <stdio.h> |
lsm | 0:b21e585b3aa1 | 118 | #include <stdlib.h> |
lsm | 0:b21e585b3aa1 | 119 | #include <string.h> |
lsm | 0:b21e585b3aa1 | 120 | |
lsm | 0:b21e585b3aa1 | 121 | #if defined(ESP_PLATFORM) |
lsm | 0:b21e585b3aa1 | 122 | #include "esp_log.h" |
lsm | 0:b21e585b3aa1 | 123 | #define FLOGI(...) ESP_LOGI("freshen", __VA_ARGS__) |
lsm | 0:b21e585b3aa1 | 124 | #else |
lsm | 0:b21e585b3aa1 | 125 | #define FLOGI(...) \ |
lsm | 0:b21e585b3aa1 | 126 | do { \ |
lsm | 0:b21e585b3aa1 | 127 | printf(__VA_ARGS__); \ |
lsm | 0:b21e585b3aa1 | 128 | putchar('\n'); \ |
lsm | 0:b21e585b3aa1 | 129 | } while (0) |
lsm | 0:b21e585b3aa1 | 130 | #endif |
lsm | 0:b21e585b3aa1 | 131 | #define FLOGE FLOGI |
lsm | 0:b21e585b3aa1 | 132 | |
lsm | 0:b21e585b3aa1 | 133 | static int freshen_json(const char *s, size_t sz, const char *key, size_t keysz, |
lsm | 0:b21e585b3aa1 | 134 | const char **value, size_t *valuesz) { |
lsm | 0:b21e585b3aa1 | 135 | enum { |
lsm | 0:b21e585b3aa1 | 136 | JSON_STATE_VALUE, |
lsm | 0:b21e585b3aa1 | 137 | JSON_STATE_LITERAL, |
lsm | 0:b21e585b3aa1 | 138 | JSON_STATE_STRING, |
lsm | 0:b21e585b3aa1 | 139 | JSON_STATE_ESCAPE, |
lsm | 0:b21e585b3aa1 | 140 | JSON_STATE_UTF8 |
lsm | 0:b21e585b3aa1 | 141 | } state = JSON_STATE_VALUE; |
lsm | 0:b21e585b3aa1 | 142 | const char *k = NULL; |
lsm | 0:b21e585b3aa1 | 143 | int index = 1; |
lsm | 0:b21e585b3aa1 | 144 | int depth = 0; |
lsm | 0:b21e585b3aa1 | 145 | int utf8_bytes = 0; |
lsm | 0:b21e585b3aa1 | 146 | |
lsm | 0:b21e585b3aa1 | 147 | if (key == NULL) { |
lsm | 0:b21e585b3aa1 | 148 | index = keysz; |
lsm | 0:b21e585b3aa1 | 149 | keysz = 0; |
lsm | 0:b21e585b3aa1 | 150 | } |
lsm | 0:b21e585b3aa1 | 151 | |
lsm | 0:b21e585b3aa1 | 152 | *value = NULL; |
lsm | 0:b21e585b3aa1 | 153 | *valuesz = 0; |
lsm | 0:b21e585b3aa1 | 154 | |
lsm | 0:b21e585b3aa1 | 155 | for (; sz > 0; s++, sz--) { |
lsm | 0:b21e585b3aa1 | 156 | enum { |
lsm | 0:b21e585b3aa1 | 157 | JSON_ACTION_NONE, |
lsm | 0:b21e585b3aa1 | 158 | JSON_ACTION_START, |
lsm | 0:b21e585b3aa1 | 159 | JSON_ACTION_END, |
lsm | 0:b21e585b3aa1 | 160 | JSON_ACTION_START_STRUCT, |
lsm | 0:b21e585b3aa1 | 161 | JSON_ACTION_END_STRUCT |
lsm | 0:b21e585b3aa1 | 162 | } action = JSON_ACTION_NONE; |
lsm | 0:b21e585b3aa1 | 163 | unsigned char c = *s; |
lsm | 0:b21e585b3aa1 | 164 | switch (state) { |
lsm | 0:b21e585b3aa1 | 165 | case JSON_STATE_VALUE: |
lsm | 0:b21e585b3aa1 | 166 | if (c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == ',' || |
lsm | 0:b21e585b3aa1 | 167 | c == ':') { |
lsm | 0:b21e585b3aa1 | 168 | continue; |
lsm | 0:b21e585b3aa1 | 169 | } else if (c == '"') { |
lsm | 0:b21e585b3aa1 | 170 | action = JSON_ACTION_START; |
lsm | 0:b21e585b3aa1 | 171 | state = JSON_STATE_STRING; |
lsm | 0:b21e585b3aa1 | 172 | } else if (c == '{' || c == '[') { |
lsm | 0:b21e585b3aa1 | 173 | action = JSON_ACTION_START_STRUCT; |
lsm | 0:b21e585b3aa1 | 174 | } else if (c == '}' || c == ']') { |
lsm | 0:b21e585b3aa1 | 175 | action = JSON_ACTION_END_STRUCT; |
lsm | 0:b21e585b3aa1 | 176 | } else if (c == 't' || c == 'f' || c == 'n' || c == '-' || |
lsm | 0:b21e585b3aa1 | 177 | (c >= '0' && c <= '9')) { |
lsm | 0:b21e585b3aa1 | 178 | action = JSON_ACTION_START; |
lsm | 0:b21e585b3aa1 | 179 | state = JSON_STATE_LITERAL; |
lsm | 0:b21e585b3aa1 | 180 | } else { |
lsm | 0:b21e585b3aa1 | 181 | return -1; |
lsm | 0:b21e585b3aa1 | 182 | } |
lsm | 0:b21e585b3aa1 | 183 | break; |
lsm | 0:b21e585b3aa1 | 184 | case JSON_STATE_LITERAL: |
lsm | 0:b21e585b3aa1 | 185 | if (c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == ',' || |
lsm | 0:b21e585b3aa1 | 186 | c == ']' || c == '}' || c == ':') { |
lsm | 0:b21e585b3aa1 | 187 | state = JSON_STATE_VALUE; |
lsm | 0:b21e585b3aa1 | 188 | s--; |
lsm | 0:b21e585b3aa1 | 189 | sz++; |
lsm | 0:b21e585b3aa1 | 190 | action = JSON_ACTION_END; |
lsm | 0:b21e585b3aa1 | 191 | } else if (c < 32 || c > 126) { |
lsm | 0:b21e585b3aa1 | 192 | return -1; |
lsm | 0:b21e585b3aa1 | 193 | } |
lsm | 0:b21e585b3aa1 | 194 | case JSON_STATE_STRING: |
lsm | 0:b21e585b3aa1 | 195 | if (c < 32 || (c > 126 && c < 192)) { |
lsm | 0:b21e585b3aa1 | 196 | return -1; |
lsm | 0:b21e585b3aa1 | 197 | } else if (c == '"') { |
lsm | 0:b21e585b3aa1 | 198 | action = JSON_ACTION_END; |
lsm | 0:b21e585b3aa1 | 199 | state = JSON_STATE_VALUE; |
lsm | 0:b21e585b3aa1 | 200 | } else if (c == '\\') { |
lsm | 0:b21e585b3aa1 | 201 | state = JSON_STATE_ESCAPE; |
lsm | 0:b21e585b3aa1 | 202 | } else if (c >= 192 && c < 224) { |
lsm | 0:b21e585b3aa1 | 203 | utf8_bytes = 1; |
lsm | 0:b21e585b3aa1 | 204 | state = JSON_STATE_UTF8; |
lsm | 0:b21e585b3aa1 | 205 | } else if (c >= 224 && c < 240) { |
lsm | 0:b21e585b3aa1 | 206 | utf8_bytes = 2; |
lsm | 0:b21e585b3aa1 | 207 | state = JSON_STATE_UTF8; |
lsm | 0:b21e585b3aa1 | 208 | } else if (c >= 240 && c < 247) { |
lsm | 0:b21e585b3aa1 | 209 | utf8_bytes = 3; |
lsm | 0:b21e585b3aa1 | 210 | state = JSON_STATE_UTF8; |
lsm | 0:b21e585b3aa1 | 211 | } else if (c >= 128 && c < 192) { |
lsm | 0:b21e585b3aa1 | 212 | return -1; |
lsm | 0:b21e585b3aa1 | 213 | } |
lsm | 0:b21e585b3aa1 | 214 | break; |
lsm | 0:b21e585b3aa1 | 215 | case JSON_STATE_ESCAPE: |
lsm | 0:b21e585b3aa1 | 216 | if (c == '"' || c == '\\' || c == '/' || c == 'b' || c == 'f' || |
lsm | 0:b21e585b3aa1 | 217 | c == 'n' || c == 'r' || c == 't' || c == 'u') { |
lsm | 0:b21e585b3aa1 | 218 | state = JSON_STATE_STRING; |
lsm | 0:b21e585b3aa1 | 219 | } else { |
lsm | 0:b21e585b3aa1 | 220 | return -1; |
lsm | 0:b21e585b3aa1 | 221 | } |
lsm | 0:b21e585b3aa1 | 222 | break; |
lsm | 0:b21e585b3aa1 | 223 | case JSON_STATE_UTF8: |
lsm | 0:b21e585b3aa1 | 224 | if (c < 128 || c > 191) { |
lsm | 0:b21e585b3aa1 | 225 | return -1; |
lsm | 0:b21e585b3aa1 | 226 | } |
lsm | 0:b21e585b3aa1 | 227 | utf8_bytes--; |
lsm | 0:b21e585b3aa1 | 228 | if (utf8_bytes == 0) { |
lsm | 0:b21e585b3aa1 | 229 | state = JSON_STATE_STRING; |
lsm | 0:b21e585b3aa1 | 230 | } |
lsm | 0:b21e585b3aa1 | 231 | break; |
lsm | 0:b21e585b3aa1 | 232 | default: |
lsm | 0:b21e585b3aa1 | 233 | return -1; |
lsm | 0:b21e585b3aa1 | 234 | } |
lsm | 0:b21e585b3aa1 | 235 | |
lsm | 0:b21e585b3aa1 | 236 | if (action == JSON_ACTION_END_STRUCT) { |
lsm | 0:b21e585b3aa1 | 237 | depth--; |
lsm | 0:b21e585b3aa1 | 238 | } |
lsm | 0:b21e585b3aa1 | 239 | |
lsm | 0:b21e585b3aa1 | 240 | if (depth == 1) { |
lsm | 0:b21e585b3aa1 | 241 | if (action == JSON_ACTION_START || action == JSON_ACTION_START_STRUCT) { |
lsm | 0:b21e585b3aa1 | 242 | if (index == 0) { |
lsm | 0:b21e585b3aa1 | 243 | *value = s; |
lsm | 0:b21e585b3aa1 | 244 | } else if (keysz > 0 && index == 1) { |
lsm | 0:b21e585b3aa1 | 245 | k = s; |
lsm | 0:b21e585b3aa1 | 246 | } else { |
lsm | 0:b21e585b3aa1 | 247 | index--; |
lsm | 0:b21e585b3aa1 | 248 | } |
lsm | 0:b21e585b3aa1 | 249 | } else if (action == JSON_ACTION_END || |
lsm | 0:b21e585b3aa1 | 250 | action == JSON_ACTION_END_STRUCT) { |
lsm | 0:b21e585b3aa1 | 251 | if (*value != NULL && index == 0) { |
lsm | 0:b21e585b3aa1 | 252 | *valuesz = (size_t)(s + 1 - *value); |
lsm | 0:b21e585b3aa1 | 253 | return 0; |
lsm | 0:b21e585b3aa1 | 254 | } else if (keysz > 0 && k != NULL) { |
lsm | 0:b21e585b3aa1 | 255 | if (keysz == (size_t)(s - k - 1) && memcmp(key, k + 1, keysz) == 0) { |
lsm | 0:b21e585b3aa1 | 256 | index = 0; |
lsm | 0:b21e585b3aa1 | 257 | } else { |
lsm | 0:b21e585b3aa1 | 258 | index = 2; |
lsm | 0:b21e585b3aa1 | 259 | } |
lsm | 0:b21e585b3aa1 | 260 | k = NULL; |
lsm | 0:b21e585b3aa1 | 261 | } |
lsm | 0:b21e585b3aa1 | 262 | } |
lsm | 0:b21e585b3aa1 | 263 | } |
lsm | 0:b21e585b3aa1 | 264 | |
lsm | 0:b21e585b3aa1 | 265 | if (action == JSON_ACTION_START_STRUCT) { |
lsm | 0:b21e585b3aa1 | 266 | depth++; |
lsm | 0:b21e585b3aa1 | 267 | } |
lsm | 0:b21e585b3aa1 | 268 | } |
lsm | 0:b21e585b3aa1 | 269 | return -1; |
lsm | 0:b21e585b3aa1 | 270 | } |
lsm | 0:b21e585b3aa1 | 271 | |
lsm | 0:b21e585b3aa1 | 272 | static int freshen_json_unescape(const char *s, size_t n, char *out) { |
lsm | 0:b21e585b3aa1 | 273 | int r = 0; |
lsm | 0:b21e585b3aa1 | 274 | if (*s++ != '"') { |
lsm | 0:b21e585b3aa1 | 275 | return FRESHEN_ERROR_INVALID; |
lsm | 0:b21e585b3aa1 | 276 | } |
lsm | 0:b21e585b3aa1 | 277 | while (n > 2) { |
lsm | 0:b21e585b3aa1 | 278 | char c = *s; |
lsm | 0:b21e585b3aa1 | 279 | if (c == '\\') { |
lsm | 0:b21e585b3aa1 | 280 | s++; |
lsm | 0:b21e585b3aa1 | 281 | n--; |
lsm | 0:b21e585b3aa1 | 282 | switch (*s) { |
lsm | 0:b21e585b3aa1 | 283 | case 'b': |
lsm | 0:b21e585b3aa1 | 284 | c = '\b'; |
lsm | 0:b21e585b3aa1 | 285 | break; |
lsm | 0:b21e585b3aa1 | 286 | case 'f': |
lsm | 0:b21e585b3aa1 | 287 | c = '\f'; |
lsm | 0:b21e585b3aa1 | 288 | break; |
lsm | 0:b21e585b3aa1 | 289 | case 'n': |
lsm | 0:b21e585b3aa1 | 290 | c = '\n'; |
lsm | 0:b21e585b3aa1 | 291 | break; |
lsm | 0:b21e585b3aa1 | 292 | case 'r': |
lsm | 0:b21e585b3aa1 | 293 | c = '\r'; |
lsm | 0:b21e585b3aa1 | 294 | break; |
lsm | 0:b21e585b3aa1 | 295 | case 't': |
lsm | 0:b21e585b3aa1 | 296 | c = '\t'; |
lsm | 0:b21e585b3aa1 | 297 | break; |
lsm | 0:b21e585b3aa1 | 298 | case '\\': |
lsm | 0:b21e585b3aa1 | 299 | c = '\\'; |
lsm | 0:b21e585b3aa1 | 300 | break; |
lsm | 0:b21e585b3aa1 | 301 | case '/': |
lsm | 0:b21e585b3aa1 | 302 | c = '/'; |
lsm | 0:b21e585b3aa1 | 303 | break; |
lsm | 0:b21e585b3aa1 | 304 | case '\"': |
lsm | 0:b21e585b3aa1 | 305 | c = '\"'; |
lsm | 0:b21e585b3aa1 | 306 | break; |
lsm | 0:b21e585b3aa1 | 307 | default: /* XXX: we don't support unicode \uXXXX yet */ |
lsm | 0:b21e585b3aa1 | 308 | return FRESHEN_ERROR_INVALID; |
lsm | 0:b21e585b3aa1 | 309 | } |
lsm | 0:b21e585b3aa1 | 310 | } |
lsm | 0:b21e585b3aa1 | 311 | if (out != NULL) { |
lsm | 0:b21e585b3aa1 | 312 | *out++ = c; |
lsm | 0:b21e585b3aa1 | 313 | } |
lsm | 0:b21e585b3aa1 | 314 | s++; |
lsm | 0:b21e585b3aa1 | 315 | n--; |
lsm | 0:b21e585b3aa1 | 316 | r++; |
lsm | 0:b21e585b3aa1 | 317 | } |
lsm | 0:b21e585b3aa1 | 318 | if (*s != '"') { |
lsm | 0:b21e585b3aa1 | 319 | return FRESHEN_ERROR_INVALID; |
lsm | 0:b21e585b3aa1 | 320 | } |
lsm | 0:b21e585b3aa1 | 321 | if (out != NULL) { |
lsm | 0:b21e585b3aa1 | 322 | *out = '\0'; |
lsm | 0:b21e585b3aa1 | 323 | } |
lsm | 0:b21e585b3aa1 | 324 | return r; |
lsm | 0:b21e585b3aa1 | 325 | } |
lsm | 0:b21e585b3aa1 | 326 | |
lsm | 0:b21e585b3aa1 | 327 | static int freshen_json_escape(const char *in, size_t in_sz, char *out, |
lsm | 0:b21e585b3aa1 | 328 | size_t out_sz) { |
lsm | 0:b21e585b3aa1 | 329 | size_t n = 0; |
lsm | 0:b21e585b3aa1 | 330 | for (; in != NULL && *in && in_sz > 0; in++, in_sz--) { |
lsm | 0:b21e585b3aa1 | 331 | const char *chr; |
lsm | 0:b21e585b3aa1 | 332 | size_t chr_sz; |
lsm | 0:b21e585b3aa1 | 333 | switch (*in) { |
lsm | 0:b21e585b3aa1 | 334 | case '\\': |
lsm | 0:b21e585b3aa1 | 335 | chr = "\\\\"; |
lsm | 0:b21e585b3aa1 | 336 | chr_sz = 2; |
lsm | 0:b21e585b3aa1 | 337 | break; |
lsm | 0:b21e585b3aa1 | 338 | case '"': |
lsm | 0:b21e585b3aa1 | 339 | chr = "\\\""; |
lsm | 0:b21e585b3aa1 | 340 | chr_sz = 2; |
lsm | 0:b21e585b3aa1 | 341 | break; |
lsm | 0:b21e585b3aa1 | 342 | case '/': |
lsm | 0:b21e585b3aa1 | 343 | chr = "\\/"; |
lsm | 0:b21e585b3aa1 | 344 | chr_sz = 2; |
lsm | 0:b21e585b3aa1 | 345 | break; |
lsm | 0:b21e585b3aa1 | 346 | case '\b': |
lsm | 0:b21e585b3aa1 | 347 | chr = "\\b"; |
lsm | 0:b21e585b3aa1 | 348 | chr_sz = 2; |
lsm | 0:b21e585b3aa1 | 349 | break; |
lsm | 0:b21e585b3aa1 | 350 | case '\f': |
lsm | 0:b21e585b3aa1 | 351 | chr = "\\f"; |
lsm | 0:b21e585b3aa1 | 352 | chr_sz = 2; |
lsm | 0:b21e585b3aa1 | 353 | break; |
lsm | 0:b21e585b3aa1 | 354 | case '\n': |
lsm | 0:b21e585b3aa1 | 355 | chr = "\\n"; |
lsm | 0:b21e585b3aa1 | 356 | chr_sz = 2; |
lsm | 0:b21e585b3aa1 | 357 | break; |
lsm | 0:b21e585b3aa1 | 358 | case '\r': |
lsm | 0:b21e585b3aa1 | 359 | chr = "\\r"; |
lsm | 0:b21e585b3aa1 | 360 | chr_sz = 2; |
lsm | 0:b21e585b3aa1 | 361 | break; |
lsm | 0:b21e585b3aa1 | 362 | case '\t': |
lsm | 0:b21e585b3aa1 | 363 | chr = "\\t"; |
lsm | 0:b21e585b3aa1 | 364 | chr_sz = 2; |
lsm | 0:b21e585b3aa1 | 365 | break; |
lsm | 0:b21e585b3aa1 | 366 | default: |
lsm | 0:b21e585b3aa1 | 367 | chr = in; |
lsm | 0:b21e585b3aa1 | 368 | chr_sz = 1; |
lsm | 0:b21e585b3aa1 | 369 | break; |
lsm | 0:b21e585b3aa1 | 370 | } |
lsm | 0:b21e585b3aa1 | 371 | if (out != NULL && out_sz > chr_sz) { |
lsm | 0:b21e585b3aa1 | 372 | memmove(out + n, chr, chr_sz); |
lsm | 0:b21e585b3aa1 | 373 | out = out + chr_sz; |
lsm | 0:b21e585b3aa1 | 374 | out_sz = out_sz - chr_sz; |
lsm | 0:b21e585b3aa1 | 375 | } |
lsm | 0:b21e585b3aa1 | 376 | n = n + chr_sz; |
lsm | 0:b21e585b3aa1 | 377 | } |
lsm | 0:b21e585b3aa1 | 378 | if (out != NULL && out_sz > 0) { |
lsm | 0:b21e585b3aa1 | 379 | *out = '\0'; |
lsm | 0:b21e585b3aa1 | 380 | } |
lsm | 0:b21e585b3aa1 | 381 | n = n + 1; |
lsm | 0:b21e585b3aa1 | 382 | return n; |
lsm | 0:b21e585b3aa1 | 383 | } |
lsm | 0:b21e585b3aa1 | 384 | static void freshen_buf_init(struct freshen_buf *buf, void *data, |
lsm | 0:b21e585b3aa1 | 385 | size_t data_sz) { |
lsm | 0:b21e585b3aa1 | 386 | buf->ptr = data; |
lsm | 0:b21e585b3aa1 | 387 | buf->len = 0; |
lsm | 0:b21e585b3aa1 | 388 | buf->cap = data_sz; |
lsm | 0:b21e585b3aa1 | 389 | buf->overflow = 0; |
lsm | 0:b21e585b3aa1 | 390 | } |
lsm | 0:b21e585b3aa1 | 391 | |
lsm | 0:b21e585b3aa1 | 392 | static void freshen_buf_append(struct freshen_buf *buf, const void *data, |
lsm | 0:b21e585b3aa1 | 393 | size_t data_sz) { |
lsm | 0:b21e585b3aa1 | 394 | if (buf->overflow) { |
lsm | 0:b21e585b3aa1 | 395 | return; |
lsm | 0:b21e585b3aa1 | 396 | } |
lsm | 0:b21e585b3aa1 | 397 | if (buf->cap - buf->len < data_sz) { |
lsm | 0:b21e585b3aa1 | 398 | data_sz = buf->cap - buf->len; |
lsm | 0:b21e585b3aa1 | 399 | buf->overflow = 1; |
lsm | 0:b21e585b3aa1 | 400 | } |
lsm | 0:b21e585b3aa1 | 401 | memmove((char *) buf->ptr + buf->len, data, data_sz); |
lsm | 0:b21e585b3aa1 | 402 | buf->len = buf->len + data_sz; |
lsm | 0:b21e585b3aa1 | 403 | } |
lsm | 0:b21e585b3aa1 | 404 | |
lsm | 0:b21e585b3aa1 | 405 | static void freshen_buf_append_int(struct freshen_buf *buf, int n) { |
lsm | 0:b21e585b3aa1 | 406 | if (n < 0) { |
lsm | 0:b21e585b3aa1 | 407 | freshen_buf_append(buf, "-", 1); |
lsm | 0:b21e585b3aa1 | 408 | freshen_buf_append_int(buf, -n); |
lsm | 0:b21e585b3aa1 | 409 | return; |
lsm | 0:b21e585b3aa1 | 410 | } else if (n > 10) { |
lsm | 0:b21e585b3aa1 | 411 | freshen_buf_append_int(buf, n / 10); |
lsm | 0:b21e585b3aa1 | 412 | } |
lsm | 0:b21e585b3aa1 | 413 | freshen_buf_append(buf, &("0123456789"[n % 10]), 1); |
lsm | 0:b21e585b3aa1 | 414 | } |
lsm | 0:b21e585b3aa1 | 415 | |
lsm | 0:b21e585b3aa1 | 416 | static void freshen_buf_append_json_string(struct freshen_buf *buf, |
lsm | 0:b21e585b3aa1 | 417 | const char *s) { |
lsm | 0:b21e585b3aa1 | 418 | for (; s != NULL && *s; s++) { |
lsm | 0:b21e585b3aa1 | 419 | char out[7]; |
lsm | 0:b21e585b3aa1 | 420 | int n = freshen_json_escape(s, 1, out, sizeof(out)); |
lsm | 0:b21e585b3aa1 | 421 | freshen_buf_append(buf, out, n - 1); |
lsm | 0:b21e585b3aa1 | 422 | } |
lsm | 0:b21e585b3aa1 | 423 | } |
lsm | 0:b21e585b3aa1 | 424 | FRESHEN_API int freshen_core_notify(struct freshen_ctx *freshen, |
lsm | 0:b21e585b3aa1 | 425 | const char *method, const char *arg) { |
lsm | 0:b21e585b3aa1 | 426 | if (method == NULL || strlen(method) == 0) { |
lsm | 0:b21e585b3aa1 | 427 | return FRESHEN_ERROR_INVALID; |
lsm | 0:b21e585b3aa1 | 428 | } |
lsm | 0:b21e585b3aa1 | 429 | if (arg == NULL) { |
lsm | 0:b21e585b3aa1 | 430 | arg = ""; |
lsm | 0:b21e585b3aa1 | 431 | } |
lsm | 0:b21e585b3aa1 | 432 | int method_sz = strlen(method) + 1; |
lsm | 0:b21e585b3aa1 | 433 | int arg_sz = strlen(arg) + 1; |
lsm | 0:b21e585b3aa1 | 434 | freshen_spinlock_lock(&freshen->queue.lock); |
lsm | 0:b21e585b3aa1 | 435 | if (freshen->queue.tail + method_sz + arg_sz >= freshen->queue.buf_sz) { |
lsm | 0:b21e585b3aa1 | 436 | freshen_spinlock_unlock(&freshen->queue.lock); |
lsm | 0:b21e585b3aa1 | 437 | return FRESHEN_ERROR_INTERNAL; |
lsm | 0:b21e585b3aa1 | 438 | } |
lsm | 0:b21e585b3aa1 | 439 | memmove((char *) freshen->queue.buf + freshen->queue.tail, method, method_sz); |
lsm | 0:b21e585b3aa1 | 440 | memmove((char *) freshen->queue.buf + freshen->queue.tail + method_sz, arg, |
lsm | 0:b21e585b3aa1 | 441 | arg_sz); |
lsm | 0:b21e585b3aa1 | 442 | freshen->queue.tail = freshen->queue.tail + method_sz + arg_sz; |
lsm | 0:b21e585b3aa1 | 443 | freshen_spinlock_unlock(&freshen->queue.lock); |
lsm | 0:b21e585b3aa1 | 444 | return 0; |
lsm | 0:b21e585b3aa1 | 445 | } |
lsm | 0:b21e585b3aa1 | 446 | |
lsm | 0:b21e585b3aa1 | 447 | static int freshen_jsonrpc_notify(struct freshen_ctx *freshen, void *res, |
lsm | 0:b21e585b3aa1 | 448 | size_t res_sz) { |
lsm | 0:b21e585b3aa1 | 449 | struct freshen_buf buf; |
lsm | 0:b21e585b3aa1 | 450 | freshen_buf_init(&buf, res, res_sz); |
lsm | 0:b21e585b3aa1 | 451 | freshen_spinlock_lock(&freshen->queue.lock); |
lsm | 0:b21e585b3aa1 | 452 | char *method = (char *) freshen->queue.buf; |
lsm | 0:b21e585b3aa1 | 453 | int method_sz = strlen(method) + 1; |
lsm | 0:b21e585b3aa1 | 454 | if (method_sz == 1) { |
lsm | 0:b21e585b3aa1 | 455 | freshen_spinlock_unlock(&freshen->queue.lock); |
lsm | 0:b21e585b3aa1 | 456 | return 0; |
lsm | 0:b21e585b3aa1 | 457 | } |
lsm | 0:b21e585b3aa1 | 458 | char *arg = (char *) freshen->queue.buf + method_sz; |
lsm | 0:b21e585b3aa1 | 459 | int arg_sz = strlen(arg) + 1; |
lsm | 0:b21e585b3aa1 | 460 | |
lsm | 0:b21e585b3aa1 | 461 | freshen_buf_append(&buf, "{\"method\":\"", 11); |
lsm | 0:b21e585b3aa1 | 462 | freshen_buf_append_json_string(&buf, method); |
lsm | 0:b21e585b3aa1 | 463 | freshen_buf_append(&buf, "\",\"params\":[\"", 13); |
lsm | 0:b21e585b3aa1 | 464 | freshen_buf_append_json_string(&buf, arg); |
lsm | 0:b21e585b3aa1 | 465 | freshen_buf_append(&buf, "\"]}", 4); |
lsm | 0:b21e585b3aa1 | 466 | |
lsm | 0:b21e585b3aa1 | 467 | memmove(freshen->queue.buf, (char *) freshen->queue.buf + method_sz + arg_sz, |
lsm | 0:b21e585b3aa1 | 468 | freshen->queue.buf_sz - method_sz - arg_sz); |
lsm | 0:b21e585b3aa1 | 469 | memset( |
lsm | 0:b21e585b3aa1 | 470 | (char *) freshen->queue.buf + freshen->queue.buf_sz - method_sz - arg_sz, |
lsm | 0:b21e585b3aa1 | 471 | 0, method_sz + arg_sz); |
lsm | 0:b21e585b3aa1 | 472 | freshen->queue.tail = freshen->queue.tail - method_sz - arg_sz; |
lsm | 0:b21e585b3aa1 | 473 | freshen_spinlock_unlock(&freshen->queue.lock); |
lsm | 0:b21e585b3aa1 | 474 | |
lsm | 0:b21e585b3aa1 | 475 | if (buf.overflow) { |
lsm | 0:b21e585b3aa1 | 476 | return FRESHEN_ERROR_INTERNAL; |
lsm | 0:b21e585b3aa1 | 477 | } |
lsm | 0:b21e585b3aa1 | 478 | return buf.len; |
lsm | 0:b21e585b3aa1 | 479 | } |
lsm | 0:b21e585b3aa1 | 480 | |
lsm | 0:b21e585b3aa1 | 481 | static int freshen_jsonrpc_result(struct freshen_buf *buf, const char *id, |
lsm | 0:b21e585b3aa1 | 482 | size_t id_sz, int code, const char *res) { |
lsm | 0:b21e585b3aa1 | 483 | freshen_buf_append(buf, "{\"id\":", 6); |
lsm | 0:b21e585b3aa1 | 484 | freshen_buf_append(buf, id, id_sz); |
lsm | 0:b21e585b3aa1 | 485 | if (code == 0) { |
lsm | 0:b21e585b3aa1 | 486 | freshen_buf_append(buf, ",\"result\":\"", 11); |
lsm | 0:b21e585b3aa1 | 487 | } else { |
lsm | 0:b21e585b3aa1 | 488 | freshen_buf_append(buf, ",\"error\":{\"code\":", 17); |
lsm | 0:b21e585b3aa1 | 489 | freshen_buf_append_int(buf, code); |
lsm | 0:b21e585b3aa1 | 490 | freshen_buf_append(buf, ",\"message\":\"", 12); |
lsm | 0:b21e585b3aa1 | 491 | } |
lsm | 0:b21e585b3aa1 | 492 | freshen_buf_append_json_string(buf, res); |
lsm | 0:b21e585b3aa1 | 493 | if (code == 0) { |
lsm | 0:b21e585b3aa1 | 494 | freshen_buf_append(buf, "\"}", 2); |
lsm | 0:b21e585b3aa1 | 495 | } else { |
lsm | 0:b21e585b3aa1 | 496 | freshen_buf_append(buf, "\"}}", 3); |
lsm | 0:b21e585b3aa1 | 497 | } |
lsm | 0:b21e585b3aa1 | 498 | freshen_buf_append(buf, "", 1); |
lsm | 0:b21e585b3aa1 | 499 | if (buf->overflow) { |
lsm | 0:b21e585b3aa1 | 500 | return FRESHEN_ERROR_INTERNAL; |
lsm | 0:b21e585b3aa1 | 501 | } |
lsm | 0:b21e585b3aa1 | 502 | return buf->len; |
lsm | 0:b21e585b3aa1 | 503 | } |
lsm | 0:b21e585b3aa1 | 504 | |
lsm | 0:b21e585b3aa1 | 505 | static int freshen_jsonrpc_call(struct freshen_ctx *freshen, char *req, |
lsm | 0:b21e585b3aa1 | 506 | size_t req_sz, char *res, size_t res_sz) { |
lsm | 0:b21e585b3aa1 | 507 | int n; |
lsm | 0:b21e585b3aa1 | 508 | const char *id = NULL, *method = NULL, *params = NULL, *arg = NULL; |
lsm | 0:b21e585b3aa1 | 509 | size_t id_sz = 0, method_sz = 0, params_sz = 0, arg_sz = 0; |
lsm | 0:b21e585b3aa1 | 510 | struct freshen_method *m; |
lsm | 0:b21e585b3aa1 | 511 | |
lsm | 0:b21e585b3aa1 | 512 | /* ID must exist */ |
lsm | 0:b21e585b3aa1 | 513 | if (freshen_json(req, req_sz, "id", 2, &id, &id_sz) < 0) { |
lsm | 0:b21e585b3aa1 | 514 | return FRESHEN_ERROR_INVALID; |
lsm | 0:b21e585b3aa1 | 515 | } |
lsm | 0:b21e585b3aa1 | 516 | |
lsm | 0:b21e585b3aa1 | 517 | /* Method must exist and must be a string*/ |
lsm | 0:b21e585b3aa1 | 518 | if (freshen_json(req, req_sz, "method", 6, &method, &method_sz) < 0) { |
lsm | 0:b21e585b3aa1 | 519 | return FRESHEN_ERROR_INVALID; |
lsm | 0:b21e585b3aa1 | 520 | } |
lsm | 0:b21e585b3aa1 | 521 | if (method_sz < 3 || method[0] != '\"' || method[method_sz - 1] != '\"') { |
lsm | 0:b21e585b3aa1 | 522 | return FRESHEN_ERROR_INVALID; |
lsm | 0:b21e585b3aa1 | 523 | } |
lsm | 0:b21e585b3aa1 | 524 | |
lsm | 0:b21e585b3aa1 | 525 | /* Params must exist and must be an array */ |
lsm | 0:b21e585b3aa1 | 526 | if (freshen_json(req, req_sz, "params", 6, ¶ms, ¶ms_sz) < 0) { |
lsm | 0:b21e585b3aa1 | 527 | return FRESHEN_ERROR_INVALID; |
lsm | 0:b21e585b3aa1 | 528 | } |
lsm | 0:b21e585b3aa1 | 529 | if (params_sz < 2 || params[0] != '[' || params[params_sz - 1] != ']') { |
lsm | 0:b21e585b3aa1 | 530 | return FRESHEN_ERROR_INVALID; |
lsm | 0:b21e585b3aa1 | 531 | } |
lsm | 0:b21e585b3aa1 | 532 | |
lsm | 0:b21e585b3aa1 | 533 | /* Extract first param value and unescape it as a string */ |
lsm | 0:b21e585b3aa1 | 534 | if (freshen_json(params, params_sz, NULL, 0, &arg, &arg_sz) < 0) { |
lsm | 0:b21e585b3aa1 | 535 | return FRESHEN_ERROR_INVALID; |
lsm | 0:b21e585b3aa1 | 536 | } |
lsm | 0:b21e585b3aa1 | 537 | if ((n = freshen_json_unescape(arg, arg_sz, req)) < 0) { |
lsm | 0:b21e585b3aa1 | 538 | return n; |
lsm | 0:b21e585b3aa1 | 539 | } |
lsm | 0:b21e585b3aa1 | 540 | |
lsm | 0:b21e585b3aa1 | 541 | /* number of bytes used for success result prologue */ |
lsm | 0:b21e585b3aa1 | 542 | const size_t prologue_sz = 19; |
lsm | 0:b21e585b3aa1 | 543 | struct freshen_buf buf; |
lsm | 0:b21e585b3aa1 | 544 | freshen_buf_init(&buf, res, res_sz); |
lsm | 0:b21e585b3aa1 | 545 | for (m = freshen->methods; m != NULL; m = m->next) { |
lsm | 0:b21e585b3aa1 | 546 | if (m->method_sz == method_sz - 2 && |
lsm | 0:b21e585b3aa1 | 547 | memcmp(m->method, method + 1, method_sz - 2) == 0) { |
lsm | 0:b21e585b3aa1 | 548 | int code = m->func(req, res, res_sz - id_sz - prologue_sz, m->userdata); |
lsm | 0:b21e585b3aa1 | 549 | /* |
lsm | 0:b21e585b3aa1 | 550 | * move result string to the end of the buffer, then escape it in-place |
lsm | 0:b21e585b3aa1 | 551 | * when preparing the result JSON. If overflow happens - return an error |
lsm | 0:b21e585b3aa1 | 552 | */ |
lsm | 0:b21e585b3aa1 | 553 | size_t n = strlen(res) + 1; |
lsm | 0:b21e585b3aa1 | 554 | char *p = res + res_sz - n; |
lsm | 0:b21e585b3aa1 | 555 | memmove(p, res, n); |
lsm | 0:b21e585b3aa1 | 556 | int r = freshen_jsonrpc_result(&buf, id, id_sz, code, p); |
lsm | 0:b21e585b3aa1 | 557 | if (buf.overflow) { |
lsm | 0:b21e585b3aa1 | 558 | freshen_buf_init(&buf, res, res_sz); |
lsm | 0:b21e585b3aa1 | 559 | return freshen_jsonrpc_result(&buf, id, id_sz, FRESHEN_ERROR_INTERNAL, |
lsm | 0:b21e585b3aa1 | 560 | "result is too long"); |
lsm | 0:b21e585b3aa1 | 561 | } |
lsm | 0:b21e585b3aa1 | 562 | return r; |
lsm | 0:b21e585b3aa1 | 563 | } |
lsm | 0:b21e585b3aa1 | 564 | } |
lsm | 0:b21e585b3aa1 | 565 | return freshen_jsonrpc_result(&buf, id, id_sz, FRESHEN_ERROR_NOT_FOUND, |
lsm | 0:b21e585b3aa1 | 566 | "method not found"); |
lsm | 0:b21e585b3aa1 | 567 | } |
lsm | 0:b21e585b3aa1 | 568 | |
lsm | 0:b21e585b3aa1 | 569 | FRESHEN_API int freshen_core_process(struct freshen_ctx *freshen, void *req, |
lsm | 0:b21e585b3aa1 | 570 | size_t req_sz, void *res, size_t res_sz) { |
lsm | 0:b21e585b3aa1 | 571 | int r = freshen_jsonrpc_notify(freshen, res, res_sz); |
lsm | 0:b21e585b3aa1 | 572 | |
lsm | 0:b21e585b3aa1 | 573 | if (r == 0 && req_sz > 0) { |
lsm | 0:b21e585b3aa1 | 574 | r = freshen_jsonrpc_call(freshen, (char *) req, req_sz, (char *) res, |
lsm | 0:b21e585b3aa1 | 575 | res_sz); |
lsm | 0:b21e585b3aa1 | 576 | } |
lsm | 0:b21e585b3aa1 | 577 | return r; |
lsm | 0:b21e585b3aa1 | 578 | } |
lsm | 0:b21e585b3aa1 | 579 | #include <stdio.h> |
lsm | 0:b21e585b3aa1 | 580 | #include <stdlib.h> |
lsm | 0:b21e585b3aa1 | 581 | /* |
lsm | 0:b21e585b3aa1 | 582 | * Common OTA code |
lsm | 0:b21e585b3aa1 | 583 | */ |
lsm | 0:b21e585b3aa1 | 584 | static int b64rev(int c) { |
lsm | 0:b21e585b3aa1 | 585 | if (c >= 'A' && c <= 'Z') { |
lsm | 0:b21e585b3aa1 | 586 | return c - 'A'; |
lsm | 0:b21e585b3aa1 | 587 | } else if (c >= 'a' && c <= 'z') { |
lsm | 0:b21e585b3aa1 | 588 | return c + 26 - 'a'; |
lsm | 0:b21e585b3aa1 | 589 | } else if (c >= '0' && c <= '9') { |
lsm | 0:b21e585b3aa1 | 590 | return c + 52 - '0'; |
lsm | 0:b21e585b3aa1 | 591 | } else if (c == '+') { |
lsm | 0:b21e585b3aa1 | 592 | return 62; |
lsm | 0:b21e585b3aa1 | 593 | } else if (c == '/') { |
lsm | 0:b21e585b3aa1 | 594 | return 63; |
lsm | 0:b21e585b3aa1 | 595 | } else { |
lsm | 0:b21e585b3aa1 | 596 | return 64; |
lsm | 0:b21e585b3aa1 | 597 | } |
lsm | 0:b21e585b3aa1 | 598 | } |
lsm | 0:b21e585b3aa1 | 599 | |
lsm | 0:b21e585b3aa1 | 600 | static int b64dec(const char *src, int n, char *dst) { |
lsm | 0:b21e585b3aa1 | 601 | const char *end = src + n; |
lsm | 0:b21e585b3aa1 | 602 | int len = 0; |
lsm | 0:b21e585b3aa1 | 603 | while (src + 3 < end) { |
lsm | 0:b21e585b3aa1 | 604 | int a = b64rev(src[0]), b = b64rev(src[1]), c = b64rev(src[2]), |
lsm | 0:b21e585b3aa1 | 605 | d = b64rev(src[3]); |
lsm | 0:b21e585b3aa1 | 606 | dst[len++] = (a << 2) | (b >> 4); |
lsm | 0:b21e585b3aa1 | 607 | if (src[2] != '=') { |
lsm | 0:b21e585b3aa1 | 608 | dst[len++] = (b << 4) | (c >> 2); |
lsm | 0:b21e585b3aa1 | 609 | if (src[3] != '=') { |
lsm | 0:b21e585b3aa1 | 610 | dst[len++] = (c << 6) | d; |
lsm | 0:b21e585b3aa1 | 611 | } |
lsm | 0:b21e585b3aa1 | 612 | } |
lsm | 0:b21e585b3aa1 | 613 | src += 4; |
lsm | 0:b21e585b3aa1 | 614 | } |
lsm | 0:b21e585b3aa1 | 615 | return len; |
lsm | 0:b21e585b3aa1 | 616 | } |
lsm | 0:b21e585b3aa1 | 617 | |
lsm | 0:b21e585b3aa1 | 618 | static int freshen_ota_init(struct freshen_ctx *ctx); |
lsm | 0:b21e585b3aa1 | 619 | static int freshen_ota_enabled(struct freshen_ctx *ctx); |
lsm | 0:b21e585b3aa1 | 620 | static int freshen_ota_begin(struct freshen_ctx *ctx); |
lsm | 0:b21e585b3aa1 | 621 | static int freshen_ota_end(struct freshen_ctx *ctx, int success); |
lsm | 0:b21e585b3aa1 | 622 | static int freshen_ota_write(struct freshen_ctx *ctx, void *buf, size_t bufsz); |
lsm | 0:b21e585b3aa1 | 623 | static int freshen_ota_commit(struct freshen_ctx *ctx); |
lsm | 0:b21e585b3aa1 | 624 | |
lsm | 0:b21e585b3aa1 | 625 | static int freshen_rpc_ota_begin(const char *args, char *buf, size_t len, |
lsm | 0:b21e585b3aa1 | 626 | void *userdata) { |
lsm | 0:b21e585b3aa1 | 627 | struct freshen_ctx *ctx = (struct freshen_ctx *) userdata; |
lsm | 0:b21e585b3aa1 | 628 | int r = freshen_ota_begin(ctx); |
lsm | 0:b21e585b3aa1 | 629 | snprintf(buf, len, "%s", (r == 0 ? "true" : "false")); |
lsm | 0:b21e585b3aa1 | 630 | return 0; |
lsm | 0:b21e585b3aa1 | 631 | (void) args; |
lsm | 0:b21e585b3aa1 | 632 | } |
lsm | 0:b21e585b3aa1 | 633 | |
lsm | 0:b21e585b3aa1 | 634 | static int freshen_rpc_ota_end(const char *args, char *buf, size_t len, |
lsm | 0:b21e585b3aa1 | 635 | void *userdata) { |
lsm | 0:b21e585b3aa1 | 636 | struct freshen_ctx *ctx = (struct freshen_ctx *) userdata; |
lsm | 0:b21e585b3aa1 | 637 | int success = 0; |
lsm | 0:b21e585b3aa1 | 638 | if (sscanf(args, "%d", &success) != 1) { |
lsm | 0:b21e585b3aa1 | 639 | snprintf(buf, len, "bad args"); |
lsm | 0:b21e585b3aa1 | 640 | return 400; |
lsm | 0:b21e585b3aa1 | 641 | } else if (freshen_ota_end(ctx, success) != 0) { |
lsm | 0:b21e585b3aa1 | 642 | snprintf(buf, len, "failed"); |
lsm | 0:b21e585b3aa1 | 643 | return 500; |
lsm | 0:b21e585b3aa1 | 644 | } else { |
lsm | 0:b21e585b3aa1 | 645 | snprintf(buf, len, "true"); |
lsm | 0:b21e585b3aa1 | 646 | return 0; |
lsm | 0:b21e585b3aa1 | 647 | } |
lsm | 0:b21e585b3aa1 | 648 | } |
lsm | 0:b21e585b3aa1 | 649 | |
lsm | 0:b21e585b3aa1 | 650 | static int freshen_rpc_ota_write(const char *args, char *buf, size_t len, |
lsm | 0:b21e585b3aa1 | 651 | void *userdata) { |
lsm | 0:b21e585b3aa1 | 652 | struct freshen_ctx *ctx = (struct freshen_ctx *) userdata; |
lsm | 0:b21e585b3aa1 | 653 | char *dec = (char *) args; // Decode in-place |
lsm | 0:b21e585b3aa1 | 654 | int dec_len = b64dec(args, strlen(args), dec); |
lsm | 0:b21e585b3aa1 | 655 | dec[len] = '\0'; |
lsm | 0:b21e585b3aa1 | 656 | if (freshen_ota_write(ctx, (void *) dec, dec_len) != 0) { |
lsm | 0:b21e585b3aa1 | 657 | snprintf(buf, len, "cannot write"); |
lsm | 0:b21e585b3aa1 | 658 | return 500; |
lsm | 0:b21e585b3aa1 | 659 | } else { |
lsm | 0:b21e585b3aa1 | 660 | snprintf(buf, len, "true"); |
lsm | 0:b21e585b3aa1 | 661 | return 0; |
lsm | 0:b21e585b3aa1 | 662 | } |
lsm | 0:b21e585b3aa1 | 663 | } |
lsm | 0:b21e585b3aa1 | 664 | |
lsm | 0:b21e585b3aa1 | 665 | #ifdef ESP_PLATFORM |
lsm | 0:b21e585b3aa1 | 666 | |
lsm | 0:b21e585b3aa1 | 667 | #include <esp_log.h> |
lsm | 0:b21e585b3aa1 | 668 | #include <esp_ota_ops.h> |
lsm | 0:b21e585b3aa1 | 669 | #include <esp_system.h> |
lsm | 0:b21e585b3aa1 | 670 | #include <nvs_flash.h> |
lsm | 0:b21e585b3aa1 | 671 | #include <unistd.h> |
lsm | 0:b21e585b3aa1 | 672 | |
lsm | 0:b21e585b3aa1 | 673 | #define FRESHEN_TAG "freshen" |
lsm | 0:b21e585b3aa1 | 674 | #define FRESHEN_NVS_NAMESPACE "freshen" |
lsm | 0:b21e585b3aa1 | 675 | #define ROLLBACK_KV_KEY "__rollback" |
lsm | 0:b21e585b3aa1 | 676 | |
lsm | 0:b21e585b3aa1 | 677 | static struct { |
lsm | 0:b21e585b3aa1 | 678 | int can_rollback; |
lsm | 0:b21e585b3aa1 | 679 | const esp_partition_t *update_partition; |
lsm | 0:b21e585b3aa1 | 680 | esp_ota_handle_t update_handle; |
lsm | 0:b21e585b3aa1 | 681 | } s_ota; |
lsm | 0:b21e585b3aa1 | 682 | |
lsm | 0:b21e585b3aa1 | 683 | static int freshen_kv_set(const char *key, const char *value) { |
lsm | 0:b21e585b3aa1 | 684 | nvs_handle nvs_handle; |
lsm | 0:b21e585b3aa1 | 685 | esp_err_t err; |
lsm | 0:b21e585b3aa1 | 686 | err = nvs_open(FRESHEN_NVS_NAMESPACE, NVS_READWRITE, &nvs_handle); |
lsm | 0:b21e585b3aa1 | 687 | if (err != ESP_OK) { |
lsm | 0:b21e585b3aa1 | 688 | ESP_LOGE(FRESHEN_TAG, "nvs_open: err=%d", err); |
lsm | 0:b21e585b3aa1 | 689 | return -1; |
lsm | 0:b21e585b3aa1 | 690 | } |
lsm | 0:b21e585b3aa1 | 691 | err = nvs_set_str(nvs_handle, key, value == NULL ? "" : value); |
lsm | 0:b21e585b3aa1 | 692 | if (err != ESP_OK) { |
lsm | 0:b21e585b3aa1 | 693 | ESP_LOGE(FRESHEN_TAG, "nvs_set_str: err=%d", err); |
lsm | 0:b21e585b3aa1 | 694 | return -1; |
lsm | 0:b21e585b3aa1 | 695 | } |
lsm | 0:b21e585b3aa1 | 696 | err = nvs_commit(nvs_handle); |
lsm | 0:b21e585b3aa1 | 697 | if (err != ESP_OK) { |
lsm | 0:b21e585b3aa1 | 698 | ESP_LOGE(FRESHEN_TAG, "nvs_commit: err=%d", err); |
lsm | 0:b21e585b3aa1 | 699 | return -1; |
lsm | 0:b21e585b3aa1 | 700 | } |
lsm | 0:b21e585b3aa1 | 701 | nvs_close(nvs_handle); |
lsm | 0:b21e585b3aa1 | 702 | return 0; |
lsm | 0:b21e585b3aa1 | 703 | } |
lsm | 0:b21e585b3aa1 | 704 | |
lsm | 0:b21e585b3aa1 | 705 | static const char *freshen_kv_get(const char *key) { |
lsm | 0:b21e585b3aa1 | 706 | nvs_handle nvs_handle = 0; |
lsm | 0:b21e585b3aa1 | 707 | size_t size; |
lsm | 0:b21e585b3aa1 | 708 | char *value = NULL; |
lsm | 0:b21e585b3aa1 | 709 | esp_err_t err; |
lsm | 0:b21e585b3aa1 | 710 | |
lsm | 0:b21e585b3aa1 | 711 | if ((err = nvs_open(FRESHEN_NVS_NAMESPACE, NVS_READONLY, &nvs_handle)) != |
lsm | 0:b21e585b3aa1 | 712 | ESP_OK) { |
lsm | 0:b21e585b3aa1 | 713 | ESP_LOGE(FRESHEN_TAG, "nvs_open: err=%d", err); |
lsm | 0:b21e585b3aa1 | 714 | } else if ((err = nvs_get_str(nvs_handle, key, NULL, &size)) != ESP_OK) { |
lsm | 0:b21e585b3aa1 | 715 | ESP_LOGE(FRESHEN_TAG, "nvs_get_str: err=%d", err); |
lsm | 0:b21e585b3aa1 | 716 | } else if ((value = (char *) calloc(1, size)) == NULL) { |
lsm | 0:b21e585b3aa1 | 717 | ESP_LOGE(FRESHEN_TAG, "OOM: value is %zu", size); |
lsm | 0:b21e585b3aa1 | 718 | } else if ((err = nvs_get_str(nvs_handle, key, value, &size)) != ESP_OK) { |
lsm | 0:b21e585b3aa1 | 719 | ESP_LOGE(FRESHEN_TAG, "nvs_get_str: err=%d", err); |
lsm | 0:b21e585b3aa1 | 720 | } |
lsm | 0:b21e585b3aa1 | 721 | nvs_close(nvs_handle); |
lsm | 0:b21e585b3aa1 | 722 | |
lsm | 0:b21e585b3aa1 | 723 | return value; |
lsm | 0:b21e585b3aa1 | 724 | } |
lsm | 0:b21e585b3aa1 | 725 | |
lsm | 0:b21e585b3aa1 | 726 | static int freshen_ota_init(struct freshen_ctx *ctx) { |
lsm | 0:b21e585b3aa1 | 727 | const char *label = freshen_kv_get(ROLLBACK_KV_KEY); |
lsm | 0:b21e585b3aa1 | 728 | if (label == NULL) { |
lsm | 0:b21e585b3aa1 | 729 | ESP_LOGI(FRESHEN_TAG, |
lsm | 0:b21e585b3aa1 | 730 | "no rollback partition is given, booting up normally"); |
lsm | 0:b21e585b3aa1 | 731 | return 0; |
lsm | 0:b21e585b3aa1 | 732 | } |
lsm | 0:b21e585b3aa1 | 733 | const esp_partition_t *p = esp_partition_find_first( |
lsm | 0:b21e585b3aa1 | 734 | ESP_PARTITION_TYPE_APP, ESP_PARTITION_SUBTYPE_ANY, label); |
lsm | 0:b21e585b3aa1 | 735 | if (p == NULL) { |
lsm | 0:b21e585b3aa1 | 736 | ESP_LOGI(FRESHEN_TAG, "can't find partition %s for rollback", label); |
lsm | 0:b21e585b3aa1 | 737 | return 0; |
lsm | 0:b21e585b3aa1 | 738 | } |
lsm | 0:b21e585b3aa1 | 739 | ESP_LOGI(FRESHEN_TAG, "use partition %s for rollback", label); |
lsm | 0:b21e585b3aa1 | 740 | freshen_kv_set(ROLLBACK_KV_KEY, NULL); |
lsm | 0:b21e585b3aa1 | 741 | esp_ota_set_boot_partition(p); |
lsm | 0:b21e585b3aa1 | 742 | s_ota.can_rollback = 1; |
lsm | 0:b21e585b3aa1 | 743 | return 0; |
lsm | 0:b21e585b3aa1 | 744 | (void) ctx; |
lsm | 0:b21e585b3aa1 | 745 | } |
lsm | 0:b21e585b3aa1 | 746 | |
lsm | 0:b21e585b3aa1 | 747 | static int freshen_ota_enabled(struct freshen_ctx *ctx) { |
lsm | 0:b21e585b3aa1 | 748 | return esp_ota_get_next_update_partition(NULL) == NULL ? 0 : 1; |
lsm | 0:b21e585b3aa1 | 749 | (void) ctx; |
lsm | 0:b21e585b3aa1 | 750 | } |
lsm | 0:b21e585b3aa1 | 751 | |
lsm | 0:b21e585b3aa1 | 752 | static int freshen_ota_begin(struct freshen_ctx *ctx) { |
lsm | 0:b21e585b3aa1 | 753 | if (s_ota.update_partition != NULL) { |
lsm | 0:b21e585b3aa1 | 754 | ESP_LOGE(FRESHEN_TAG, "another OTA is already in-progress"); |
lsm | 0:b21e585b3aa1 | 755 | return -1; |
lsm | 0:b21e585b3aa1 | 756 | } |
lsm | 0:b21e585b3aa1 | 757 | s_ota.update_partition = esp_ota_get_next_update_partition(NULL); |
lsm | 0:b21e585b3aa1 | 758 | ESP_LOGI(FRESHEN_TAG, "Starting OTA. update_partition=%p", |
lsm | 0:b21e585b3aa1 | 759 | s_ota.update_partition); |
lsm | 0:b21e585b3aa1 | 760 | esp_err_t err = esp_ota_begin(s_ota.update_partition, OTA_SIZE_UNKNOWN, |
lsm | 0:b21e585b3aa1 | 761 | &s_ota.update_handle); |
lsm | 0:b21e585b3aa1 | 762 | if (err != ESP_OK) { |
lsm | 0:b21e585b3aa1 | 763 | ESP_LOGE(FRESHEN_TAG, "esp_ota_begin failed, err=%#x", err); |
lsm | 0:b21e585b3aa1 | 764 | return -1; |
lsm | 0:b21e585b3aa1 | 765 | } |
lsm | 0:b21e585b3aa1 | 766 | return 0; |
lsm | 0:b21e585b3aa1 | 767 | (void) ctx; |
lsm | 0:b21e585b3aa1 | 768 | } |
lsm | 0:b21e585b3aa1 | 769 | |
lsm | 0:b21e585b3aa1 | 770 | static int freshen_ota_write(struct freshen_ctx *ctx, void *buf, size_t bufsz) { |
lsm | 0:b21e585b3aa1 | 771 | esp_err_t err = esp_ota_write(s_ota.update_handle, buf, bufsz); |
lsm | 0:b21e585b3aa1 | 772 | if (err != ESP_OK) { |
lsm | 0:b21e585b3aa1 | 773 | ESP_LOGE(FRESHEN_TAG, "esp_ota_write failed, err=%#x", err); |
lsm | 0:b21e585b3aa1 | 774 | return -1; |
lsm | 0:b21e585b3aa1 | 775 | } |
lsm | 0:b21e585b3aa1 | 776 | ESP_LOGD(FRESHEN_TAG, "written %d bytes", bufsz); |
lsm | 0:b21e585b3aa1 | 777 | return 0; |
lsm | 0:b21e585b3aa1 | 778 | (void) ctx; |
lsm | 0:b21e585b3aa1 | 779 | } |
lsm | 0:b21e585b3aa1 | 780 | |
lsm | 0:b21e585b3aa1 | 781 | static int freshen_ota_end(struct freshen_ctx *ctx, int success) { |
lsm | 0:b21e585b3aa1 | 782 | const esp_partition_t *p = s_ota.update_partition; |
lsm | 0:b21e585b3aa1 | 783 | esp_err_t err = esp_ota_end(s_ota.update_handle); |
lsm | 0:b21e585b3aa1 | 784 | if (err != ESP_OK) { |
lsm | 0:b21e585b3aa1 | 785 | ESP_LOGE(FRESHEN_TAG, "err=0x%x", err); |
lsm | 0:b21e585b3aa1 | 786 | return -1; |
lsm | 0:b21e585b3aa1 | 787 | } |
lsm | 0:b21e585b3aa1 | 788 | s_ota.update_partition = NULL; |
lsm | 0:b21e585b3aa1 | 789 | if (success) { |
lsm | 0:b21e585b3aa1 | 790 | const esp_partition_t *rollback = esp_ota_get_running_partition(); |
lsm | 0:b21e585b3aa1 | 791 | ESP_LOGI(FRESHEN_TAG, "use partition %s for the next boot", |
lsm | 0:b21e585b3aa1 | 792 | rollback->label); |
lsm | 0:b21e585b3aa1 | 793 | if (freshen_kv_set(ROLLBACK_KV_KEY, rollback->label) < 0) return -1; |
lsm | 0:b21e585b3aa1 | 794 | esp_err_t err = esp_ota_set_boot_partition(p); |
lsm | 0:b21e585b3aa1 | 795 | if (err != ESP_OK) { |
lsm | 0:b21e585b3aa1 | 796 | ESP_LOGE(FRESHEN_TAG, "esp_ota_set_boot_partition: err=0x%d", err); |
lsm | 0:b21e585b3aa1 | 797 | return -1; |
lsm | 0:b21e585b3aa1 | 798 | } |
lsm | 0:b21e585b3aa1 | 799 | ESP_LOGI(FRESHEN_TAG, "restarting..."); |
lsm | 0:b21e585b3aa1 | 800 | sleep(2); |
lsm | 0:b21e585b3aa1 | 801 | esp_restart(); |
lsm | 0:b21e585b3aa1 | 802 | } |
lsm | 0:b21e585b3aa1 | 803 | return 0; |
lsm | 0:b21e585b3aa1 | 804 | (void) ctx; |
lsm | 0:b21e585b3aa1 | 805 | } |
lsm | 0:b21e585b3aa1 | 806 | |
lsm | 0:b21e585b3aa1 | 807 | static int freshen_ota_commit(struct freshen_ctx *ctx) { |
lsm | 0:b21e585b3aa1 | 808 | if (!s_ota.can_rollback) return 0; |
lsm | 0:b21e585b3aa1 | 809 | const esp_partition_t *p = esp_ota_get_running_partition(); |
lsm | 0:b21e585b3aa1 | 810 | esp_err_t err = esp_ota_set_boot_partition(p); |
lsm | 0:b21e585b3aa1 | 811 | if (err != ESP_OK) { |
lsm | 0:b21e585b3aa1 | 812 | ESP_LOGE(FRESHEN_TAG, "esp_ota_set_boot_partition: err=0x%d", err); |
lsm | 0:b21e585b3aa1 | 813 | return -1; |
lsm | 0:b21e585b3aa1 | 814 | } |
lsm | 0:b21e585b3aa1 | 815 | s_ota.can_rollback = 0; |
lsm | 0:b21e585b3aa1 | 816 | return 0; |
lsm | 0:b21e585b3aa1 | 817 | (void) ctx; |
lsm | 0:b21e585b3aa1 | 818 | } |
lsm | 0:b21e585b3aa1 | 819 | |
lsm | 0:b21e585b3aa1 | 820 | #elif (defined(__linux__) || defined(__APPLE__)) |
lsm | 0:b21e585b3aa1 | 821 | |
lsm | 0:b21e585b3aa1 | 822 | #include <errno.h> |
lsm | 0:b21e585b3aa1 | 823 | #include <signal.h> |
lsm | 0:b21e585b3aa1 | 824 | #include <sys/stat.h> |
lsm | 0:b21e585b3aa1 | 825 | #include <sys/types.h> |
lsm | 0:b21e585b3aa1 | 826 | #include <sys/wait.h> |
lsm | 0:b21e585b3aa1 | 827 | #include <time.h> |
lsm | 0:b21e585b3aa1 | 828 | #include <unistd.h> |
lsm | 0:b21e585b3aa1 | 829 | |
lsm | 0:b21e585b3aa1 | 830 | #define FRESHEN_OTA_FILE "./tmp" |
lsm | 0:b21e585b3aa1 | 831 | |
lsm | 0:b21e585b3aa1 | 832 | #ifdef __APPLE__ |
lsm | 0:b21e585b3aa1 | 833 | /* https://groups.google.com/forum/#!topic/comp.unix.programmer/JlTIncgY-vg */ |
lsm | 0:b21e585b3aa1 | 834 | int sigtimedwait(const sigset_t *set, siginfo_t *info, |
lsm | 0:b21e585b3aa1 | 835 | const struct timespec *timeout) { |
lsm | 0:b21e585b3aa1 | 836 | struct timespec elapsed = {0, 0}, rem; |
lsm | 0:b21e585b3aa1 | 837 | sigset_t pending; |
lsm | 0:b21e585b3aa1 | 838 | int signo; |
lsm | 0:b21e585b3aa1 | 839 | long ns; |
lsm | 0:b21e585b3aa1 | 840 | |
lsm | 0:b21e585b3aa1 | 841 | do { |
lsm | 0:b21e585b3aa1 | 842 | sigpending(&pending); /* doesn't clear pending queue */ |
lsm | 0:b21e585b3aa1 | 843 | for (signo = 1; signo < NSIG; signo++) { |
lsm | 0:b21e585b3aa1 | 844 | if (sigismember(set, signo) && sigismember(&pending, signo)) { |
lsm | 0:b21e585b3aa1 | 845 | if (info) { |
lsm | 0:b21e585b3aa1 | 846 | memset(info, 0, sizeof *info); |
lsm | 0:b21e585b3aa1 | 847 | info->si_signo = signo; |
lsm | 0:b21e585b3aa1 | 848 | } |
lsm | 0:b21e585b3aa1 | 849 | |
lsm | 0:b21e585b3aa1 | 850 | return signo; |
lsm | 0:b21e585b3aa1 | 851 | } |
lsm | 0:b21e585b3aa1 | 852 | } |
lsm | 0:b21e585b3aa1 | 853 | ns = 200000000L; /* 2/10th second */ |
lsm | 0:b21e585b3aa1 | 854 | nanosleep(&(struct timespec){0, ns}, &rem); |
lsm | 0:b21e585b3aa1 | 855 | ns -= rem.tv_nsec; |
lsm | 0:b21e585b3aa1 | 856 | elapsed.tv_sec += (elapsed.tv_nsec + ns) / 1000000000L; |
lsm | 0:b21e585b3aa1 | 857 | elapsed.tv_nsec = (elapsed.tv_nsec + ns) % 1000000000L; |
lsm | 0:b21e585b3aa1 | 858 | } while (elapsed.tv_sec < timeout->tv_sec || |
lsm | 0:b21e585b3aa1 | 859 | (elapsed.tv_sec == timeout->tv_sec && |
lsm | 0:b21e585b3aa1 | 860 | elapsed.tv_nsec < timeout->tv_nsec)); |
lsm | 0:b21e585b3aa1 | 861 | errno = EAGAIN; |
lsm | 0:b21e585b3aa1 | 862 | return -1; |
lsm | 0:b21e585b3aa1 | 863 | } |
lsm | 0:b21e585b3aa1 | 864 | #endif |
lsm | 0:b21e585b3aa1 | 865 | |
lsm | 0:b21e585b3aa1 | 866 | static struct { |
lsm | 0:b21e585b3aa1 | 867 | int can_rollback; |
lsm | 0:b21e585b3aa1 | 868 | FILE *ota_file; |
lsm | 0:b21e585b3aa1 | 869 | } s_ota = {0, NULL}; |
lsm | 0:b21e585b3aa1 | 870 | |
lsm | 0:b21e585b3aa1 | 871 | static const char *freshen_arg(int n) { |
lsm | 0:b21e585b3aa1 | 872 | #if defined(__linux__) |
lsm | 0:b21e585b3aa1 | 873 | static char cmdline[8192]; |
lsm | 0:b21e585b3aa1 | 874 | FILE *f = fopen("/proc/self/cmdline", "r"); |
lsm | 0:b21e585b3aa1 | 875 | if (f == NULL) { |
lsm | 0:b21e585b3aa1 | 876 | return NULL; |
lsm | 0:b21e585b3aa1 | 877 | } |
lsm | 0:b21e585b3aa1 | 878 | int sz = fread(cmdline, 1, sizeof(cmdline), f); |
lsm | 0:b21e585b3aa1 | 879 | if (sz < 0) { |
lsm | 0:b21e585b3aa1 | 880 | return NULL; |
lsm | 0:b21e585b3aa1 | 881 | } |
lsm | 0:b21e585b3aa1 | 882 | fclose(f); |
lsm | 0:b21e585b3aa1 | 883 | int i = 0; |
lsm | 0:b21e585b3aa1 | 884 | char *arg = &cmdline[0]; |
lsm | 0:b21e585b3aa1 | 885 | for (i = 0; i < n && *arg; i++) { |
lsm | 0:b21e585b3aa1 | 886 | arg = arg + strlen(arg) + 1; |
lsm | 0:b21e585b3aa1 | 887 | } |
lsm | 0:b21e585b3aa1 | 888 | if (*arg == '\0') { |
lsm | 0:b21e585b3aa1 | 889 | return NULL; |
lsm | 0:b21e585b3aa1 | 890 | } |
lsm | 0:b21e585b3aa1 | 891 | return arg; |
lsm | 0:b21e585b3aa1 | 892 | #elif defined(__APPLE__) |
lsm | 0:b21e585b3aa1 | 893 | extern int *_NSGetArgc(void); |
lsm | 0:b21e585b3aa1 | 894 | extern char ***_NSGetArgv(void); |
lsm | 0:b21e585b3aa1 | 895 | if (n < *_NSGetArgc()) { |
lsm | 0:b21e585b3aa1 | 896 | return (*_NSGetArgv())[n]; |
lsm | 0:b21e585b3aa1 | 897 | } |
lsm | 0:b21e585b3aa1 | 898 | return NULL; |
lsm | 0:b21e585b3aa1 | 899 | #endif |
lsm | 0:b21e585b3aa1 | 900 | } |
lsm | 0:b21e585b3aa1 | 901 | |
lsm | 0:b21e585b3aa1 | 902 | static void freshen_ota_restart(void) { |
lsm | 0:b21e585b3aa1 | 903 | FLOGI("called system restart.."); |
lsm | 0:b21e585b3aa1 | 904 | int exit_code = 0; |
lsm | 0:b21e585b3aa1 | 905 | struct stat st; |
lsm | 0:b21e585b3aa1 | 906 | if (strcmp(freshen_arg(0), FRESHEN_OTA_FILE) != 0 && |
lsm | 0:b21e585b3aa1 | 907 | stat(FRESHEN_OTA_FILE, &st) == 0) { |
lsm | 0:b21e585b3aa1 | 908 | FLOGI("update is available"); |
lsm | 0:b21e585b3aa1 | 909 | sigset_t mask; |
lsm | 0:b21e585b3aa1 | 910 | sigemptyset(&mask); |
lsm | 0:b21e585b3aa1 | 911 | sigaddset(&mask, SIGCHLD); |
lsm | 0:b21e585b3aa1 | 912 | sigaddset(&mask, SIGUSR1); |
lsm | 0:b21e585b3aa1 | 913 | if (sigprocmask(SIG_BLOCK, &mask, NULL) < 0) { |
lsm | 0:b21e585b3aa1 | 914 | FLOGE("sigprocmask(): errno=%d", errno); |
lsm | 0:b21e585b3aa1 | 915 | goto error; |
lsm | 0:b21e585b3aa1 | 916 | } |
lsm | 0:b21e585b3aa1 | 917 | int pid = fork(); |
lsm | 0:b21e585b3aa1 | 918 | FLOGI("pid=%d, ppid=%d", pid, getppid()); |
lsm | 0:b21e585b3aa1 | 919 | if (pid < 0) { |
lsm | 0:b21e585b3aa1 | 920 | FLOGE("fork(): errno=%d", errno); |
lsm | 0:b21e585b3aa1 | 921 | goto error; |
lsm | 0:b21e585b3aa1 | 922 | } |
lsm | 0:b21e585b3aa1 | 923 | if (pid == 0) { |
lsm | 0:b21e585b3aa1 | 924 | int argc = 0; |
lsm | 0:b21e585b3aa1 | 925 | for (; freshen_arg(argc) != NULL; argc++) |
lsm | 0:b21e585b3aa1 | 926 | ; |
lsm | 0:b21e585b3aa1 | 927 | char **argv = (char **) calloc(argc + 1, sizeof(char *)); |
lsm | 0:b21e585b3aa1 | 928 | if (argv == NULL) { |
lsm | 0:b21e585b3aa1 | 929 | exit(EXIT_FAILURE); |
lsm | 0:b21e585b3aa1 | 930 | } |
lsm | 0:b21e585b3aa1 | 931 | for (int i = 0; i < argc + 1; i++) { |
lsm | 0:b21e585b3aa1 | 932 | argv[i] = (char *) freshen_arg(i); |
lsm | 0:b21e585b3aa1 | 933 | } |
lsm | 0:b21e585b3aa1 | 934 | argv[0] = FRESHEN_OTA_FILE; |
lsm | 0:b21e585b3aa1 | 935 | chmod(FRESHEN_OTA_FILE, 0755); |
lsm | 0:b21e585b3aa1 | 936 | execv(FRESHEN_OTA_FILE, argv); |
lsm | 0:b21e585b3aa1 | 937 | exit(EXIT_FAILURE); |
lsm | 0:b21e585b3aa1 | 938 | } |
lsm | 0:b21e585b3aa1 | 939 | |
lsm | 0:b21e585b3aa1 | 940 | siginfo_t sig; |
lsm | 0:b21e585b3aa1 | 941 | int status; |
lsm | 0:b21e585b3aa1 | 942 | struct timespec t; |
lsm | 0:b21e585b3aa1 | 943 | t.tv_sec = 10; |
lsm | 0:b21e585b3aa1 | 944 | t.tv_nsec = 0; |
lsm | 0:b21e585b3aa1 | 945 | for (;;) { |
lsm | 0:b21e585b3aa1 | 946 | if (sigtimedwait(&mask, &sig, &t) < 0) { |
lsm | 0:b21e585b3aa1 | 947 | if (errno == EINTR) { |
lsm | 0:b21e585b3aa1 | 948 | FLOGI("interruped by another signal, continue"); |
lsm | 0:b21e585b3aa1 | 949 | continue; |
lsm | 0:b21e585b3aa1 | 950 | } else if (errno == EAGAIN) { |
lsm | 0:b21e585b3aa1 | 951 | FLOGE("timeout, killing child process"); |
lsm | 0:b21e585b3aa1 | 952 | kill(pid, SIGKILL); |
lsm | 0:b21e585b3aa1 | 953 | } else { |
lsm | 0:b21e585b3aa1 | 954 | FLOGE("sigtimedwait() failed: errno=%d", errno); |
lsm | 0:b21e585b3aa1 | 955 | } |
lsm | 0:b21e585b3aa1 | 956 | break; |
lsm | 0:b21e585b3aa1 | 957 | } |
lsm | 0:b21e585b3aa1 | 958 | if (sig.si_signo == SIGUSR1) { |
lsm | 0:b21e585b3aa1 | 959 | FLOGI("child process succeeded!"); |
lsm | 0:b21e585b3aa1 | 960 | rename(FRESHEN_OTA_FILE, freshen_arg(0)); |
lsm | 0:b21e585b3aa1 | 961 | } else { |
lsm | 0:b21e585b3aa1 | 962 | FLOGI("child process exited!"); |
lsm | 0:b21e585b3aa1 | 963 | } |
lsm | 0:b21e585b3aa1 | 964 | break; |
lsm | 0:b21e585b3aa1 | 965 | } |
lsm | 0:b21e585b3aa1 | 966 | if (waitpid(pid, &status, 0) < 0) { |
lsm | 0:b21e585b3aa1 | 967 | FLOGE("waitpid(): errno=%d", errno); |
lsm | 0:b21e585b3aa1 | 968 | return; |
lsm | 0:b21e585b3aa1 | 969 | } |
lsm | 0:b21e585b3aa1 | 970 | exit_code = (WEXITSTATUS(status)); |
lsm | 0:b21e585b3aa1 | 971 | FLOGI("child process exited with %d", exit_code); |
lsm | 0:b21e585b3aa1 | 972 | goto error; |
lsm | 0:b21e585b3aa1 | 973 | } |
lsm | 0:b21e585b3aa1 | 974 | return; |
lsm | 0:b21e585b3aa1 | 975 | error: |
lsm | 0:b21e585b3aa1 | 976 | unlink(FRESHEN_OTA_FILE); |
lsm | 0:b21e585b3aa1 | 977 | } |
lsm | 0:b21e585b3aa1 | 978 | |
lsm | 0:b21e585b3aa1 | 979 | static int freshen_ota_init(struct freshen_ctx *ctx) { |
lsm | 0:b21e585b3aa1 | 980 | FLOGI("argv0=%s", freshen_arg(0)); |
lsm | 0:b21e585b3aa1 | 981 | if (strcmp(freshen_arg(0), FRESHEN_OTA_FILE) == 0) { |
lsm | 0:b21e585b3aa1 | 982 | s_ota.can_rollback = 1; |
lsm | 0:b21e585b3aa1 | 983 | } |
lsm | 0:b21e585b3aa1 | 984 | return 0; |
lsm | 0:b21e585b3aa1 | 985 | (void) ctx; |
lsm | 0:b21e585b3aa1 | 986 | } |
lsm | 0:b21e585b3aa1 | 987 | |
lsm | 0:b21e585b3aa1 | 988 | static int freshen_ota_enabled(struct freshen_ctx *ctx) { |
lsm | 0:b21e585b3aa1 | 989 | return 1; |
lsm | 0:b21e585b3aa1 | 990 | (void) ctx; |
lsm | 0:b21e585b3aa1 | 991 | } |
lsm | 0:b21e585b3aa1 | 992 | |
lsm | 0:b21e585b3aa1 | 993 | static int freshen_ota_begin(struct freshen_ctx *ctx) { |
lsm | 0:b21e585b3aa1 | 994 | FLOGI("ctx: %p", (void *) ctx); |
lsm | 0:b21e585b3aa1 | 995 | if (s_ota.ota_file != NULL) { |
lsm | 0:b21e585b3aa1 | 996 | FLOGE("another OTA process is in-progress"); |
lsm | 0:b21e585b3aa1 | 997 | return -1; |
lsm | 0:b21e585b3aa1 | 998 | } |
lsm | 0:b21e585b3aa1 | 999 | s_ota.ota_file = fopen(FRESHEN_OTA_FILE, "wb"); |
lsm | 0:b21e585b3aa1 | 1000 | if (s_ota.ota_file == NULL) { |
lsm | 0:b21e585b3aa1 | 1001 | FLOGE("failed to open temporary file"); |
lsm | 0:b21e585b3aa1 | 1002 | return -1; |
lsm | 0:b21e585b3aa1 | 1003 | } |
lsm | 0:b21e585b3aa1 | 1004 | return 0; |
lsm | 0:b21e585b3aa1 | 1005 | (void) ctx; |
lsm | 0:b21e585b3aa1 | 1006 | } |
lsm | 0:b21e585b3aa1 | 1007 | |
lsm | 0:b21e585b3aa1 | 1008 | static int freshen_ota_end(struct freshen_ctx *ctx, int success) { |
lsm | 0:b21e585b3aa1 | 1009 | FLOGI("ctx: %p, success=%d", (void *) ctx, success); |
lsm | 0:b21e585b3aa1 | 1010 | if (s_ota.ota_file == NULL) { |
lsm | 0:b21e585b3aa1 | 1011 | FLOGE("ota_write: OTA process is not started"); |
lsm | 0:b21e585b3aa1 | 1012 | return -1; |
lsm | 0:b21e585b3aa1 | 1013 | } |
lsm | 0:b21e585b3aa1 | 1014 | fclose(s_ota.ota_file); |
lsm | 0:b21e585b3aa1 | 1015 | s_ota.ota_file = NULL; |
lsm | 0:b21e585b3aa1 | 1016 | if (!success) { |
lsm | 0:b21e585b3aa1 | 1017 | FLOGI("not succeeded, remove temporary file"); |
lsm | 0:b21e585b3aa1 | 1018 | unlink(FRESHEN_OTA_FILE); |
lsm | 0:b21e585b3aa1 | 1019 | } else { |
lsm | 0:b21e585b3aa1 | 1020 | freshen_ota_restart(); |
lsm | 0:b21e585b3aa1 | 1021 | } |
lsm | 0:b21e585b3aa1 | 1022 | return 0; |
lsm | 0:b21e585b3aa1 | 1023 | (void) ctx; |
lsm | 0:b21e585b3aa1 | 1024 | } |
lsm | 0:b21e585b3aa1 | 1025 | |
lsm | 0:b21e585b3aa1 | 1026 | static int freshen_ota_write(struct freshen_ctx *ctx, void *buf, size_t bufsz) { |
lsm | 0:b21e585b3aa1 | 1027 | FLOGI("ctx: %p, bufsz=%zu", (void *) ctx, bufsz); |
lsm | 0:b21e585b3aa1 | 1028 | if (s_ota.ota_file == NULL) { |
lsm | 0:b21e585b3aa1 | 1029 | FLOGE("OTA process is not started"); |
lsm | 0:b21e585b3aa1 | 1030 | return -1; |
lsm | 0:b21e585b3aa1 | 1031 | } |
lsm | 0:b21e585b3aa1 | 1032 | if (fwrite(buf, bufsz, 1, s_ota.ota_file) != 1) { |
lsm | 0:b21e585b3aa1 | 1033 | FLOGE("failed to write chunk"); |
lsm | 0:b21e585b3aa1 | 1034 | return -1; |
lsm | 0:b21e585b3aa1 | 1035 | } |
lsm | 0:b21e585b3aa1 | 1036 | return 0; |
lsm | 0:b21e585b3aa1 | 1037 | (void) ctx; |
lsm | 0:b21e585b3aa1 | 1038 | } |
lsm | 0:b21e585b3aa1 | 1039 | |
lsm | 0:b21e585b3aa1 | 1040 | static int freshen_ota_commit(struct freshen_ctx *ctx) { |
lsm | 0:b21e585b3aa1 | 1041 | if (!s_ota.can_rollback) { |
lsm | 0:b21e585b3aa1 | 1042 | return 0; |
lsm | 0:b21e585b3aa1 | 1043 | } |
lsm | 0:b21e585b3aa1 | 1044 | FLOGI("ctx: %p, argv0=%s", (void *) ctx, freshen_arg(0)); |
lsm | 0:b21e585b3aa1 | 1045 | s_ota.can_rollback = 0; |
lsm | 0:b21e585b3aa1 | 1046 | return kill(getppid(), SIGUSR1); |
lsm | 0:b21e585b3aa1 | 1047 | (void) ctx; |
lsm | 0:b21e585b3aa1 | 1048 | } |
lsm | 0:b21e585b3aa1 | 1049 | #else |
lsm | 0:b21e585b3aa1 | 1050 | |
lsm | 0:b21e585b3aa1 | 1051 | static int freshen_ota_enabled(struct freshen_ctx *ctx) { |
lsm | 0:b21e585b3aa1 | 1052 | (void) ctx; |
lsm | 0:b21e585b3aa1 | 1053 | return 0; |
lsm | 0:b21e585b3aa1 | 1054 | } |
lsm | 0:b21e585b3aa1 | 1055 | |
lsm | 0:b21e585b3aa1 | 1056 | static int freshen_ota_init(struct freshen_ctx *ctx) { |
lsm | 0:b21e585b3aa1 | 1057 | (void) ctx; |
lsm | 0:b21e585b3aa1 | 1058 | return 0; |
lsm | 0:b21e585b3aa1 | 1059 | } |
lsm | 0:b21e585b3aa1 | 1060 | |
lsm | 0:b21e585b3aa1 | 1061 | static int freshen_ota_commit(struct freshen_ctx *ctx) { |
lsm | 0:b21e585b3aa1 | 1062 | (void) ctx; |
lsm | 0:b21e585b3aa1 | 1063 | return 0; |
lsm | 0:b21e585b3aa1 | 1064 | } |
lsm | 0:b21e585b3aa1 | 1065 | |
lsm | 0:b21e585b3aa1 | 1066 | #endif |
lsm | 0:b21e585b3aa1 | 1067 | |
lsm | 0:b21e585b3aa1 | 1068 | #if defined(FRESHEN_ENABLE_DASH) |
lsm | 0:b21e585b3aa1 | 1069 | #define FRESHEN_ENABLE_MBEDTLS |
lsm | 0:b21e585b3aa1 | 1070 | |
lsm | 0:b21e585b3aa1 | 1071 | #include <errno.h> |
lsm | 0:b21e585b3aa1 | 1072 | #include <stdarg.h> |
lsm | 0:b21e585b3aa1 | 1073 | #include <stdbool.h> |
lsm | 0:b21e585b3aa1 | 1074 | #include <stdint.h> |
lsm | 0:b21e585b3aa1 | 1075 | #include <stdio.h> |
lsm | 0:b21e585b3aa1 | 1076 | #include <stdlib.h> |
lsm | 0:b21e585b3aa1 | 1077 | #include <string.h> |
lsm | 0:b21e585b3aa1 | 1078 | #include <time.h> |
lsm | 0:b21e585b3aa1 | 1079 | |
lsm | 0:b21e585b3aa1 | 1080 | #if defined(__unix__) || defined(__APPLE__) |
lsm | 0:b21e585b3aa1 | 1081 | #define FRESHEN_ENABLE_SOCKET |
lsm | 0:b21e585b3aa1 | 1082 | #include <signal.h> |
lsm | 0:b21e585b3aa1 | 1083 | #include <unistd.h> |
lsm | 0:b21e585b3aa1 | 1084 | #endif |
lsm | 0:b21e585b3aa1 | 1085 | |
lsm | 0:b21e585b3aa1 | 1086 | #if defined(FRESHEN_ENABLE_SOCKET) |
lsm | 0:b21e585b3aa1 | 1087 | #include <netdb.h> |
lsm | 0:b21e585b3aa1 | 1088 | #include <sys/select.h> |
lsm | 0:b21e585b3aa1 | 1089 | #include <sys/socket.h> |
lsm | 0:b21e585b3aa1 | 1090 | #endif |
lsm | 0:b21e585b3aa1 | 1091 | |
lsm | 0:b21e585b3aa1 | 1092 | #if defined(MBED_LIBRARY_VERSION) |
lsm | 0:b21e585b3aa1 | 1093 | #include "mbed.h" |
lsm | 0:b21e585b3aa1 | 1094 | #endif |
lsm | 0:b21e585b3aa1 | 1095 | |
lsm | 0:b21e585b3aa1 | 1096 | #if defined(FRESHEN_ENABLE_MBEDTLS) |
lsm | 0:b21e585b3aa1 | 1097 | #include "mbedtls/ctr_drbg.h" |
lsm | 0:b21e585b3aa1 | 1098 | #include "mbedtls/entropy.h" |
lsm | 0:b21e585b3aa1 | 1099 | #include "mbedtls/net.h" |
lsm | 0:b21e585b3aa1 | 1100 | #include "mbedtls/ssl.h" |
lsm | 0:b21e585b3aa1 | 1101 | #endif |
lsm | 0:b21e585b3aa1 | 1102 | |
lsm | 0:b21e585b3aa1 | 1103 | struct conn { |
lsm | 0:b21e585b3aa1 | 1104 | int sock; |
lsm | 0:b21e585b3aa1 | 1105 | bool is_tls; |
lsm | 0:b21e585b3aa1 | 1106 | bool is_ws; |
lsm | 0:b21e585b3aa1 | 1107 | |
lsm | 0:b21e585b3aa1 | 1108 | #if defined(MBED_LIBRARY_VERSION) |
lsm | 0:b21e585b3aa1 | 1109 | TCPSocket *mbedsock; |
lsm | 0:b21e585b3aa1 | 1110 | #endif |
lsm | 0:b21e585b3aa1 | 1111 | |
lsm | 0:b21e585b3aa1 | 1112 | #if defined(FRESHEN_ENABLE_MBEDTLS) |
lsm | 0:b21e585b3aa1 | 1113 | mbedtls_net_context server_fd; |
lsm | 0:b21e585b3aa1 | 1114 | mbedtls_entropy_context entropy; |
lsm | 0:b21e585b3aa1 | 1115 | mbedtls_ctr_drbg_context drbg; |
lsm | 0:b21e585b3aa1 | 1116 | mbedtls_ssl_context ssl; |
lsm | 0:b21e585b3aa1 | 1117 | mbedtls_ssl_config conf; |
lsm | 0:b21e585b3aa1 | 1118 | mbedtls_x509_crt cacert; |
lsm | 0:b21e585b3aa1 | 1119 | #endif |
lsm | 0:b21e585b3aa1 | 1120 | }; |
lsm | 0:b21e585b3aa1 | 1121 | |
lsm | 0:b21e585b3aa1 | 1122 | static void freshen_net_init_conn(struct conn *c) { |
lsm | 0:b21e585b3aa1 | 1123 | memset(c, 0, sizeof(*c)); |
lsm | 0:b21e585b3aa1 | 1124 | c->sock = -1; |
lsm | 0:b21e585b3aa1 | 1125 | } |
lsm | 0:b21e585b3aa1 | 1126 | |
lsm | 0:b21e585b3aa1 | 1127 | static bool freshen_net_is_disconnected(const struct conn *c) { |
lsm | 0:b21e585b3aa1 | 1128 | #if defined(MBED_LIBRARY_VERSION) |
lsm | 0:b21e585b3aa1 | 1129 | return c->mbedsock == NULL; |
lsm | 0:b21e585b3aa1 | 1130 | #else |
lsm | 0:b21e585b3aa1 | 1131 | return c->sock == -1; |
lsm | 0:b21e585b3aa1 | 1132 | #endif |
lsm | 0:b21e585b3aa1 | 1133 | } |
lsm | 0:b21e585b3aa1 | 1134 | |
lsm | 0:b21e585b3aa1 | 1135 | static int freshen_net_recv(struct conn *c, void *buf, size_t len) { |
lsm | 0:b21e585b3aa1 | 1136 | int n = 0; |
lsm | 0:b21e585b3aa1 | 1137 | |
lsm | 0:b21e585b3aa1 | 1138 | #if defined(FRESHEN_ENABLE_SOCKET) |
lsm | 0:b21e585b3aa1 | 1139 | /* If socket has no data, return immediately */ |
lsm | 0:b21e585b3aa1 | 1140 | struct timeval tv = {0, 0}; |
lsm | 0:b21e585b3aa1 | 1141 | fd_set rset; |
lsm | 0:b21e585b3aa1 | 1142 | FD_ZERO(&rset); |
lsm | 0:b21e585b3aa1 | 1143 | FD_SET(c->sock, &rset); |
lsm | 0:b21e585b3aa1 | 1144 | if (select(c->sock + 1, &rset, NULL, NULL, &tv) != 1) return n; |
lsm | 0:b21e585b3aa1 | 1145 | #endif |
lsm | 0:b21e585b3aa1 | 1146 | |
lsm | 0:b21e585b3aa1 | 1147 | #if defined(FRESHEN_ENABLE_MBEDTLS) |
lsm | 0:b21e585b3aa1 | 1148 | if (c->is_tls) { |
lsm | 0:b21e585b3aa1 | 1149 | n = mbedtls_ssl_read(&c->ssl, (unsigned char *) buf, len); |
lsm | 0:b21e585b3aa1 | 1150 | } else |
lsm | 0:b21e585b3aa1 | 1151 | #endif |
lsm | 0:b21e585b3aa1 | 1152 | { |
lsm | 0:b21e585b3aa1 | 1153 | #if defined(FRESHEN_ENABLE_SOCKET) |
lsm | 0:b21e585b3aa1 | 1154 | n = recv(c->sock, buf, len, 0); |
lsm | 0:b21e585b3aa1 | 1155 | #endif |
lsm | 0:b21e585b3aa1 | 1156 | } |
lsm | 0:b21e585b3aa1 | 1157 | return n <= 0 ? -1 : n; |
lsm | 0:b21e585b3aa1 | 1158 | } |
lsm | 0:b21e585b3aa1 | 1159 | |
lsm | 0:b21e585b3aa1 | 1160 | static int freshen_net_send(struct conn *c, const void *buf, size_t len) { |
lsm | 0:b21e585b3aa1 | 1161 | #if defined(FRESHEN_ENABLE_MBEDTLS) |
lsm | 0:b21e585b3aa1 | 1162 | if (c->is_tls) { |
lsm | 0:b21e585b3aa1 | 1163 | return mbedtls_ssl_write(&c->ssl, (const unsigned char *) buf, len); |
lsm | 0:b21e585b3aa1 | 1164 | } else |
lsm | 0:b21e585b3aa1 | 1165 | #endif |
lsm | 0:b21e585b3aa1 | 1166 | { |
lsm | 0:b21e585b3aa1 | 1167 | #if defined(FRESHEN_ENABLE_SOCKET) |
lsm | 0:b21e585b3aa1 | 1168 | return send(c->sock, buf, len, 0); |
lsm | 0:b21e585b3aa1 | 1169 | #endif |
lsm | 0:b21e585b3aa1 | 1170 | } |
lsm | 0:b21e585b3aa1 | 1171 | } |
lsm | 0:b21e585b3aa1 | 1172 | |
lsm | 0:b21e585b3aa1 | 1173 | static void freshen_net_close(struct conn *c) { |
lsm | 0:b21e585b3aa1 | 1174 | #if defined(MBED_LIBRARY_VERSION) |
lsm | 0:b21e585b3aa1 | 1175 | if (c->mbedsock != NULL) c->mbedsock->close(); |
lsm | 0:b21e585b3aa1 | 1176 | c->mbedsock = NULL; |
lsm | 0:b21e585b3aa1 | 1177 | FLOGI("closed mbedsock"); |
lsm | 0:b21e585b3aa1 | 1178 | #endif |
lsm | 0:b21e585b3aa1 | 1179 | |
lsm | 0:b21e585b3aa1 | 1180 | #if defined(FRESHEN_ENABLE_MBEDTLS) |
lsm | 0:b21e585b3aa1 | 1181 | if (c->is_tls) { |
lsm | 0:b21e585b3aa1 | 1182 | #if defined(FRESHEN_ENABLE_SOCKET) |
lsm | 0:b21e585b3aa1 | 1183 | mbedtls_net_free(&c->server_fd); |
lsm | 0:b21e585b3aa1 | 1184 | #endif |
lsm | 0:b21e585b3aa1 | 1185 | mbedtls_ssl_free(&c->ssl); |
lsm | 0:b21e585b3aa1 | 1186 | mbedtls_ssl_config_free(&c->conf); |
lsm | 0:b21e585b3aa1 | 1187 | mbedtls_ctr_drbg_free(&c->drbg); |
lsm | 0:b21e585b3aa1 | 1188 | mbedtls_entropy_free(&c->entropy); |
lsm | 0:b21e585b3aa1 | 1189 | mbedtls_x509_crt_free(&c->cacert); |
lsm | 0:b21e585b3aa1 | 1190 | } |
lsm | 0:b21e585b3aa1 | 1191 | #endif |
lsm | 0:b21e585b3aa1 | 1192 | |
lsm | 0:b21e585b3aa1 | 1193 | if (c->sock != -1) close(c->sock); |
lsm | 0:b21e585b3aa1 | 1194 | c->sock = -1; |
lsm | 0:b21e585b3aa1 | 1195 | c->is_ws = false; |
lsm | 0:b21e585b3aa1 | 1196 | } |
lsm | 0:b21e585b3aa1 | 1197 | |
lsm | 0:b21e585b3aa1 | 1198 | #if defined(MBED_LIBRARY_VERSION) |
lsm | 0:b21e585b3aa1 | 1199 | NetworkInterface *freshen_net; |
lsm | 0:b21e585b3aa1 | 1200 | |
lsm | 0:b21e585b3aa1 | 1201 | static int tls_net_recv(void *ctx, unsigned char *buf, size_t len) { |
lsm | 0:b21e585b3aa1 | 1202 | TCPSocket *socket = static_cast<TCPSocket *>(ctx); |
lsm | 0:b21e585b3aa1 | 1203 | int n = socket->recv(buf, len); |
lsm | 0:b21e585b3aa1 | 1204 | return n; |
lsm | 0:b21e585b3aa1 | 1205 | } |
lsm | 0:b21e585b3aa1 | 1206 | |
lsm | 0:b21e585b3aa1 | 1207 | static int tls_net_send(void *ctx, const unsigned char *buf, size_t len) { |
lsm | 0:b21e585b3aa1 | 1208 | TCPSocket *socket = static_cast<TCPSocket *>(ctx); |
lsm | 0:b21e585b3aa1 | 1209 | int n = socket->send(buf, len); |
lsm | 0:b21e585b3aa1 | 1210 | return n; |
lsm | 0:b21e585b3aa1 | 1211 | } |
lsm | 0:b21e585b3aa1 | 1212 | |
lsm | 0:b21e585b3aa1 | 1213 | void mbedtls_net_init(mbedtls_net_context *ctx) { |
lsm | 0:b21e585b3aa1 | 1214 | (void) ctx; |
lsm | 0:b21e585b3aa1 | 1215 | } |
lsm | 0:b21e585b3aa1 | 1216 | |
lsm | 0:b21e585b3aa1 | 1217 | void mbedtls_net_free(mbedtls_net_context *ctx) { |
lsm | 0:b21e585b3aa1 | 1218 | (void) ctx; |
lsm | 0:b21e585b3aa1 | 1219 | } |
lsm | 0:b21e585b3aa1 | 1220 | |
lsm | 0:b21e585b3aa1 | 1221 | int mbedtls_hardware_poll(void *data, unsigned char *output, size_t len, |
lsm | 0:b21e585b3aa1 | 1222 | size_t *olen) { |
lsm | 0:b21e585b3aa1 | 1223 | memset(output, 0, len); |
lsm | 0:b21e585b3aa1 | 1224 | return 0; |
lsm | 0:b21e585b3aa1 | 1225 | } |
lsm | 0:b21e585b3aa1 | 1226 | #endif |
lsm | 0:b21e585b3aa1 | 1227 | |
lsm | 0:b21e585b3aa1 | 1228 | static bool fwsconnect(struct conn *c, const char *host, const char *port, |
lsm | 0:b21e585b3aa1 | 1229 | const char *uri, const char *token) { |
lsm | 0:b21e585b3aa1 | 1230 | int n, sent; |
lsm | 0:b21e585b3aa1 | 1231 | char buf[512]; |
lsm | 0:b21e585b3aa1 | 1232 | if ((n = snprintf(buf, sizeof(buf), |
lsm | 0:b21e585b3aa1 | 1233 | "GET %s HTTP/1.1\r\n" |
lsm | 0:b21e585b3aa1 | 1234 | "Host: %s:%s\r\n" |
lsm | 0:b21e585b3aa1 | 1235 | "Authorization: Bearer %s\r\n" |
lsm | 0:b21e585b3aa1 | 1236 | "Sec-WebSocket-Version: 13\r\n" |
lsm | 0:b21e585b3aa1 | 1237 | "Sec-WebSocket-Key: p0EAAPE61hDZrLdgKgy1Og==\r\n" |
lsm | 0:b21e585b3aa1 | 1238 | "Sec-WebSocket-Protocol: dash.freshen.cc\r\n" |
lsm | 0:b21e585b3aa1 | 1239 | "Upgrade: websocket\r\n" |
lsm | 0:b21e585b3aa1 | 1240 | "Connection: Upgrade\r\n" |
lsm | 0:b21e585b3aa1 | 1241 | "Origin: http://%s\r\n" |
lsm | 0:b21e585b3aa1 | 1242 | "\r\n", |
lsm | 0:b21e585b3aa1 | 1243 | uri, host, port, token, host)) > (int) sizeof(buf) || |
lsm | 0:b21e585b3aa1 | 1244 | n < 0) { |
lsm | 0:b21e585b3aa1 | 1245 | FLOGE("snprintf: %d", n); |
lsm | 0:b21e585b3aa1 | 1246 | } else if ((sent = freshen_net_send(c, buf, n)) != n) { |
lsm | 0:b21e585b3aa1 | 1247 | FLOGE("send(%d) = %d : %d", n, sent, errno); |
lsm | 0:b21e585b3aa1 | 1248 | } else { |
lsm | 0:b21e585b3aa1 | 1249 | return true; |
lsm | 0:b21e585b3aa1 | 1250 | } |
lsm | 0:b21e585b3aa1 | 1251 | freshen_net_close(c); |
lsm | 0:b21e585b3aa1 | 1252 | return false; |
lsm | 0:b21e585b3aa1 | 1253 | } |
lsm | 0:b21e585b3aa1 | 1254 | |
lsm | 0:b21e585b3aa1 | 1255 | static bool freconnect(const char *url, const char *token, struct conn *c) { |
lsm | 0:b21e585b3aa1 | 1256 | #if defined(FRESHEN_ENABLE_SOCKET) |
lsm | 0:b21e585b3aa1 | 1257 | struct sockaddr_in sin; |
lsm | 0:b21e585b3aa1 | 1258 | struct hostent *he; |
lsm | 0:b21e585b3aa1 | 1259 | #endif |
lsm | 0:b21e585b3aa1 | 1260 | char proto[10], host[100], port[10] = "80", uri[20]; |
lsm | 0:b21e585b3aa1 | 1261 | int sock; |
lsm | 0:b21e585b3aa1 | 1262 | |
lsm | 0:b21e585b3aa1 | 1263 | if (!freshen_net_is_disconnected(c)) return true; |
lsm | 0:b21e585b3aa1 | 1264 | |
lsm | 0:b21e585b3aa1 | 1265 | sock = c->sock = -1; |
lsm | 0:b21e585b3aa1 | 1266 | proto[0] = host[0] = port[0] = uri[0] = '\0'; |
lsm | 0:b21e585b3aa1 | 1267 | if (sscanf(url, "%9[^:]://%99[^:]:%9[0-9]%19s", proto, host, port, uri) != |
lsm | 0:b21e585b3aa1 | 1268 | 4) { |
lsm | 0:b21e585b3aa1 | 1269 | if (strcmp(proto, "wss") == 0) strcpy(port, "443"); |
lsm | 0:b21e585b3aa1 | 1270 | sscanf(url, "%9[^:]://%99[^/]%19s", proto, host, uri); |
lsm | 0:b21e585b3aa1 | 1271 | } |
lsm | 0:b21e585b3aa1 | 1272 | c->is_tls = strcmp(proto, "wss") == 0 ? true : false; |
lsm | 0:b21e585b3aa1 | 1273 | FLOGI("dst [%s][%s][%s][%s]", proto, host, port, uri); |
lsm | 0:b21e585b3aa1 | 1274 | |
lsm | 0:b21e585b3aa1 | 1275 | #if defined(FRESHEN_ENABLE_SOCKET) |
lsm | 0:b21e585b3aa1 | 1276 | memset(&sin, 0, sizeof(sin)); |
lsm | 0:b21e585b3aa1 | 1277 | sin.sin_family = AF_INET; |
lsm | 0:b21e585b3aa1 | 1278 | sin.sin_port = htons((uint16_t) atoi(port)); |
lsm | 0:b21e585b3aa1 | 1279 | if ((he = gethostbyname(host)) == NULL) { |
lsm | 0:b21e585b3aa1 | 1280 | FLOGE("gethostbyname(%s): %d", host, errno); |
lsm | 0:b21e585b3aa1 | 1281 | } else if (!memcpy(&sin.sin_addr, he->h_addr_list[0], sizeof(sin.sin_addr))) { |
lsm | 0:b21e585b3aa1 | 1282 | } else if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1) { |
lsm | 0:b21e585b3aa1 | 1283 | FLOGE("socket: %d", errno); |
lsm | 0:b21e585b3aa1 | 1284 | } else if (connect(sock, (struct sockaddr *) &sin, sizeof(sin)) != 0) { |
lsm | 0:b21e585b3aa1 | 1285 | FLOGE("connect: %d", errno); |
lsm | 0:b21e585b3aa1 | 1286 | close(sock); |
lsm | 0:b21e585b3aa1 | 1287 | } else if (!c->is_tls) { |
lsm | 0:b21e585b3aa1 | 1288 | c->sock = sock; |
lsm | 0:b21e585b3aa1 | 1289 | return true; |
lsm | 0:b21e585b3aa1 | 1290 | } else { |
lsm | 0:b21e585b3aa1 | 1291 | #elif defined(MBED_LIBRARY_VERSION) |
lsm | 0:b21e585b3aa1 | 1292 | if (freshen_net == NULL) { |
lsm | 0:b21e585b3aa1 | 1293 | FLOGE("freshen_net pointer is not set"); |
lsm | 0:b21e585b3aa1 | 1294 | } else if ((c->mbedsock = new TCPSocket(freshen_net)) == NULL) { |
lsm | 0:b21e585b3aa1 | 1295 | FLOGE("cant create tcp socket"); |
lsm | 0:b21e585b3aa1 | 1296 | } else if (c->mbedsock->connect(host, atoi(port)) != NSAPI_ERROR_OK) { |
lsm | 0:b21e585b3aa1 | 1297 | FLOGE("mbed connect(%s:%d): %d", host, atoi(port), errno); |
lsm | 0:b21e585b3aa1 | 1298 | } else { |
lsm | 0:b21e585b3aa1 | 1299 | #endif |
lsm | 0:b21e585b3aa1 | 1300 | #if defined(FRESHEN_ENABLE_MBEDTLS) |
lsm | 0:b21e585b3aa1 | 1301 | |
lsm | 0:b21e585b3aa1 | 1302 | #if !defined(FRESHEN_CA_PEM) |
lsm | 0:b21e585b3aa1 | 1303 | #define FRESHEN_CA_PEM \ |
lsm | 0:b21e585b3aa1 | 1304 | "-----BEGIN CERTIFICATE-----\n" \ |
lsm | 0:b21e585b3aa1 | 1305 | "MIIEkjCCA3qgAwIBAgIQCgFBQgAAAVOFc2oLheynCDANBgkqhkiG9w0BAQsFADA/\n" \ |
lsm | 0:b21e585b3aa1 | 1306 | "MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT\n" \ |
lsm | 0:b21e585b3aa1 | 1307 | "DkRTVCBSb290IENBIFgzMB4XDTE2MDMxNzE2NDA0NloXDTIxMDMxNzE2NDA0Nlow\n" \ |
lsm | 0:b21e585b3aa1 | 1308 | "SjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUxldCdzIEVuY3J5cHQxIzAhBgNVBAMT\n" \ |
lsm | 0:b21e585b3aa1 | 1309 | "GkxldCdzIEVuY3J5cHQgQXV0aG9yaXR5IFgzMIIBIjANBgkqhkiG9w0BAQEFAAOC\n" \ |
lsm | 0:b21e585b3aa1 | 1310 | "AQ8AMIIBCgKCAQEAnNMM8FrlLke3cl03g7NoYzDq1zUmGSXhvb418XCSL7e4S0EF\n" \ |
lsm | 0:b21e585b3aa1 | 1311 | "q6meNQhY7LEqxGiHC6PjdeTm86dicbp5gWAf15Gan/PQeGdxyGkOlZHP/uaZ6WA8\n" \ |
lsm | 0:b21e585b3aa1 | 1312 | "SMx+yk13EiSdRxta67nsHjcAHJyse6cF6s5K671B5TaYucv9bTyWaN8jKkKQDIZ0\n" \ |
lsm | 0:b21e585b3aa1 | 1313 | "Z8h/pZq4UmEUEz9l6YKHy9v6Dlb2honzhT+Xhq+w3Brvaw2VFn3EK6BlspkENnWA\n" \ |
lsm | 0:b21e585b3aa1 | 1314 | "a6xK8xuQSXgvopZPKiAlKQTGdMDQMc2PMTiVFrqoM7hD8bEfwzB/onkxEz0tNvjj\n" \ |
lsm | 0:b21e585b3aa1 | 1315 | "/PIzark5McWvxI0NHWQWM6r6hCm21AvA2H3DkwIDAQABo4IBfTCCAXkwEgYDVR0T\n" \ |
lsm | 0:b21e585b3aa1 | 1316 | "AQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwfwYIKwYBBQUHAQEEczBxMDIG\n" \ |
lsm | 0:b21e585b3aa1 | 1317 | "CCsGAQUFBzABhiZodHRwOi8vaXNyZy50cnVzdGlkLm9jc3AuaWRlbnRydXN0LmNv\n" \ |
lsm | 0:b21e585b3aa1 | 1318 | "bTA7BggrBgEFBQcwAoYvaHR0cDovL2FwcHMuaWRlbnRydXN0LmNvbS9yb290cy9k\n" \ |
lsm | 0:b21e585b3aa1 | 1319 | "c3Ryb290Y2F4My5wN2MwHwYDVR0jBBgwFoAUxKexpHsscfrb4UuQdf/EFWCFiRAw\n" \ |
lsm | 0:b21e585b3aa1 | 1320 | "VAYDVR0gBE0wSzAIBgZngQwBAgEwPwYLKwYBBAGC3xMBAQEwMDAuBggrBgEFBQcC\n" \ |
lsm | 0:b21e585b3aa1 | 1321 | "ARYiaHR0cDovL2Nwcy5yb290LXgxLmxldHNlbmNyeXB0Lm9yZzA8BgNVHR8ENTAz\n" \ |
lsm | 0:b21e585b3aa1 | 1322 | "MDGgL6AthitodHRwOi8vY3JsLmlkZW50cnVzdC5jb20vRFNUUk9PVENBWDNDUkwu\n" \ |
lsm | 0:b21e585b3aa1 | 1323 | "Y3JsMB0GA1UdDgQWBBSoSmpjBH3duubRObemRWXv86jsoTANBgkqhkiG9w0BAQsF\n" \ |
lsm | 0:b21e585b3aa1 | 1324 | "AAOCAQEA3TPXEfNjWDjdGBX7CVW+dla5cEilaUcne8IkCJLxWh9KEik3JHRRHGJo\n" \ |
lsm | 0:b21e585b3aa1 | 1325 | "uM2VcGfl96S8TihRzZvoroed6ti6WqEBmtzw3Wodatg+VyOeph4EYpr/1wXKtx8/\n" \ |
lsm | 0:b21e585b3aa1 | 1326 | "wApIvJSwtmVi4MFU5aMqrSDE6ea73Mj2tcMyo5jMd6jmeWUHK8so/joWUoHOUgwu\n" \ |
lsm | 0:b21e585b3aa1 | 1327 | "X4Po1QYz+3dszkDqMp4fklxBwXRsW10KXzPMTZ+sOPAveyxindmjkW8lGy+QsRlG\n" \ |
lsm | 0:b21e585b3aa1 | 1328 | "PfZ+G6Z6h7mjem0Y+iWlkYcV4PIWL1iwBi8saCbGS5jN2p8M+X+Q7UNKEkROb3N6\n" \ |
lsm | 0:b21e585b3aa1 | 1329 | "KOqkqm57TH2H3eDJAkSnh6/DNFu0Qg==\n" \ |
lsm | 0:b21e585b3aa1 | 1330 | "-----END CERTIFICATE-----" |
lsm | 0:b21e585b3aa1 | 1331 | #endif |
lsm | 0:b21e585b3aa1 | 1332 | |
lsm | 0:b21e585b3aa1 | 1333 | const char *ca_pem = FRESHEN_CA_PEM; |
lsm | 0:b21e585b3aa1 | 1334 | int res; |
lsm | 0:b21e585b3aa1 | 1335 | mbedtls_ssl_init(&c->ssl); |
lsm | 0:b21e585b3aa1 | 1336 | mbedtls_ssl_config_init(&c->conf); |
lsm | 0:b21e585b3aa1 | 1337 | mbedtls_entropy_init(&c->entropy); |
lsm | 0:b21e585b3aa1 | 1338 | mbedtls_ctr_drbg_init(&c->drbg); |
lsm | 0:b21e585b3aa1 | 1339 | mbedtls_x509_crt_init(&c->cacert); |
lsm | 0:b21e585b3aa1 | 1340 | mbedtls_ctr_drbg_seed(&c->drbg, mbedtls_entropy_func, &c->entropy, 0, 0); |
lsm | 0:b21e585b3aa1 | 1341 | mbedtls_ssl_config_defaults(&c->conf, MBEDTLS_SSL_IS_CLIENT, |
lsm | 0:b21e585b3aa1 | 1342 | MBEDTLS_SSL_TRANSPORT_STREAM, |
lsm | 0:b21e585b3aa1 | 1343 | MBEDTLS_SSL_PRESET_DEFAULT); |
lsm | 0:b21e585b3aa1 | 1344 | mbedtls_ssl_conf_rng(&c->conf, mbedtls_ctr_drbg_random, &c->drbg); |
lsm | 0:b21e585b3aa1 | 1345 | res = mbedtls_x509_crt_parse(&c->cacert, (const unsigned char *) ca_pem, |
lsm | 0:b21e585b3aa1 | 1346 | strlen(ca_pem) + 1); /* must contain \0 */ |
lsm | 0:b21e585b3aa1 | 1347 | if (res != 0) FLOGE("crt_parse: -%#x", -res); |
lsm | 0:b21e585b3aa1 | 1348 | mbedtls_ssl_conf_ca_chain(&c->conf, &c->cacert, NULL); |
lsm | 0:b21e585b3aa1 | 1349 | mbedtls_ssl_conf_authmode(&c->conf, MBEDTLS_SSL_VERIFY_REQUIRED); |
lsm | 0:b21e585b3aa1 | 1350 | |
lsm | 0:b21e585b3aa1 | 1351 | res = mbedtls_ssl_setup(&c->ssl, &c->conf); |
lsm | 0:b21e585b3aa1 | 1352 | if (res != 0) FLOGE("ssl_setup: -%#x", -res); |
lsm | 0:b21e585b3aa1 | 1353 | mbedtls_ssl_set_hostname(&c->ssl, host); |
lsm | 0:b21e585b3aa1 | 1354 | #if defined(FRESHEN_ENABLE_SOCKET) |
lsm | 0:b21e585b3aa1 | 1355 | mbedtls_net_init(&c->server_fd); |
lsm | 0:b21e585b3aa1 | 1356 | c->server_fd.fd = c->sock = sock; |
lsm | 0:b21e585b3aa1 | 1357 | mbedtls_ssl_set_bio(&c->ssl, &c->server_fd, mbedtls_net_send, |
lsm | 0:b21e585b3aa1 | 1358 | mbedtls_net_recv, NULL); |
lsm | 0:b21e585b3aa1 | 1359 | #elif defined(MBED_LIBRARY_VERSION) |
lsm | 0:b21e585b3aa1 | 1360 | mbedtls_ssl_set_bio(&c->ssl, static_cast<void *>(c->mbedsock), tls_net_send, |
lsm | 0:b21e585b3aa1 | 1361 | tls_net_recv, NULL); |
lsm | 0:b21e585b3aa1 | 1362 | #endif |
lsm | 0:b21e585b3aa1 | 1363 | if ((res = mbedtls_ssl_handshake(&c->ssl)) == 0) { |
lsm | 0:b21e585b3aa1 | 1364 | return fwsconnect(c, host, port, uri, token); |
lsm | 0:b21e585b3aa1 | 1365 | } |
lsm | 0:b21e585b3aa1 | 1366 | FLOGE("handshake error: -%#x", -res); |
lsm | 0:b21e585b3aa1 | 1367 | #else |
lsm | 0:b21e585b3aa1 | 1368 | FLOGE("TLS support is not enabled"); |
lsm | 0:b21e585b3aa1 | 1369 | #endif |
lsm | 0:b21e585b3aa1 | 1370 | } |
lsm | 0:b21e585b3aa1 | 1371 | |
lsm | 0:b21e585b3aa1 | 1372 | freshen_net_close(c); |
lsm | 0:b21e585b3aa1 | 1373 | return false; |
lsm | 0:b21e585b3aa1 | 1374 | } |
lsm | 0:b21e585b3aa1 | 1375 | |
lsm | 0:b21e585b3aa1 | 1376 | static void freshen_sleep(int seconds) { |
lsm | 0:b21e585b3aa1 | 1377 | #if defined(MBED_LIBRARY_VERSION) |
lsm | 0:b21e585b3aa1 | 1378 | wait(seconds); |
lsm | 0:b21e585b3aa1 | 1379 | #else |
lsm | 0:b21e585b3aa1 | 1380 | sleep(seconds); |
lsm | 0:b21e585b3aa1 | 1381 | #endif |
lsm | 0:b21e585b3aa1 | 1382 | } |
lsm | 0:b21e585b3aa1 | 1383 | #endif |
lsm | 0:b21e585b3aa1 | 1384 | #if defined(FRESHEN_ENABLE_DASH) |
lsm | 0:b21e585b3aa1 | 1385 | #define FRESHEN_ENABLE_SOCKET |
lsm | 0:b21e585b3aa1 | 1386 | #define FRESHEN_ENABLE_MBEDTLS |
lsm | 0:b21e585b3aa1 | 1387 | #endif |
lsm | 0:b21e585b3aa1 | 1388 | |
lsm | 0:b21e585b3aa1 | 1389 | #if defined(FRESHEN_ENABLE_DASH) |
lsm | 0:b21e585b3aa1 | 1390 | |
lsm | 0:b21e585b3aa1 | 1391 | // WEBSOCKET |
lsm | 0:b21e585b3aa1 | 1392 | #define WEBSOCKET_OP_CONTINUE 0 |
lsm | 0:b21e585b3aa1 | 1393 | #define WEBSOCKET_OP_TEXT 1 |
lsm | 0:b21e585b3aa1 | 1394 | #define WEBSOCKET_OP_BINARY 2 |
lsm | 0:b21e585b3aa1 | 1395 | #define WEBSOCKET_OP_CLOSE 8 |
lsm | 0:b21e585b3aa1 | 1396 | #define WEBSOCKET_OP_PING 9 |
lsm | 0:b21e585b3aa1 | 1397 | #define WEBSOCKET_OP_PONG 10 |
lsm | 0:b21e585b3aa1 | 1398 | |
lsm | 0:b21e585b3aa1 | 1399 | #define FLAGS_MASK_FIN (1 << 7) |
lsm | 0:b21e585b3aa1 | 1400 | #define FLAGS_MASK_OP 0x0f |
lsm | 0:b21e585b3aa1 | 1401 | |
lsm | 0:b21e585b3aa1 | 1402 | struct ws_msg { |
lsm | 0:b21e585b3aa1 | 1403 | int header_len; |
lsm | 0:b21e585b3aa1 | 1404 | int data_len; |
lsm | 0:b21e585b3aa1 | 1405 | int flags; |
lsm | 0:b21e585b3aa1 | 1406 | }; |
lsm | 0:b21e585b3aa1 | 1407 | |
lsm | 0:b21e585b3aa1 | 1408 | #ifndef IN_BUF_SIZE |
lsm | 0:b21e585b3aa1 | 1409 | #define IN_BUF_SIZE 4096 |
lsm | 0:b21e585b3aa1 | 1410 | #endif |
lsm | 0:b21e585b3aa1 | 1411 | |
lsm | 0:b21e585b3aa1 | 1412 | #ifndef OUT_BUF_SIZE |
lsm | 0:b21e585b3aa1 | 1413 | #define OUT_BUF_SIZE IN_BUF_SIZE |
lsm | 0:b21e585b3aa1 | 1414 | #endif |
lsm | 0:b21e585b3aa1 | 1415 | |
lsm | 0:b21e585b3aa1 | 1416 | #ifndef QUEUE_BUF_SIZE |
lsm | 0:b21e585b3aa1 | 1417 | #define QUEUE_BUF_SIZE OUT_BUF_SIZE |
lsm | 0:b21e585b3aa1 | 1418 | #endif |
lsm | 0:b21e585b3aa1 | 1419 | |
lsm | 0:b21e585b3aa1 | 1420 | static int parse_ws_frame(char *buf, int len, struct ws_msg *msg) { |
lsm | 0:b21e585b3aa1 | 1421 | int i, n = 0, mask_len = 0; |
lsm | 0:b21e585b3aa1 | 1422 | msg->header_len = msg->data_len = 0; |
lsm | 0:b21e585b3aa1 | 1423 | if (len >= 2) { |
lsm | 0:b21e585b3aa1 | 1424 | n = buf[1] & 0x7f; |
lsm | 0:b21e585b3aa1 | 1425 | mask_len = buf[1] & FLAGS_MASK_FIN ? 4 : 0; |
lsm | 0:b21e585b3aa1 | 1426 | msg->flags = *(unsigned char *) buf; |
lsm | 0:b21e585b3aa1 | 1427 | if (n < 126 && len >= mask_len) { |
lsm | 0:b21e585b3aa1 | 1428 | msg->data_len = n; |
lsm | 0:b21e585b3aa1 | 1429 | msg->header_len = 2 + mask_len; |
lsm | 0:b21e585b3aa1 | 1430 | } else if (n == 126 && len >= 4 + mask_len) { |
lsm | 0:b21e585b3aa1 | 1431 | msg->header_len = 4 + mask_len; |
lsm | 0:b21e585b3aa1 | 1432 | msg->data_len = ntohs(*(uint16_t *) &buf[2]); |
lsm | 0:b21e585b3aa1 | 1433 | } else if (len >= 10 + mask_len) { |
lsm | 0:b21e585b3aa1 | 1434 | msg->header_len = 10 + mask_len; |
lsm | 0:b21e585b3aa1 | 1435 | msg->data_len = (((uint64_t) ntohl(*(uint32_t *) &buf[2])) << 32) + |
lsm | 0:b21e585b3aa1 | 1436 | ntohl(*(uint32_t *) &buf[6]); |
lsm | 0:b21e585b3aa1 | 1437 | } |
lsm | 0:b21e585b3aa1 | 1438 | } |
lsm | 0:b21e585b3aa1 | 1439 | if (msg->header_len + msg->data_len > len) return 0; |
lsm | 0:b21e585b3aa1 | 1440 | /* Apply mask */ |
lsm | 0:b21e585b3aa1 | 1441 | if (mask_len > 0) { |
lsm | 0:b21e585b3aa1 | 1442 | for (i = 0; i < msg->data_len; i++) { |
lsm | 0:b21e585b3aa1 | 1443 | buf[i + msg->header_len] ^= (buf + msg->header_len - mask_len)[i % 4]; |
lsm | 0:b21e585b3aa1 | 1444 | } |
lsm | 0:b21e585b3aa1 | 1445 | } |
lsm | 0:b21e585b3aa1 | 1446 | return msg->header_len + msg->data_len; |
lsm | 0:b21e585b3aa1 | 1447 | } |
lsm | 0:b21e585b3aa1 | 1448 | |
lsm | 0:b21e585b3aa1 | 1449 | static void ws_send(struct conn *c, int op, char *buf, int len) { |
lsm | 0:b21e585b3aa1 | 1450 | unsigned char header[10]; |
lsm | 0:b21e585b3aa1 | 1451 | int i, header_len = 0; |
lsm | 0:b21e585b3aa1 | 1452 | uint8_t mask[4] = {0x71, 0x3e, 0x5a, 0xcc}; /* it is random, i bet ya */ |
lsm | 0:b21e585b3aa1 | 1453 | header[0] = op | FLAGS_MASK_FIN; |
lsm | 0:b21e585b3aa1 | 1454 | |
lsm | 0:b21e585b3aa1 | 1455 | if (len < 126) { |
lsm | 0:b21e585b3aa1 | 1456 | header[1] = (unsigned char) len; |
lsm | 0:b21e585b3aa1 | 1457 | header_len = 2; |
lsm | 0:b21e585b3aa1 | 1458 | } else if (len < 65535) { |
lsm | 0:b21e585b3aa1 | 1459 | uint16_t tmp = htons((uint16_t) len); |
lsm | 0:b21e585b3aa1 | 1460 | header[1] = 126; |
lsm | 0:b21e585b3aa1 | 1461 | memcpy(&header[2], &tmp, sizeof(tmp)); |
lsm | 0:b21e585b3aa1 | 1462 | header_len = 4; |
lsm | 0:b21e585b3aa1 | 1463 | } else { |
lsm | 0:b21e585b3aa1 | 1464 | uint32_t tmp; |
lsm | 0:b21e585b3aa1 | 1465 | header[1] = 127; |
lsm | 0:b21e585b3aa1 | 1466 | tmp = htonl((uint32_t)((uint64_t) len >> 32)); |
lsm | 0:b21e585b3aa1 | 1467 | memcpy(&header[2], &tmp, sizeof(tmp)); |
lsm | 0:b21e585b3aa1 | 1468 | tmp = htonl((uint32_t)(len & 0xffffffff)); |
lsm | 0:b21e585b3aa1 | 1469 | memcpy(&header[6], &tmp, sizeof(tmp)); |
lsm | 0:b21e585b3aa1 | 1470 | header_len = 10; |
lsm | 0:b21e585b3aa1 | 1471 | } |
lsm | 0:b21e585b3aa1 | 1472 | header[1] |= 1 << 7; /* set masking flag */ |
lsm | 0:b21e585b3aa1 | 1473 | freshen_net_send(c, header, header_len); |
lsm | 0:b21e585b3aa1 | 1474 | freshen_net_send(c, mask, sizeof(mask)); |
lsm | 0:b21e585b3aa1 | 1475 | for (i = 0; i < len; i++) { |
lsm | 0:b21e585b3aa1 | 1476 | buf[i] ^= mask[i % sizeof(mask)]; |
lsm | 0:b21e585b3aa1 | 1477 | } |
lsm | 0:b21e585b3aa1 | 1478 | freshen_net_send(c, buf, len); |
lsm | 0:b21e585b3aa1 | 1479 | } |
lsm | 0:b21e585b3aa1 | 1480 | |
lsm | 0:b21e585b3aa1 | 1481 | static int info(const char *args, char *buf, size_t len, void *userdata) { |
lsm | 0:b21e585b3aa1 | 1482 | struct freshen_ctx *ctx = (struct freshen_ctx *) userdata; |
lsm | 0:b21e585b3aa1 | 1483 | const char *arch = |
lsm | 0:b21e585b3aa1 | 1484 | #if defined(__APPLE__) |
lsm | 0:b21e585b3aa1 | 1485 | "darwin" |
lsm | 0:b21e585b3aa1 | 1486 | #elif defined(__linux__) |
lsm | 0:b21e585b3aa1 | 1487 | "linux" |
lsm | 0:b21e585b3aa1 | 1488 | #elif defined(ESP_PLATFORM) |
lsm | 0:b21e585b3aa1 | 1489 | "esp32" |
lsm | 0:b21e585b3aa1 | 1490 | #elif defined(MBED_LIBRARY_VERSION) |
lsm | 0:b21e585b3aa1 | 1491 | "mbedOS" |
lsm | 0:b21e585b3aa1 | 1492 | #else |
lsm | 0:b21e585b3aa1 | 1493 | "posix" |
lsm | 0:b21e585b3aa1 | 1494 | #endif |
lsm | 0:b21e585b3aa1 | 1495 | ; |
lsm | 0:b21e585b3aa1 | 1496 | int ota_enabled = freshen_ota_enabled(ctx); |
lsm | 0:b21e585b3aa1 | 1497 | snprintf(buf, len, |
lsm | 0:b21e585b3aa1 | 1498 | "{\"fw_version\": \"%s\",\"arch\": \"%s\"" |
lsm | 0:b21e585b3aa1 | 1499 | ",\"ota_enabled\": %s,\"built\": \"%s\"}", |
lsm | 0:b21e585b3aa1 | 1500 | (const char *) userdata, arch, ota_enabled ? "true" : "false", |
lsm | 0:b21e585b3aa1 | 1501 | __DATE__ " " __TIME__); |
lsm | 0:b21e585b3aa1 | 1502 | return 0; |
lsm | 0:b21e585b3aa1 | 1503 | (void) args; |
lsm | 0:b21e585b3aa1 | 1504 | } |
lsm | 0:b21e585b3aa1 | 1505 | |
lsm | 0:b21e585b3aa1 | 1506 | static int rpclist(const char *args, char *buf, size_t len, void *userdata) { |
lsm | 0:b21e585b3aa1 | 1507 | struct freshen_ctx *ctx = (struct freshen_ctx *) userdata; |
lsm | 0:b21e585b3aa1 | 1508 | struct freshen_method *m; |
lsm | 0:b21e585b3aa1 | 1509 | const char *comma = ""; |
lsm | 0:b21e585b3aa1 | 1510 | buf[0] = '\0'; |
lsm | 0:b21e585b3aa1 | 1511 | snprintf(buf + strlen(buf), len - strlen(buf), "["); |
lsm | 0:b21e585b3aa1 | 1512 | for (m = ctx->methods; m != NULL; m = m->next) { |
lsm | 0:b21e585b3aa1 | 1513 | snprintf(buf + strlen(buf), len - strlen(buf), "%s\"%s\"", comma, |
lsm | 0:b21e585b3aa1 | 1514 | m->method); |
lsm | 0:b21e585b3aa1 | 1515 | comma = ","; |
lsm | 0:b21e585b3aa1 | 1516 | } |
lsm | 0:b21e585b3aa1 | 1517 | snprintf(buf + strlen(buf), len - strlen(buf), "]"); |
lsm | 0:b21e585b3aa1 | 1518 | return 0; |
lsm | 0:b21e585b3aa1 | 1519 | (void) args; |
lsm | 0:b21e585b3aa1 | 1520 | } |
lsm | 0:b21e585b3aa1 | 1521 | |
lsm | 0:b21e585b3aa1 | 1522 | struct privdata { |
lsm | 0:b21e585b3aa1 | 1523 | struct conn conn; |
lsm | 0:b21e585b3aa1 | 1524 | char *queue, *in, *out; |
lsm | 0:b21e585b3aa1 | 1525 | int in_len; |
lsm | 0:b21e585b3aa1 | 1526 | }; |
lsm | 0:b21e585b3aa1 | 1527 | |
lsm | 0:b21e585b3aa1 | 1528 | static void freshen_poll(struct freshen_ctx *ctx, const char *url, |
lsm | 0:b21e585b3aa1 | 1529 | const char *version, const char *token) { |
lsm | 0:b21e585b3aa1 | 1530 | struct privdata *pd = (struct privdata *) ctx->privdata; |
lsm | 0:b21e585b3aa1 | 1531 | if (pd == NULL) { |
lsm | 0:b21e585b3aa1 | 1532 | ctx->privdata = pd = (struct privdata *) calloc(1, sizeof(*pd)); |
lsm | 0:b21e585b3aa1 | 1533 | #if defined(__unix__) |
lsm | 0:b21e585b3aa1 | 1534 | signal(SIGPIPE, SIG_IGN); |
lsm | 0:b21e585b3aa1 | 1535 | #endif |
lsm | 0:b21e585b3aa1 | 1536 | ctx->queue.buf = malloc(QUEUE_BUF_SIZE); |
lsm | 0:b21e585b3aa1 | 1537 | ctx->queue.buf_sz = QUEUE_BUF_SIZE; |
lsm | 0:b21e585b3aa1 | 1538 | |
lsm | 0:b21e585b3aa1 | 1539 | freshen_net_init_conn(&pd->conn); |
lsm | 0:b21e585b3aa1 | 1540 | |
lsm | 0:b21e585b3aa1 | 1541 | freshen_core_export(ctx, "info", info, (void *) version); |
lsm | 0:b21e585b3aa1 | 1542 | freshen_core_export(ctx, "rpc.list", rpclist, ctx); |
lsm | 0:b21e585b3aa1 | 1543 | |
lsm | 0:b21e585b3aa1 | 1544 | #if defined(__unix__) || defined(__APPLE__) || defined(ESP_PLATFORM) |
lsm | 0:b21e585b3aa1 | 1545 | freshen_core_export(ctx, "ota.begin", freshen_rpc_ota_begin, ctx); |
lsm | 0:b21e585b3aa1 | 1546 | freshen_core_export(ctx, "ota.write", freshen_rpc_ota_write, ctx); |
lsm | 0:b21e585b3aa1 | 1547 | freshen_core_export(ctx, "ota.end", freshen_rpc_ota_end, ctx); |
lsm | 0:b21e585b3aa1 | 1548 | #endif |
lsm | 0:b21e585b3aa1 | 1549 | |
lsm | 0:b21e585b3aa1 | 1550 | pd->in = (char *) malloc(IN_BUF_SIZE); |
lsm | 0:b21e585b3aa1 | 1551 | pd->out = (char *) malloc(OUT_BUF_SIZE); |
lsm | 0:b21e585b3aa1 | 1552 | |
lsm | 0:b21e585b3aa1 | 1553 | pd->in[0] = pd->out[0] = ((char *) ctx->queue.buf)[0] = '\0'; |
lsm | 0:b21e585b3aa1 | 1554 | |
lsm | 0:b21e585b3aa1 | 1555 | freshen_ota_init(ctx); |
lsm | 0:b21e585b3aa1 | 1556 | } |
lsm | 0:b21e585b3aa1 | 1557 | |
lsm | 0:b21e585b3aa1 | 1558 | if (!freconnect(url, token, &pd->conn)) { |
lsm | 0:b21e585b3aa1 | 1559 | freshen_sleep(1); |
lsm | 0:b21e585b3aa1 | 1560 | return; |
lsm | 0:b21e585b3aa1 | 1561 | } |
lsm | 0:b21e585b3aa1 | 1562 | |
lsm | 0:b21e585b3aa1 | 1563 | if (pd->in_len >= IN_BUF_SIZE) { |
lsm | 0:b21e585b3aa1 | 1564 | FLOGE("input buffer overflow, closing.."); |
lsm | 0:b21e585b3aa1 | 1565 | freshen_net_close(&pd->conn); |
lsm | 0:b21e585b3aa1 | 1566 | return; |
lsm | 0:b21e585b3aa1 | 1567 | } |
lsm | 0:b21e585b3aa1 | 1568 | |
lsm | 0:b21e585b3aa1 | 1569 | int n = freshen_net_recv(&pd->conn, pd->in + pd->in_len, |
lsm | 0:b21e585b3aa1 | 1570 | IN_BUF_SIZE - pd->in_len); |
lsm | 0:b21e585b3aa1 | 1571 | if (n < 0) { |
lsm | 0:b21e585b3aa1 | 1572 | FLOGE("read error, closing.."); |
lsm | 0:b21e585b3aa1 | 1573 | freshen_net_close(&pd->conn); |
lsm | 0:b21e585b3aa1 | 1574 | return; |
lsm | 0:b21e585b3aa1 | 1575 | } |
lsm | 0:b21e585b3aa1 | 1576 | pd->in_len += n; |
lsm | 0:b21e585b3aa1 | 1577 | |
lsm | 0:b21e585b3aa1 | 1578 | // Still in the WS handshake mode. Buffer and skip HTTP reply |
lsm | 0:b21e585b3aa1 | 1579 | if (!pd->conn.is_ws) { |
lsm | 0:b21e585b3aa1 | 1580 | for (int i = 4; i <= pd->in_len; i++) { |
lsm | 0:b21e585b3aa1 | 1581 | if (memcmp(pd->in + i - 4, "\r\n\r\n", 4) == 0) { |
lsm | 0:b21e585b3aa1 | 1582 | memmove(pd->in, pd->in + i, pd->in_len - i); |
lsm | 0:b21e585b3aa1 | 1583 | pd->in_len -= i; |
lsm | 0:b21e585b3aa1 | 1584 | pd->conn.is_ws = true; |
lsm | 0:b21e585b3aa1 | 1585 | break; |
lsm | 0:b21e585b3aa1 | 1586 | } |
lsm | 0:b21e585b3aa1 | 1587 | } |
lsm | 0:b21e585b3aa1 | 1588 | if (!pd->conn.is_ws) return; |
lsm | 0:b21e585b3aa1 | 1589 | } |
lsm | 0:b21e585b3aa1 | 1590 | |
lsm | 0:b21e585b3aa1 | 1591 | freshen_ota_commit(ctx); |
lsm | 0:b21e585b3aa1 | 1592 | |
lsm | 0:b21e585b3aa1 | 1593 | struct ws_msg msg; |
lsm | 0:b21e585b3aa1 | 1594 | if (parse_ws_frame(pd->in, pd->in_len, &msg)) { |
lsm | 0:b21e585b3aa1 | 1595 | char *data = pd->in + msg.header_len; |
lsm | 0:b21e585b3aa1 | 1596 | int data_len = msg.data_len; |
lsm | 0:b21e585b3aa1 | 1597 | while (data_len > 0 && data[data_len - 1] != '}') data_len--; |
lsm | 0:b21e585b3aa1 | 1598 | |
lsm | 0:b21e585b3aa1 | 1599 | switch (msg.flags & FLAGS_MASK_OP) { |
lsm | 0:b21e585b3aa1 | 1600 | case WEBSOCKET_OP_PING: |
lsm | 0:b21e585b3aa1 | 1601 | FLOGI("WS PONG"); |
lsm | 0:b21e585b3aa1 | 1602 | ws_send(&pd->conn, WEBSOCKET_OP_PONG, data, data_len); |
lsm | 0:b21e585b3aa1 | 1603 | break; |
lsm | 0:b21e585b3aa1 | 1604 | case WEBSOCKET_OP_CLOSE: |
lsm | 0:b21e585b3aa1 | 1605 | FLOGI("WS close received, closing connection"); |
lsm | 0:b21e585b3aa1 | 1606 | freshen_net_close(&pd->conn); |
lsm | 0:b21e585b3aa1 | 1607 | return; |
lsm | 0:b21e585b3aa1 | 1608 | } |
lsm | 0:b21e585b3aa1 | 1609 | |
lsm | 0:b21e585b3aa1 | 1610 | FLOGI("WS in: %d [%.*s]", data_len, data_len, data); |
lsm | 0:b21e585b3aa1 | 1611 | pd->out[0] = pd->out[OUT_BUF_SIZE - 1] = '\0'; |
lsm | 0:b21e585b3aa1 | 1612 | freshen_core_process(ctx, data, data_len, pd->out, OUT_BUF_SIZE - 1); |
lsm | 0:b21e585b3aa1 | 1613 | int out_len = strlen(pd->out); |
lsm | 0:b21e585b3aa1 | 1614 | FLOGI("WS out: %d [%.*s]", out_len, out_len, pd->out); |
lsm | 0:b21e585b3aa1 | 1615 | if (out_len > 0) { |
lsm | 0:b21e585b3aa1 | 1616 | ws_send(&pd->conn, WEBSOCKET_OP_TEXT, pd->out, out_len); |
lsm | 0:b21e585b3aa1 | 1617 | } |
lsm | 0:b21e585b3aa1 | 1618 | |
lsm | 0:b21e585b3aa1 | 1619 | int framelen = msg.header_len + msg.data_len; |
lsm | 0:b21e585b3aa1 | 1620 | memmove(pd->in, pd->in + framelen, pd->in_len - framelen); |
lsm | 0:b21e585b3aa1 | 1621 | pd->in_len -= framelen; |
lsm | 0:b21e585b3aa1 | 1622 | } |
lsm | 0:b21e585b3aa1 | 1623 | } |
lsm | 0:b21e585b3aa1 | 1624 | |
lsm | 0:b21e585b3aa1 | 1625 | static void freshen_poll_dash(struct freshen_ctx *ctx, const char *version, |
lsm | 0:b21e585b3aa1 | 1626 | const char *token) { |
lsm | 0:b21e585b3aa1 | 1627 | freshen_poll(ctx, "wss://dash.freshen.cc/api/v1/rpc", version, token); |
lsm | 0:b21e585b3aa1 | 1628 | } |
lsm | 0:b21e585b3aa1 | 1629 | #endif |
lsm | 0:b21e585b3aa1 | 1630 | #endif /* FRESHEN_DECLARATIONS_ONLY */ |
lsm | 0:b21e585b3aa1 | 1631 | |
lsm | 0:b21e585b3aa1 | 1632 | #ifdef __cplusplus |
lsm | 0:b21e585b3aa1 | 1633 | } |
lsm | 0:b21e585b3aa1 | 1634 | #endif /* __cplusplus */ |
lsm | 0:b21e585b3aa1 | 1635 | |
lsm | 0:b21e585b3aa1 | 1636 | #endif /* FRESHEN_H */ |