kit

kit
git clone https://git.ryansepassi.com/git/kit.git
Log | Files | Refs | README

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 }