diff options
author | Patrick Steinhardt <ps@pks.im> | 2018-10-05 20:03:38 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-10-05 20:03:38 +0200 |
commit | ce8803a295063b69d7d26442a0407e38cc939bbe (patch) | |
tree | 70cfa2471b83295fc67d7ea4dce37502b7f92632 | |
parent | 84d6f439a2936685ab6b3082d12f98a38ace5e6c (diff) | |
parent | 1bc5b05c614c7b10de021fa392943e8e6bd12c77 (diff) | |
download | libgit2-ce8803a295063b69d7d26442a0407e38cc939bbe.tar.gz |
Merge pull request #4836 from pks-t/pks/smart-packets
Smart packet security fixes
-rw-r--r-- | src/transports/smart.h | 6 | ||||
-rw-r--r-- | src/transports/smart_pkt.c | 218 | ||||
-rw-r--r-- | src/transports/smart_protocol.c | 10 | ||||
-rw-r--r-- | tests/transports/smart/packet.c | 340 |
4 files changed, 465 insertions, 109 deletions
diff --git a/src/transports/smart.h b/src/transports/smart.h index 057d7e6e0..abf80286b 100644 --- a/src/transports/smart.h +++ b/src/transports/smart.h @@ -90,7 +90,7 @@ typedef struct { typedef struct { git_pkt_type type; - int len; + size_t len; char data[GIT_FLEX_ARRAY]; } git_pkt_data; @@ -98,7 +98,7 @@ typedef git_pkt_data git_pkt_progress; typedef struct { git_pkt_type type; - int len; + size_t len; char error[GIT_FLEX_ARRAY]; } git_pkt_err; @@ -188,7 +188,7 @@ int git_smart__get_push_stream(transport_smart *t, git_smart_subtransport_stream int git_smart__update_heads(transport_smart *t, git_vector *symrefs); /* smart_pkt.c */ -int git_pkt_parse_line(git_pkt **head, const char *line, const char **out, size_t len); +int git_pkt_parse_line(git_pkt **head, const char **endptr, const char *line, size_t linelen); int git_pkt_buffer_flush(git_buf *buf); int git_pkt_send_flush(GIT_SOCKET s); int git_pkt_buffer_done(git_buf *buf); diff --git a/src/transports/smart_pkt.c b/src/transports/smart_pkt.c index 00798c396..6a404efe4 100644 --- a/src/transports/smart_pkt.c +++ b/src/transports/smart_pkt.c @@ -43,34 +43,43 @@ static int flush_pkt(git_pkt **out) static int ack_pkt(git_pkt **out, const char *line, size_t len) { git_pkt_ack *pkt; - GIT_UNUSED(line); - GIT_UNUSED(len); pkt = git__calloc(1, sizeof(git_pkt_ack)); GITERR_CHECK_ALLOC(pkt); - pkt->type = GIT_PKT_ACK; - line += 3; - len -= 3; - if (len >= GIT_OID_HEXSZ) { - git_oid_fromstr(&pkt->oid, line + 1); - line += GIT_OID_HEXSZ + 1; - len -= GIT_OID_HEXSZ + 1; - } + if (git__prefixncmp(line, len, "ACK ")) + goto out_err; + line += 4; + len -= 4; + + if (len < GIT_OID_HEXSZ || git_oid_fromstr(&pkt->oid, line) < 0) + goto out_err; + line += GIT_OID_HEXSZ; + len -= GIT_OID_HEXSZ; + + if (len && line[0] == ' ') { + line++; + len--; - if (len >= 7) { - if (!git__prefixcmp(line + 1, "continue")) + if (!git__prefixncmp(line, len, "continue")) pkt->status = GIT_ACK_CONTINUE; - if (!git__prefixcmp(line + 1, "common")) + else if (!git__prefixncmp(line, len, "common")) pkt->status = GIT_ACK_COMMON; - if (!git__prefixcmp(line + 1, "ready")) + else if (!git__prefixncmp(line, len, "ready")) pkt->status = GIT_ACK_READY; + else + goto out_err; } *out = (git_pkt *) pkt; return 0; + +out_err: + giterr_set(GITERR_NET, "error parsing ACK pkt-line"); + git__free(pkt); + return -1; } static int nak_pkt(git_pkt **out) @@ -107,10 +116,12 @@ static int comment_pkt(git_pkt **out, const char *line, size_t len) static int err_pkt(git_pkt **out, const char *line, size_t len) { - git_pkt_err *pkt; + git_pkt_err *pkt = NULL; size_t alloclen; /* Remove "ERR " from the line */ + if (git__prefixncmp(line, len, "ERR ")) + goto out_err; line += 4; len -= 4; @@ -118,15 +129,20 @@ static int err_pkt(git_pkt **out, const char *line, size_t len) GITERR_CHECK_ALLOC_ADD(&alloclen, alloclen, 1); pkt = git__malloc(alloclen); GITERR_CHECK_ALLOC(pkt); - pkt->type = GIT_PKT_ERR; - pkt->len = (int)len; + pkt->len = len; + memcpy(pkt->error, line, len); pkt->error[len] = '\0'; *out = (git_pkt *) pkt; return 0; + +out_err: + giterr_set(GITERR_NET, "error parsing ERR pkt-line"); + git__free(pkt); + return -1; } static int data_pkt(git_pkt **out, const char *line, size_t len) @@ -142,7 +158,7 @@ static int data_pkt(git_pkt **out, const char *line, size_t len) GITERR_CHECK_ALLOC(pkt); pkt->type = GIT_PKT_DATA; - pkt->len = (int) len; + pkt->len = len; memcpy(pkt->data, line, len); *out = (git_pkt *) pkt; @@ -163,7 +179,7 @@ static int sideband_progress_pkt(git_pkt **out, const char *line, size_t len) GITERR_CHECK_ALLOC(pkt); pkt->type = GIT_PKT_PROGRESS; - pkt->len = (int) len; + pkt->len = len; memcpy(pkt->data, line, len); *out = (git_pkt *) pkt; @@ -199,33 +215,25 @@ static int sideband_error_pkt(git_pkt **out, const char *line, size_t len) */ static int ref_pkt(git_pkt **out, const char *line, size_t len) { - int error; git_pkt_ref *pkt; size_t alloclen; - if (len < GIT_OID_HEXSZ + 1) { - giterr_set(GITERR_NET, "error parsing pkt-line"); - return -1; - } - - pkt = git__malloc(sizeof(git_pkt_ref)); + pkt = git__calloc(1, sizeof(git_pkt_ref)); GITERR_CHECK_ALLOC(pkt); - - memset(pkt, 0x0, sizeof(git_pkt_ref)); pkt->type = GIT_PKT_REF; - if ((error = git_oid_fromstr(&pkt->head.oid, line)) < 0) - goto error_out; - - /* Check for a bit of consistency */ - if (line[GIT_OID_HEXSZ] != ' ') { - giterr_set(GITERR_NET, "error parsing pkt-line"); - error = -1; - goto error_out; - } - /* Jump from the name */ - line += GIT_OID_HEXSZ + 1; - len -= (GIT_OID_HEXSZ + 1); + if (len < GIT_OID_HEXSZ || git_oid_fromstr(&pkt->head.oid, line) < 0) + goto out_err; + line += GIT_OID_HEXSZ; + len -= GIT_OID_HEXSZ; + + if (git__prefixncmp(line, len, " ")) + goto out_err; + line++; + len--; + + if (!len) + goto out_err; if (line[len - 1] == '\n') --len; @@ -237,36 +245,36 @@ static int ref_pkt(git_pkt **out, const char *line, size_t len) memcpy(pkt->head.name, line, len); pkt->head.name[len] = '\0'; - if (strlen(pkt->head.name) < len) { + if (strlen(pkt->head.name) < len) pkt->capabilities = strchr(pkt->head.name, '\0') + 1; - } *out = (git_pkt *)pkt; return 0; -error_out: +out_err: + giterr_set(GITERR_NET, "error parsing REF pkt-line"); + if (pkt) + git__free(pkt->head.name); git__free(pkt); - return error; + return -1; } static int ok_pkt(git_pkt **out, const char *line, size_t len) { git_pkt_ok *pkt; - const char *ptr; size_t alloc_len; pkt = git__malloc(sizeof(*pkt)); GITERR_CHECK_ALLOC(pkt); - pkt->type = GIT_PKT_OK; - line += 3; /* skip "ok " */ - if (!(ptr = strchr(line, '\n'))) { - giterr_set(GITERR_NET, "invalid packet line"); - git__free(pkt); - return -1; - } - len = ptr - line; + if (git__prefixncmp(line, len, "ok ")) + goto out_err; + line += 3; + len -= 3; + + if (line[len - 1] == '\n') + --len; GITERR_CHECK_ALLOC_ADD(&alloc_len, len, 1); pkt->ref = git__malloc(alloc_len); @@ -277,6 +285,11 @@ static int ok_pkt(git_pkt **out, const char *line, size_t len) *out = (git_pkt *)pkt; return 0; + +out_err: + giterr_set(GITERR_NET, "error parsing OK pkt-line"); + git__free(pkt); + return -1; } static int ng_pkt(git_pkt **out, const char *line, size_t len) @@ -293,9 +306,9 @@ static int ng_pkt(git_pkt **out, const char *line, size_t len) eol = line + len; - if (len < 3) + if (git__prefixncmp(line, len, "ng ")) goto out_err; - line += 3; /* skip "ng " */ + line += 3; if (!(ptr = memchr(line, ' ', eol - line))) goto out_err; @@ -337,13 +350,11 @@ static int unpack_pkt(git_pkt **out, const char *line, size_t len) { git_pkt_unpack *pkt; - GIT_UNUSED(len); - pkt = git__malloc(sizeof(*pkt)); GITERR_CHECK_ALLOC(pkt); - pkt->type = GIT_PKT_UNPACK; - if (!git__prefixcmp(line, "unpack ok")) + + if (!git__prefixncmp(line, len, "unpack ok")) pkt->unpack_ok = 1; else pkt->unpack_ok = 0; @@ -352,13 +363,17 @@ static int unpack_pkt(git_pkt **out, const char *line, size_t len) return 0; } -static int32_t parse_len(const char *line) +static int parse_len(size_t *out, const char *line, size_t linelen) { char num[PKT_LEN_SIZE + 1]; int i, k, error; int32_t len; const char *num_end; + /* Not even enough for the length */ + if (linelen < PKT_LEN_SIZE) + return GIT_EBUFS; + memcpy(num, line, PKT_LEN_SIZE); num[PKT_LEN_SIZE] = '\0'; @@ -379,7 +394,11 @@ static int32_t parse_len(const char *line) if ((error = git__strtol32(&len, num, &num_end, 16)) < 0) return error; - return len; + if (len < 0) + return -1; + + *out = (size_t) len; + return 0; } /* @@ -396,35 +415,32 @@ static int32_t parse_len(const char *line) */ int git_pkt_parse_line( - git_pkt **head, const char *line, const char **out, size_t bufflen) + git_pkt **pkt, const char **endptr, const char *line, size_t linelen) { - int ret; - int32_t len; - - /* Not even enough for the length */ - if (bufflen > 0 && bufflen < PKT_LEN_SIZE) - return GIT_EBUFS; + int error; + size_t len; - len = parse_len(line); - if (len < 0) { + if ((error = parse_len(&len, line, linelen)) < 0) { /* - * If we fail to parse the length, it might be because the - * server is trying to send us the packfile already. + * If we fail to parse the length, it might be + * because the server is trying to send us the + * packfile already or because we do not yet have + * enough data. */ - if (bufflen >= 4 && !git__prefixcmp(line, "PACK")) { + if (error == GIT_EBUFS) + ; + else if (!git__prefixncmp(line, linelen, "PACK")) giterr_set(GITERR_NET, "unexpected pack file"); - } else { + else giterr_set(GITERR_NET, "bad packet length"); - } - - return -1; + return error; } /* - * If we were given a buffer length, then make sure there is - * enough in the buffer to satisfy this line + * Make sure there is enough in the buffer to satisfy + * this line. */ - if (bufflen > 0 && bufflen < (size_t)len) + if (linelen < len) return GIT_EBUFS; /* @@ -447,38 +463,38 @@ int git_pkt_parse_line( } if (len == 0) { /* Flush pkt */ - *out = line; - return flush_pkt(head); + *endptr = line; + return flush_pkt(pkt); } len -= PKT_LEN_SIZE; /* the encoded length includes its own size */ if (*line == GIT_SIDE_BAND_DATA) - ret = data_pkt(head, line, len); + error = data_pkt(pkt, line, len); else if (*line == GIT_SIDE_BAND_PROGRESS) - ret = sideband_progress_pkt(head, line, len); + error = sideband_progress_pkt(pkt, line, len); else if (*line == GIT_SIDE_BAND_ERROR) - ret = sideband_error_pkt(head, line, len); - else if (!git__prefixcmp(line, "ACK")) - ret = ack_pkt(head, line, len); - else if (!git__prefixcmp(line, "NAK")) - ret = nak_pkt(head); - else if (!git__prefixcmp(line, "ERR ")) - ret = err_pkt(head, line, len); + error = sideband_error_pkt(pkt, line, len); + else if (!git__prefixncmp(line, len, "ACK")) + error = ack_pkt(pkt, line, len); + else if (!git__prefixncmp(line, len, "NAK")) + error = nak_pkt(pkt); + else if (!git__prefixncmp(line, len, "ERR")) + error = err_pkt(pkt, line, len); else if (*line == '#') - ret = comment_pkt(head, line, len); - else if (!git__prefixcmp(line, "ok")) - ret = ok_pkt(head, line, len); - else if (!git__prefixcmp(line, "ng")) - ret = ng_pkt(head, line, len); - else if (!git__prefixcmp(line, "unpack")) - ret = unpack_pkt(head, line, len); + error = comment_pkt(pkt, line, len); + else if (!git__prefixncmp(line, len, "ok")) + error = ok_pkt(pkt, line, len); + else if (!git__prefixncmp(line, len, "ng")) + error = ng_pkt(pkt, line, len); + else if (!git__prefixncmp(line, len, "unpack")) + error = unpack_pkt(pkt, line, len); else - ret = ref_pkt(head, line, len); + error = ref_pkt(pkt, line, len); - *out = line + len; + *endptr = line + len; - return ret; + return error; } void git_pkt_free(git_pkt *pkt) diff --git a/src/transports/smart_protocol.c b/src/transports/smart_protocol.c index ce63cb195..10a5b4e2e 100644 --- a/src/transports/smart_protocol.c +++ b/src/transports/smart_protocol.c @@ -44,7 +44,7 @@ int git_smart__store_refs(transport_smart *t, int flushes) do { if (buf->offset > 0) - error = git_pkt_parse_line(&pkt, buf->data, &line_end, buf->offset); + error = git_pkt_parse_line(&pkt, &line_end, buf->data, buf->offset); else error = GIT_EBUFS; @@ -217,7 +217,7 @@ static int recv_pkt(git_pkt **out_pkt, git_pkt_type *out_type, gitno_buffer *buf do { if (buf->offset > 0) - error = git_pkt_parse_line(&pkt, ptr, &line_end, buf->offset); + error = git_pkt_parse_line(&pkt, &line_end, ptr, buf->offset); else error = GIT_EBUFS; @@ -755,7 +755,7 @@ static int add_push_report_sideband_pkt(git_push *push, git_pkt_data *data_pkt, } while (line_len > 0) { - error = git_pkt_parse_line(&pkt, line, &line_end, line_len); + error = git_pkt_parse_line(&pkt, &line_end, line, line_len); if (error == GIT_EBUFS) { /* Buffer the data when the inner packet is split @@ -798,8 +798,8 @@ static int parse_report(transport_smart *transport, git_push *push) for (;;) { if (buf->offset > 0) - error = git_pkt_parse_line(&pkt, buf->data, - &line_end, buf->offset); + error = git_pkt_parse_line(&pkt, &line_end, + buf->data, buf->offset); else error = GIT_EBUFS; diff --git a/tests/transports/smart/packet.c b/tests/transports/smart/packet.c new file mode 100644 index 000000000..5b623a378 --- /dev/null +++ b/tests/transports/smart/packet.c @@ -0,0 +1,340 @@ +#include "clar_libgit2.h" +#include "transports/smart.h" + +enum expected_status { + PARSE_SUCCESS, + PARSE_FAILURE +}; + +static void assert_flush_parses(const char *line) +{ + size_t linelen = strlen(line) + 1; + const char *endptr; + git_pkt *pkt; + + cl_git_pass(git_pkt_parse_line((git_pkt **) &pkt, &endptr, line, linelen)); + cl_assert_equal_i(pkt->type, GIT_PKT_FLUSH); + cl_assert_equal_strn(endptr, line + 4, linelen - 4); + + git_pkt_free((git_pkt *) pkt); +} + +static void assert_data_pkt_parses(const char *line, const char *expected_data, size_t expected_len) +{ + size_t linelen = strlen(line) + 1; + const char *endptr; + git_pkt_data *pkt; + + cl_git_pass(git_pkt_parse_line((git_pkt **) &pkt, &endptr, line, linelen)); + cl_assert_equal_i(pkt->type, GIT_PKT_DATA); + cl_assert_equal_i(pkt->len, expected_len); + cl_assert_equal_strn(pkt->data, expected_data, expected_len); + + git_pkt_free((git_pkt *) pkt); +} + +static void assert_sideband_progress_parses(const char *line, const char *expected_data, size_t expected_len) +{ + size_t linelen = strlen(line) + 1; + const char *endptr; + git_pkt_progress *pkt; + + cl_git_pass(git_pkt_parse_line((git_pkt **) &pkt, &endptr, line, linelen)); + cl_assert_equal_i(pkt->type, GIT_PKT_PROGRESS); + cl_assert_equal_i(pkt->len, expected_len); + cl_assert_equal_strn(pkt->data, expected_data, expected_len); + + git_pkt_free((git_pkt *) pkt); +} + +static void assert_error_parses(const char *line, const char *expected_error, size_t expected_len) +{ + size_t linelen = strlen(line) + 1; + const char *endptr; + git_pkt_err *pkt; + + cl_git_pass(git_pkt_parse_line((git_pkt **) &pkt, &endptr, line, linelen)); + cl_assert_equal_i(pkt->type, GIT_PKT_ERR); + cl_assert_equal_i(pkt->len, expected_len); + cl_assert_equal_strn(pkt->error, expected_error, expected_len); + + git_pkt_free((git_pkt *) pkt); +} + +static void assert_ack_parses(const char *line, const char *expected_oid, enum git_ack_status expected_status) +{ + size_t linelen = strlen(line) + 1; + const char *endptr; + git_pkt_ack *pkt; + git_oid oid; + + cl_git_pass(git_oid_fromstr(&oid, expected_oid)); + + cl_git_pass(git_pkt_parse_line((git_pkt **) &pkt, &endptr, line, linelen)); + cl_assert_equal_i(pkt->type, GIT_PKT_ACK); + cl_assert_equal_oid(&pkt->oid, &oid); + cl_assert_equal_i(pkt->status, expected_status); + + git_pkt_free((git_pkt *) pkt); +} + +static void assert_nak_parses(const char *line) +{ + size_t linelen = strlen(line) + 1; + const char *endptr; + git_pkt *pkt; + + cl_git_pass(git_pkt_parse_line((git_pkt **) &pkt, &endptr, line, linelen)); + cl_assert_equal_i(pkt->type, GIT_PKT_NAK); + cl_assert_equal_strn(endptr, line + 7, linelen - 7); + + git_pkt_free((git_pkt *) pkt); +} + +static void assert_comment_parses(const char *line, const char *expected_comment) +{ + size_t linelen = strlen(line) + 1; + const char *endptr; + git_pkt_comment *pkt; + + cl_git_pass(git_pkt_parse_line((git_pkt **) &pkt, &endptr, line, linelen)); + cl_assert_equal_i(pkt->type, GIT_PKT_COMMENT); + cl_assert_equal_strn(pkt->comment, expected_comment, strlen(expected_comment)); + + git_pkt_free((git_pkt *) pkt); +} + +static void assert_ok_parses(const char *line, const char *expected_ref) +{ + size_t linelen = strlen(line) + 1; + const char *endptr; + git_pkt_ok *pkt; + + cl_git_pass(git_pkt_parse_line((git_pkt **) &pkt, &endptr, line, linelen)); + cl_assert_equal_i(pkt->type, GIT_PKT_OK); + cl_assert_equal_strn(pkt->ref, expected_ref, strlen(expected_ref)); + + git_pkt_free((git_pkt *) pkt); +} + +static void assert_unpack_parses(const char *line, bool ok) +{ + size_t linelen = strlen(line) + 1; + const char *endptr; + git_pkt_unpack *pkt; + + cl_git_pass(git_pkt_parse_line((git_pkt **) &pkt, &endptr, line, linelen)); + cl_assert_equal_i(pkt->type, GIT_PKT_UNPACK); + cl_assert_equal_i(pkt->unpack_ok, ok); + + git_pkt_free((git_pkt *) pkt); +} + +static void assert_ng_parses(const char *line, const char *expected_ref, const char *expected_msg) +{ + size_t linelen = strlen(line) + 1; + const char *endptr; + git_pkt_ng *pkt; + + cl_git_pass(git_pkt_parse_line((git_pkt **) &pkt, &endptr, line, linelen)); + cl_assert_equal_i(pkt->type, GIT_PKT_NG); + cl_assert_equal_strn(pkt->ref, expected_ref, strlen(expected_ref)); + cl_assert_equal_strn(pkt->msg, expected_msg, strlen(expected_msg)); + + git_pkt_free((git_pkt *) pkt); +} + +#define assert_ref_parses(line, expected_oid, expected_ref, expected_capabilities) \ + assert_ref_parses_(line, sizeof(line), expected_oid, expected_ref, expected_capabilities) + +static void assert_ref_parses_(const char *line, size_t linelen, const char *expected_oid, + const char *expected_ref, const char *expected_capabilities) +{ + const char *endptr; + git_pkt_ref *pkt; + git_oid oid; + + cl_git_pass(git_oid_fromstr(&oid, expected_oid)); + + cl_git_pass(git_pkt_parse_line((git_pkt **) &pkt, &endptr, line, linelen)); + cl_assert_equal_i(pkt->type, GIT_PKT_REF); + cl_assert_equal_oid(&pkt->head.oid, &oid); + cl_assert_equal_strn(pkt->head.name, expected_ref, strlen(expected_ref)); + if (expected_capabilities) + cl_assert_equal_strn(pkt->capabilities, expected_capabilities, strlen(expected_capabilities)); + else + cl_assert_equal_p(NULL, pkt->capabilities); + + git_pkt_free((git_pkt *) pkt); +} + +static void assert_pkt_fails(const char *line) +{ + const char *endptr; + git_pkt *pkt; + cl_git_fail(git_pkt_parse_line(&pkt, &endptr, line, strlen(line) + 1)); +} + +void test_transports_smart_packet__parsing_garbage_fails(void) +{ + assert_pkt_fails("0foobar"); + assert_pkt_fails("00foobar"); + assert_pkt_fails("000foobar"); + assert_pkt_fails("0001"); + assert_pkt_fails(""); + assert_pkt_fails("0"); + assert_pkt_fails("0i00"); + assert_pkt_fails("f"); +} + +void test_transports_smart_packet__flush_parses(void) +{ + assert_flush_parses("0000"); + assert_flush_parses("0000foobar"); +} + +void test_transports_smart_packet__data_pkt(void) +{ + assert_pkt_fails("000foobar"); + assert_pkt_fails("0001o"); + assert_pkt_fails("0001\1"); + assert_data_pkt_parses("0005\1", "", 0); + assert_pkt_fails("0009\1o"); + assert_data_pkt_parses("0009\1data", "data", 4); + assert_data_pkt_parses("000a\1data", "data", 5); +} + +void test_transports_smart_packet__sideband_progress_pkt(void) +{ + assert_pkt_fails("0001\2"); + assert_sideband_progress_parses("0005\2", "", 0); + assert_pkt_fails("0009\2o"); + assert_sideband_progress_parses("0009\2data", "data", 4); + assert_sideband_progress_parses("000a\2data", "data", 5); +} + +void test_transports_smart_packet__sideband_err_pkt(void) +{ + assert_pkt_fails("0001\3"); + assert_error_parses("0005\3", "", 0); + assert_pkt_fails("0009\3o"); + assert_error_parses("0009\3data", "data", 4); + assert_error_parses("000a\3data", "data", 5); +} + +void test_transports_smart_packet__ack_pkt(void) +{ + assert_ack_parses("0030ACK 0000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000", 0); + assert_ack_parses("0039ACK 0000000000000000000000000000000000000000 continue", + "0000000000000000000000000000000000000000", + GIT_ACK_CONTINUE); + assert_ack_parses("0037ACK 0000000000000000000000000000000000000000 common", + "0000000000000000000000000000000000000000", + GIT_ACK_COMMON); + assert_ack_parses("0037ACK 0000000000000000000000000000000000000000 ready", + "0000000000000000000000000000000000000000", + GIT_ACK_READY); + + /* these should fail as they don't have OIDs */ + assert_pkt_fails("0007ACK"); + assert_pkt_fails("0008ACK "); + + /* this one is missing a space and should thus fail */ + assert_pkt_fails("0036ACK00000000000000000x0000000000000000000000 ready"); + + /* the following ones have invalid OIDs and should thus fail */ + assert_pkt_fails("0037ACK 00000000000000000x0000000000000000000000 ready"); + assert_pkt_fails("0036ACK 000000000000000000000000000000000000000 ready"); + assert_pkt_fails("0036ACK 00000000000000000x0000000000000000000000ready"); + + /* this one has an invalid status and should thus fail */ + assert_pkt_fails("0036ACK 0000000000000000000000000000000000000000 read"); +} + +void test_transports_smart_packet__nak_pkt(void) +{ + assert_nak_parses("0007NAK"); + assert_pkt_fails("0007NaK"); + assert_pkt_fails("0007nak"); + assert_nak_parses("0007NAKfoobar"); + assert_pkt_fails("0007nakfoobar"); + assert_pkt_fails("0007 NAK"); +} + +void test_transports_smart_packet__error_pkt(void) +{ + assert_pkt_fails("0007ERR"); + assert_pkt_fails("0008ERRx"); + assert_error_parses("0008ERR ", "", 0); + assert_error_parses("000EERR ERRMSG", "ERRMSG", 6); +} + +void test_transports_smart_packet__comment_pkt(void) +{ + assert_comment_parses("0005#", ""); + assert_comment_parses("000B#foobar", "#fooba"); + assert_comment_parses("000C#foobar", "#foobar"); + assert_comment_parses("001A#this is a comment\nfoo", "#this is a comment\nfoo"); +} + +void test_transports_smart_packet__ok_pkt(void) +{ + assert_pkt_fails("0007ok\n"); + assert_ok_parses("0007ok ", ""); + assert_ok_parses("0008ok \n", ""); + assert_ok_parses("0008ok x", "x"); + assert_ok_parses("0009ok x\n", "x"); + assert_pkt_fails("001OK ref/foo/bar"); + assert_ok_parses("0012ok ref/foo/bar", "ref/foo/bar"); + assert_pkt_fails("0013OK ref/foo/bar\n"); + assert_ok_parses("0013ok ref/foo/bar\n", "ref/foo/bar"); +} + +void test_transports_smart_packet__ng_pkt(void) +{ + /* TODO: same as for ok pkt */ + assert_pkt_fails("0007ng\n"); + assert_pkt_fails("0008ng \n"); + assert_pkt_fails("000Bng ref\n"); + assert_pkt_fails("000Bng ref\n"); + /* TODO: is this a valid packet line? Probably not. */ + assert_ng_parses("000Ang x\n", "", "x"); + assert_ng_parses("000Fng ref msg\n", "ref", "msg"); + assert_ng_parses("000Fng ref msg\n", "ref", "msg"); +} + +void test_transports_smart_packet__unpack_pkt(void) +{ + assert_unpack_parses("000Dunpack ok", 1); + assert_unpack_parses("000Dunpack ng error-msg", 0); + /* TODO: the following tests should fail */ + assert_unpack_parses("000Aunpack", 0); + assert_unpack_parses("0011unpack foobar", 0); + assert_unpack_parses("0010unpack ng ok", 0); + assert_unpack_parses("0010unpack okfoo", 1); +} + +void test_transports_smart_packet__ref_pkt(void) +{ + assert_pkt_fails("002C0000000000000000000000000000000000000000"); + assert_pkt_fails("002D0000000000000000000000000000000000000000\n"); + assert_pkt_fails("00300000000000000000000000000000000000000000HEAD"); + assert_pkt_fails("004800000000x0000000000000000000000000000000 refs/heads/master\0multi_ack"); + assert_ref_parses( + "003F0000000000000000000000000000000000000000 refs/heads/master\0", + "0000000000000000000000000000000000000000", "refs/heads/master", ""); + assert_ref_parses( + "00480000000000000000000000000000000000000000 refs/heads/master\0multi_ack", + "0000000000000000000000000000000000000000", "refs/heads/master", "multi_ack"); + assert_ref_parses( + "00460000000000000000000000000000000000000000 refs/heads/master\0one two", + "0000000000000000000000000000000000000000", "refs/heads/master", "one two"); + assert_ref_parses( + "00310000000000000000000000000000000000000000 HEAD", + "0000000000000000000000000000000000000000", "HEAD", NULL); + assert_pkt_fails("0031000000000000000000000000000000000000000 HEAD"); + assert_ref_parses( + "00360000000000000000000000000000000000000000 HEAD HEAD", + "0000000000000000000000000000000000000000", "HEAD HEAD", NULL); +} |