protocol.c (13748B)
1 #include "protocol.h" 2 3 #include <string.h> 4 5 typedef struct Cursor { 6 const uint8_t* in; 7 uint8_t* out; 8 size_t len; 9 size_t pos; 10 int err; 11 } Cursor; 12 13 static int text_valid(KitSlice s, size_t cap, int allow_space) { 14 size_t i; 15 if (s.len >= cap) return 0; 16 if (s.len && !s.s) return 0; 17 for (i = 0; i < s.len; ++i) { 18 unsigned char c = (unsigned char)s.s[i]; 19 if (c == '\0' || c == '\n' || c == '\r') return 0; 20 if (!allow_space && (c == ' ' || c == '\t')) return 0; 21 } 22 return 1; 23 } 24 25 static void put_u8(Cursor* c, uint8_t v) { 26 if (c->pos + 1u > c->len) { 27 c->err = 1; 28 return; 29 } 30 c->out[c->pos++] = v; 31 } 32 33 static void put_u16(Cursor* c, uint16_t v) { 34 put_u8(c, (uint8_t)(v & 0xffu)); 35 put_u8(c, (uint8_t)((v >> 8) & 0xffu)); 36 } 37 38 static void put_u64(Cursor* c, uint64_t v) { 39 size_t i; 40 for (i = 0; i < 8u; ++i) put_u8(c, (uint8_t)((v >> (8u * i)) & 0xffu)); 41 } 42 43 static void put_bytes(Cursor* c, const void* data, size_t n) { 44 if (n && !data) { 45 c->err = 1; 46 return; 47 } 48 if (c->pos > c->len || n > c->len - c->pos) { 49 c->err = 1; 50 return; 51 } 52 if (n) memcpy(c->out + c->pos, data, n); 53 c->pos += n; 54 } 55 56 static void put_slice(Cursor* c, KitSlice s) { 57 if (s.len > 0xffffu) { 58 c->err = 1; 59 return; 60 } 61 put_u16(c, (uint16_t)s.len); 62 put_bytes(c, s.data, s.len); 63 } 64 65 static uint8_t get_u8(Cursor* c) { 66 if (c->pos + 1u > c->len) { 67 c->err = 1; 68 return 0; 69 } 70 return c->in[c->pos++]; 71 } 72 73 static uint16_t get_u16(Cursor* c) { 74 uint16_t lo = get_u8(c); 75 uint16_t hi = get_u8(c); 76 return (uint16_t)(lo | (uint16_t)(hi << 8)); 77 } 78 79 static uint64_t get_u64(Cursor* c) { 80 uint64_t v = 0; 81 size_t i; 82 for (i = 0; i < 8u; ++i) v |= (uint64_t)get_u8(c) << (8u * i); 83 return v; 84 } 85 86 static void get_bytes(Cursor* c, uint8_t* out, size_t n) { 87 if (c->pos > c->len || n > c->len - c->pos) { 88 c->err = 1; 89 return; 90 } 91 if (n) memcpy(out, c->in + c->pos, n); 92 c->pos += n; 93 } 94 95 static KitSlice get_slice(Cursor* c) { 96 KitSlice s; 97 uint16_t n = get_u16(c); 98 if (c->pos > c->len || (size_t)n > c->len - c->pos) { 99 c->err = 1; 100 return KIT_SLICE_NULL; 101 } 102 s.data = c->in + c->pos; 103 s.len = n; 104 c->pos += n; 105 return s; 106 } 107 108 static int known_cmd(uint8_t cmd) { 109 return cmd == BUILD_CMD_CONFIG_GET || cmd == BUILD_CMD_SOURCE || 110 cmd == BUILD_CMD_GLOB || cmd == BUILD_CMD_NEED || 111 cmd == BUILD_CMD_NEED_SUBMIT || cmd == BUILD_CMD_NEED_AWAIT || 112 cmd == BUILD_CMD_FETCH; 113 } 114 115 static int token_valid(KitSlice s, size_t cap) { 116 return s.len > 0 && text_valid(s, cap, 0); 117 } 118 119 static int hex_id_valid(KitSlice s) { 120 size_t i; 121 if (s.len != 2u * BUILD_HASH_LEN || !s.s) return 0; 122 for (i = 0; i < s.len; ++i) { 123 unsigned char c = (unsigned char)s.s[i]; 124 if (!((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || 125 (c >= 'A' && c <= 'F'))) 126 return 0; 127 } 128 return 1; 129 } 130 131 static int valid_arg_for_cmd(uint8_t cmd, KitSlice s) { 132 if (cmd == BUILD_CMD_CONFIG_GET) return token_valid(s, BUILD_KEY_MAX); 133 if (cmd == BUILD_CMD_SOURCE) return token_valid(s, BUILD_PATH_MAX); 134 if (cmd == BUILD_CMD_GLOB) return token_valid(s, BUILD_PATTERN_MAX); 135 if (cmd == BUILD_CMD_NEED || cmd == BUILD_CMD_NEED_SUBMIT) 136 return token_valid(s, BUILD_TARGET_MAX); 137 if (cmd == BUILD_CMD_FETCH) return hex_id_valid(s); 138 return 1; 139 } 140 141 int build_proto_encode_req(const BuildReq* req, uint8_t* buf, size_t cap, 142 size_t* n) { 143 size_t i; 144 Cursor c; 145 if (!req || !buf || !n) return BUILD_ERR; 146 if (!known_cmd(req->cmd)) return BUILD_ERR; 147 c.in = NULL; 148 c.out = buf; 149 c.len = cap < BUILD_FRAME_MAX ? cap : BUILD_FRAME_MAX; 150 c.pos = 0; 151 c.err = 0; 152 put_u8(&c, req->cmd); 153 if (req->cmd == BUILD_CMD_CONFIG_GET) { 154 if (!valid_arg_for_cmd(req->cmd, req->arg)) return BUILD_ERR; 155 if (req->argc > 1u || (req->argc && !req->argv)) return BUILD_ERR; 156 put_slice(&c, req->arg); 157 put_u16(&c, (uint16_t)req->argc); 158 if (req->argc) { 159 if (!text_valid(req->argv[0], BUILD_VAL_MAX, 1)) return BUILD_ERR; 160 put_slice(&c, req->argv[0]); 161 } 162 } else if (req->cmd == BUILD_CMD_SOURCE || req->cmd == BUILD_CMD_GLOB) { 163 if (!valid_arg_for_cmd(req->cmd, req->arg)) return BUILD_ERR; 164 put_slice(&c, req->arg); 165 } else if (req->cmd == BUILD_CMD_FETCH) { 166 if (!valid_arg_for_cmd(req->cmd, req->arg)) return BUILD_ERR; 167 if (req->argc == 0u || req->argc > 0xffffu || !req->argv) 168 return BUILD_ERR; 169 put_slice(&c, req->arg); 170 put_u16(&c, (uint16_t)req->argc); 171 for (i = 0; i < req->argc; ++i) { 172 if (!token_valid(req->argv[i], BUILD_URL_MAX)) return BUILD_ERR; 173 put_slice(&c, req->argv[i]); 174 } 175 } else if (req->cmd == BUILD_CMD_NEED || 176 req->cmd == BUILD_CMD_NEED_SUBMIT) { 177 if (!valid_arg_for_cmd(req->cmd, req->arg)) return BUILD_ERR; 178 if (req->noverrides > 0xffffu || req->argc > 0xffffu) return BUILD_ERR; 179 if (req->noverrides && !req->overrides) return BUILD_ERR; 180 if (req->argc && !req->argv) return BUILD_ERR; 181 put_slice(&c, req->arg); 182 put_u16(&c, (uint16_t)req->noverrides); 183 for (i = 0; i < req->noverrides; ++i) { 184 if (!token_valid(req->overrides[i].key, BUILD_KEY_MAX) || 185 !text_valid(req->overrides[i].value, BUILD_VAL_MAX, 1)) 186 return BUILD_ERR; 187 put_slice(&c, req->overrides[i].key); 188 put_slice(&c, req->overrides[i].value); 189 } 190 put_u16(&c, (uint16_t)req->argc); 191 for (i = 0; i < req->argc; ++i) { 192 if (!text_valid(req->argv[i], BUILD_VAL_MAX, 1)) return BUILD_ERR; 193 put_slice(&c, req->argv[i]); 194 } 195 } else { 196 put_u64(&c, req->token); 197 } 198 if (c.err || c.pos > BUILD_FRAME_MAX) return BUILD_ERR; 199 *n = c.pos; 200 return BUILD_OK; 201 } 202 203 int build_proto_decode_req(const uint8_t* buf, size_t len, BuildReq* out, 204 KitBuildKV* ovr_storage, size_t ovr_cap, 205 KitSlice* argv_storage, size_t argv_cap) { 206 size_t i; 207 Cursor c; 208 if (!buf || !out || len == 0 || len > BUILD_FRAME_MAX) return BUILD_ERR; 209 memset(out, 0, sizeof *out); 210 c.in = buf; 211 c.out = NULL; 212 c.len = len; 213 c.pos = 0; 214 c.err = 0; 215 out->cmd = get_u8(&c); 216 if (!known_cmd(out->cmd)) return BUILD_ERR; 217 if (out->cmd == BUILD_CMD_CONFIG_GET) { 218 uint16_t argc; 219 out->arg = get_slice(&c); 220 if (!valid_arg_for_cmd(out->cmd, out->arg)) return BUILD_ERR; 221 argc = get_u16(&c); 222 if (argc > 1u || (argc && (!argv_storage || argv_cap == 0u))) 223 return BUILD_ERR; 224 out->argv = argv_storage; 225 out->argc = argc; 226 if (argc) { 227 argv_storage[0] = get_slice(&c); 228 if (!text_valid(argv_storage[0], BUILD_VAL_MAX, 1)) return BUILD_ERR; 229 } 230 } else if (out->cmd == BUILD_CMD_SOURCE || out->cmd == BUILD_CMD_GLOB) { 231 out->arg = get_slice(&c); 232 if (!valid_arg_for_cmd(out->cmd, out->arg)) return BUILD_ERR; 233 } else if (out->cmd == BUILD_CMD_FETCH) { 234 uint16_t argc; 235 out->arg = get_slice(&c); 236 if (!valid_arg_for_cmd(out->cmd, out->arg)) return BUILD_ERR; 237 argc = get_u16(&c); 238 if (argc == 0u || (size_t)argc > argv_cap || !argv_storage) 239 return BUILD_ERR; 240 out->argv = argv_storage; 241 out->argc = argc; 242 for (i = 0; i < out->argc; ++i) { 243 argv_storage[i] = get_slice(&c); 244 if (!token_valid(argv_storage[i], BUILD_URL_MAX)) return BUILD_ERR; 245 } 246 } else if (out->cmd == BUILD_CMD_NEED || 247 out->cmd == BUILD_CMD_NEED_SUBMIT) { 248 uint16_t noverrides, argc; 249 out->arg = get_slice(&c); 250 if (!valid_arg_for_cmd(out->cmd, out->arg)) return BUILD_ERR; 251 noverrides = get_u16(&c); 252 if ((size_t)noverrides > ovr_cap) return BUILD_ERR; 253 if (noverrides && !ovr_storage) return BUILD_ERR; 254 out->overrides = ovr_storage; 255 out->noverrides = noverrides; 256 for (i = 0; i < out->noverrides; ++i) { 257 ovr_storage[i].key = get_slice(&c); 258 ovr_storage[i].value = get_slice(&c); 259 if (!token_valid(ovr_storage[i].key, BUILD_KEY_MAX) || 260 !text_valid(ovr_storage[i].value, BUILD_VAL_MAX, 1)) 261 return BUILD_ERR; 262 } 263 argc = get_u16(&c); 264 if ((size_t)argc > argv_cap) return BUILD_ERR; 265 if (argc && !argv_storage) return BUILD_ERR; 266 out->argv = argv_storage; 267 out->argc = argc; 268 for (i = 0; i < out->argc; ++i) { 269 argv_storage[i] = get_slice(&c); 270 if (!text_valid(argv_storage[i], BUILD_VAL_MAX, 1)) return BUILD_ERR; 271 } 272 } else { 273 out->token = get_u64(&c); 274 } 275 if (c.err || c.pos != c.len) return BUILD_ERR; 276 return BUILD_OK; 277 } 278 279 static int status_known(uint8_t status) { 280 return status == BUILD_RESP_OK || status == BUILD_RESP_UNSET || 281 status == BUILD_RESP_ERROR || status == BUILD_RESP_GLOB_END || 282 status == BUILD_RESP_ABSENT || status == BUILD_RESP_DEFAULT; 283 } 284 285 int build_proto_encode_resp(const BuildReq* for_cmd, const BuildResp* resp, 286 uint8_t* buf, size_t cap, size_t* n) { 287 Cursor c; 288 if (!for_cmd || !resp || !buf || !n) return BUILD_ERR; 289 if (!known_cmd(for_cmd->cmd) || !status_known(resp->status)) return BUILD_ERR; 290 c.in = NULL; 291 c.out = buf; 292 c.len = cap < BUILD_FRAME_MAX ? cap : BUILD_FRAME_MAX; 293 c.pos = 0; 294 c.err = 0; 295 put_u8(&c, resp->status); 296 if (resp->status == BUILD_RESP_ERROR) { 297 if (!text_valid(resp->text, BUILD_FRAME_MAX, 1)) return BUILD_ERR; 298 put_u16(&c, resp->error_status); 299 put_slice(&c, resp->text); 300 } else if (for_cmd->cmd == BUILD_CMD_CONFIG_GET) { 301 if (resp->status == BUILD_RESP_UNSET) { 302 } else if (resp->status == BUILD_RESP_OK || 303 resp->status == BUILD_RESP_DEFAULT) { 304 if (!text_valid(resp->text, BUILD_VAL_MAX, 1)) return BUILD_ERR; 305 put_slice(&c, resp->text); 306 } else { 307 return BUILD_ERR; 308 } 309 } else if (for_cmd->cmd == BUILD_CMD_SOURCE) { 310 if (resp->status == BUILD_RESP_ABSENT) { 311 } else if (resp->status == BUILD_RESP_OK) { 312 if (!token_valid(resp->text, BUILD_PATH_MAX)) return BUILD_ERR; 313 put_bytes(&c, resp->id, BUILD_HASH_LEN); 314 put_slice(&c, resp->text); 315 } else { 316 return BUILD_ERR; 317 } 318 } else if (for_cmd->cmd == BUILD_CMD_FETCH) { 319 if (resp->status != BUILD_RESP_OK || 320 !token_valid(resp->text, BUILD_PATH_MAX)) 321 return BUILD_ERR; 322 put_bytes(&c, resp->id, BUILD_HASH_LEN); 323 put_slice(&c, resp->text); 324 } else if (for_cmd->cmd == BUILD_CMD_GLOB) { 325 if (resp->status == BUILD_RESP_GLOB_END) { 326 } else if (resp->status == BUILD_RESP_OK) { 327 if (resp->text.len == 0) { 328 put_u16(&c, 0); 329 } else { 330 if (!token_valid(resp->text, BUILD_PATH_MAX)) return BUILD_ERR; 331 put_u16(&c, 1); 332 put_slice(&c, resp->text); 333 } 334 } else { 335 return BUILD_ERR; 336 } 337 } else if (for_cmd->cmd == BUILD_CMD_NEED || 338 for_cmd->cmd == BUILD_CMD_NEED_AWAIT) { 339 if (resp->status != BUILD_RESP_OK || 340 !token_valid(resp->text, BUILD_PATH_MAX)) 341 return BUILD_ERR; 342 put_bytes(&c, resp->id, BUILD_HASH_LEN); 343 put_slice(&c, resp->text); 344 } else if (for_cmd->cmd == BUILD_CMD_NEED_SUBMIT) { 345 if (resp->status != BUILD_RESP_OK) return BUILD_ERR; 346 put_u64(&c, resp->token); 347 } else { 348 return BUILD_ERR; 349 } 350 if (c.err || c.pos > BUILD_FRAME_MAX) return BUILD_ERR; 351 *n = c.pos; 352 return BUILD_OK; 353 } 354 355 int build_proto_decode_resp(const uint8_t* buf, size_t len, uint8_t cmd, 356 BuildResp* out, BuildProtoGlobFn glob_cb, 357 void* glob_user) { 358 Cursor c; 359 if (!buf || !out || len == 0 || len > BUILD_FRAME_MAX || !known_cmd(cmd)) 360 return BUILD_ERR; 361 memset(out, 0, sizeof *out); 362 c.in = buf; 363 c.out = NULL; 364 c.len = len; 365 c.pos = 0; 366 c.err = 0; 367 out->status = get_u8(&c); 368 if (!status_known(out->status)) return BUILD_ERR; 369 if (out->status == BUILD_RESP_ERROR) { 370 out->error_status = get_u16(&c); 371 out->text = get_slice(&c); 372 if (!text_valid(out->text, BUILD_FRAME_MAX, 1)) return BUILD_ERR; 373 } else if (cmd == BUILD_CMD_CONFIG_GET) { 374 if (out->status == BUILD_RESP_UNSET) { 375 } else if (out->status == BUILD_RESP_OK || 376 out->status == BUILD_RESP_DEFAULT) { 377 out->text = get_slice(&c); 378 if (!text_valid(out->text, BUILD_VAL_MAX, 1)) return BUILD_ERR; 379 } else { 380 return BUILD_ERR; 381 } 382 } else if (cmd == BUILD_CMD_SOURCE) { 383 if (out->status == BUILD_RESP_ABSENT) { 384 } else if (out->status == BUILD_RESP_OK) { 385 get_bytes(&c, out->id, BUILD_HASH_LEN); 386 out->text = get_slice(&c); 387 if (!token_valid(out->text, BUILD_PATH_MAX)) return BUILD_ERR; 388 } else { 389 return BUILD_ERR; 390 } 391 } else if (cmd == BUILD_CMD_FETCH) { 392 if (out->status != BUILD_RESP_OK) return BUILD_ERR; 393 get_bytes(&c, out->id, BUILD_HASH_LEN); 394 out->text = get_slice(&c); 395 if (!token_valid(out->text, BUILD_PATH_MAX)) return BUILD_ERR; 396 } else if (cmd == BUILD_CMD_GLOB) { 397 if (out->status == BUILD_RESP_GLOB_END) { 398 } else if (out->status == BUILD_RESP_OK) { 399 uint16_t npaths = get_u16(&c); 400 uint16_t i; 401 int stopped = 0; 402 for (i = 0; i < npaths; ++i) { 403 KitSlice path = get_slice(&c); 404 if (!token_valid(path, BUILD_PATH_MAX)) return BUILD_ERR; 405 if (!stopped && glob_cb) { 406 if (glob_cb(glob_user, path)) stopped = 1; 407 } 408 } 409 } else { 410 return BUILD_ERR; 411 } 412 } else if (cmd == BUILD_CMD_NEED || cmd == BUILD_CMD_NEED_AWAIT) { 413 if (out->status != BUILD_RESP_OK) return BUILD_ERR; 414 get_bytes(&c, out->id, BUILD_HASH_LEN); 415 out->text = get_slice(&c); 416 if (!token_valid(out->text, BUILD_PATH_MAX)) return BUILD_ERR; 417 } else if (cmd == BUILD_CMD_NEED_SUBMIT) { 418 if (out->status != BUILD_RESP_OK) return BUILD_ERR; 419 out->token = get_u64(&c); 420 } else { 421 return BUILD_ERR; 422 } 423 if (c.err || c.pos != c.len) return BUILD_ERR; 424 return BUILD_OK; 425 }