-
-
Save mischief/d486d090eeabacb3511ff03c09895f28 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #include <sys/param.h> /* MIN */ | |
| #include <stdio.h> | |
| #include <stdlib.h> | |
| #include <unistd.h> | |
| #include <string.h> | |
| #include <stddef.h> | |
| #include <errno.h> | |
| #include <assert.h> | |
| #include <be.h> | |
| enum { | |
| /* 9,999,999 */ | |
| BE_MAX_STR_LEN = 7, | |
| /* roughly int64 */ | |
| BE_MAX_INT_LEN = 20, | |
| }; | |
| void | |
| be_value_free(struct be_value *v) | |
| { | |
| struct be_value *ov, *head; | |
| for(; v;){ | |
| switch(v->type){ | |
| case BE_STRING: | |
| free(v->string.iov_base); | |
| break; | |
| case BE_INTEGER: | |
| break; | |
| case BE_DICTIONARY: | |
| case BE_LIST: | |
| head = STAILQ_FIRST(&v->listhead); | |
| if(head){ | |
| STAILQ_REMOVE_HEAD(&v->listhead, next); | |
| v = head; | |
| continue; | |
| } | |
| break; | |
| default: | |
| break; | |
| } | |
| free(v->key); | |
| ov = v->up; | |
| free(v); | |
| v = ov; | |
| } | |
| } | |
| static int | |
| be_alloc(int type, struct be_value *up, struct be_value **out) | |
| { | |
| struct be_value *v; | |
| v = calloc(1, sizeof(*v)); | |
| if(!v) | |
| return -ENOMEM; | |
| v->type = type; | |
| v->up = up; | |
| STAILQ_INIT(&v->listhead); | |
| *out = v; | |
| return 0; | |
| } | |
| enum { | |
| IN_DICT_FALSE, | |
| IN_DICT_KEY, | |
| IN_DICT_VALUE, | |
| }; | |
| static int | |
| be_value_parse_string(const char *buf, size_t sz, struct be_value **out) | |
| { | |
| const char *p = buf, *cp; | |
| size_t spn; | |
| ptrdiff_t pdif; | |
| int rv; | |
| unsigned long insz; | |
| struct be_value *v; | |
| *out = NULL; | |
| cp = memchr(p, ':', MIN(BE_MAX_STR_LEN, sz)); | |
| if(!cp) | |
| return -EINVAL; | |
| spn = strspn(p, "0123456789"); | |
| pdif = cp - p; | |
| if(spn != (size_t)pdif) | |
| return -EINVAL; | |
| rv = be_alloc(BE_STRING, NULL, &v); | |
| if(rv < 0) | |
| return rv; | |
| *out = v; | |
| insz = strtoul(p, NULL, 10); | |
| if(insz > sz) | |
| return -ERANGE; | |
| v->string.iov_base = malloc(insz); | |
| if(!v->string.iov_base) | |
| return -ENOMEM; | |
| v->string.iov_len = insz; | |
| memcpy(v->string.iov_base, cp+1, insz); | |
| return (cp+1-p) + insz; | |
| } | |
| static int | |
| be_value_parse_number(const char *buf, size_t sz, struct be_value **out) | |
| { | |
| const char *p = buf, *cp; | |
| size_t spn; | |
| ptrdiff_t pdif; | |
| int rv; | |
| struct be_value *v; | |
| cp = memchr(p, 'e', MIN(BE_MAX_INT_LEN, sz)); | |
| if(!cp) | |
| return -EINVAL; | |
| spn = strspn(p+1, "-0123456789"); | |
| pdif = cp - (p + 1); | |
| if(spn != (size_t)pdif) | |
| return -EINVAL; | |
| rv = be_alloc(BE_INTEGER, NULL, &v); | |
| if(rv < 0) | |
| return rv; | |
| v->integer = strtol(p+1, NULL, 10); | |
| *out = v; | |
| return cp+1-p; | |
| } | |
| static int be_value_parse_inner(int depth, const char *buf, size_t sz, struct be_value **out); | |
| static void | |
| be_insert(struct be_value *v, struct be_value *val) | |
| { | |
| if(v->type == BE_LIST || v->type == BE_DICTIONARY) | |
| STAILQ_INSERT_TAIL(&v->listhead, val, next); | |
| val->up = v; | |
| } | |
| static int | |
| be_value_parse_list(int depth, const char *buf, size_t sz, struct be_value **out) | |
| { | |
| const char *p = buf + 1; | |
| struct be_value *v, *inner; | |
| int rv, chr; | |
| chr = 1; | |
| rv = be_alloc(BE_LIST, NULL, &v); | |
| if(rv < 0) | |
| return rv; | |
| while(*p && *p != 'e'){ | |
| rv = be_value_parse_inner(depth, p, sz - chr, &inner); | |
| if(rv < 0) | |
| return rv; | |
| be_insert(v, inner); | |
| p += rv; | |
| chr += rv; | |
| } | |
| *out = v; | |
| return chr; | |
| } | |
| static int | |
| be_value_parse_dictionary(int depth, const char *buf, size_t sz, struct be_value **out) | |
| { | |
| const char *p = buf + 1; | |
| struct be_value *v, *inner, *key; | |
| int rv, chr; | |
| chr = 1; | |
| rv = be_alloc(BE_DICTIONARY, NULL, &v); | |
| if(rv < 0) | |
| return rv; | |
| while(*p && *p != 'e'){ | |
| switch(p[0]){ | |
| case '0' ... '9': | |
| rv = be_value_parse_string(p, sz, &key); | |
| if(rv < 0) | |
| return rv; | |
| p += rv; | |
| chr += rv; | |
| break; | |
| default: | |
| return -EINVAL; | |
| } | |
| rv = be_value_parse_inner(depth, p, sz - chr, &inner); | |
| if(rv < 0) | |
| return rv; | |
| /* kidnap key */ | |
| inner->key = key->string.iov_base; | |
| key->string.iov_base = NULL; | |
| be_value_free(key); | |
| be_insert(v, inner); | |
| p += rv; | |
| chr += rv; | |
| } | |
| *out = v; | |
| return chr; | |
| } | |
| static int | |
| be_value_parse_inner(int depth, const char *buf, size_t sz, struct be_value **out) | |
| { | |
| if(depth > 128) | |
| return -EINVAL; | |
| if(sz < 2) | |
| return -ENOMSG; | |
| depth++; | |
| switch(buf[0]){ | |
| case '0' ... '9': | |
| return be_value_parse_string(buf, sz, out); | |
| case 'i': | |
| return be_value_parse_number(buf, sz, out); | |
| case 'l': | |
| return be_value_parse_list(depth, buf, sz, out); | |
| case 'd': | |
| return be_value_parse_dictionary(depth, buf, sz, out); | |
| } | |
| return -EINVAL; | |
| } | |
| int | |
| be_value_parse(const char *buf, size_t sz, struct be_value **out) | |
| { | |
| return be_value_parse_inner(0, buf, sz, out); | |
| } | |
| void | |
| be_walk(struct be_value *be, int (*cb)(struct be_value *be, void *arg), void *arg) | |
| { | |
| struct be_value *inner; | |
| if(!be) | |
| return; | |
| cb(be, arg); | |
| switch(be->type){ | |
| case BE_LIST: | |
| [[fallthrough]]; | |
| case BE_DICTIONARY: | |
| STAILQ_FOREACH(inner, &be->listhead, next){ | |
| be_walk(inner, cb, arg); | |
| } | |
| break; | |
| } | |
| } | |
| struct be_value* | |
| be_find(struct be_value *be, const char *key) | |
| { | |
| struct be_value *inner; | |
| if(!be || !(be->type == BE_DICTIONARY)) | |
| return NULL; | |
| STAILQ_FOREACH(inner, &be->listhead, next){ | |
| if(strcmp(key, inner->key) == 0) | |
| return inner; | |
| } | |
| return NULL; | |
| } | |
| int be_message_new(struct be_message *m, const char *buf) | |
| { | |
| m->buf = buf; | |
| m->sz = strlen(buf); | |
| m->cur = 0; | |
| return 0; | |
| } | |
| int | |
| be_peek_type(struct be_message *m, int *type) | |
| { | |
| const char *cp; | |
| size_t spn; | |
| ptrdiff_t pdif; | |
| if(m->sz - m->cur == 0) | |
| return 0; | |
| if(m->sz - m->cur < 2) | |
| return -ENOMSG; | |
| switch(m->buf[m->cur]){ | |
| case '0' ... '9': | |
| /* string, with a limit on size */ | |
| cp = memchr(&m->buf[m->cur], ':', BE_MAX_STR_LEN); | |
| if(!cp) | |
| return -EINVAL; | |
| spn = strspn(&m->buf[m->cur], "0123456789"); | |
| pdif = cp - &m->buf[m->cur]; | |
| if(spn != (size_t)pdif) | |
| return -EINVAL; | |
| *type = BE_STRING; | |
| return 1; | |
| case 'i': | |
| cp = memchr(&m->buf[m->cur], 'e', BE_MAX_INT_LEN); | |
| if(!cp) | |
| return -EINVAL; | |
| spn = strspn(&m->buf[m->cur], "0123456789"); | |
| pdif = cp - &m->buf[m->cur]; | |
| if(spn != (size_t)pdif) | |
| return -EINVAL; | |
| *type = BE_INTEGER; | |
| return 1; | |
| case 'l': return BE_LIST; | |
| case 'd': return BE_DICTIONARY; | |
| default: | |
| fprintf(stderr, "default\n"); | |
| return -ENOMSG; | |
| } | |
| return 0; | |
| } | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #include <sys/uio.h> | |
| #include <sys/queue.h> | |
| #include <inttypes.h> | |
| enum { | |
| BE_STRING = 0, | |
| BE_INTEGER = 1, | |
| BE_LIST = 2, | |
| BE_DICTIONARY = 3, | |
| }; | |
| struct be_value; | |
| STAILQ_HEAD(be_value_head, be_value); | |
| struct be_value | |
| { | |
| int type; | |
| struct be_value *up; | |
| char *key; | |
| STAILQ_ENTRY(be_value) next; | |
| union { | |
| long long integer; | |
| struct iovec string; | |
| struct be_value_head listhead; | |
| }; | |
| }; | |
| void be_value_free(struct be_value *v); | |
| int be_value_parse(const char *buf, size_t sz, struct be_value **out); | |
| void be_walk(struct be_value *be, int (*cb)(struct be_value *be, void *arg), void *arg); | |
| struct be_value *be_find(struct be_value *be, const char *key); | |
| int be_value_new(const char *buf, size_t sz, struct be_value **out); | |
| struct be_message | |
| { | |
| const char *buf; | |
| size_t sz; | |
| size_t cur; | |
| }; | |
| int be_message_new(struct be_message *m, const char *buf); | |
| int be_peek_type(struct be_message *m, int *type); | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #include <sys/queue.h> | |
| enum { | |
| INFOHASH_SHA1 = 1, | |
| INFOHASH_SHA1HEXLEN = 40, | |
| }; | |
| struct infohash; | |
| STAILQ_HEAD(infohash_head, infohash); | |
| struct infohash | |
| { | |
| STAILQ_ENTRY(infohash) next; | |
| int type; | |
| char *hash; | |
| }; | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #include <fcntl.h> | |
| #include <err.h> | |
| #include <string.h> | |
| #include <netinet/in.h> | |
| #include <arpa/inet.h> | |
| #include <stdio.h> | |
| #include <systemd/sd-event.h> | |
| #include <lsd.h> | |
| static int | |
| lsd_handler(sd_event_source *s, int fd, uint32_t revents, void *v) | |
| { | |
| int rv; | |
| struct lsd *lsd = v; | |
| __attribute__((cleanup(lsd_frame_freep))) struct lsd_frame *frame = NULL; | |
| __attribute__((cleanup(lsd_frame_info_freep))) struct lsd_frame_info *info = NULL; | |
| struct infohash *ih; | |
| (void)s; | |
| (void)fd; | |
| (void)revents; | |
| rv = lsd_recv(lsd, &frame); | |
| if(rv < 0){ | |
| warnx("lsd_recv: %s", strerror(-rv)); | |
| sd_event_exit(sd_event_source_get_event(s), rv); | |
| return rv; | |
| } | |
| rv = lsd_frame_parse(frame, &info); | |
| if(rv < 0){ | |
| warnx("lsd_frame_parse: %s", strerror(-rv)); | |
| sd_event_exit(sd_event_source_get_event(s), rv); | |
| return rv; | |
| } | |
| printf("received LSD frame from %s\n", info->remote); | |
| printf("bt port %hu\n", info->port); | |
| printf("cookie: %s\n", info->cookie); | |
| printf("hashes:\n"); | |
| STAILQ_FOREACH(ih, &info->infohashes, next){ | |
| printf("%s\n", ih->hash); | |
| } | |
| return 0; | |
| } | |
| int | |
| main(int argc, char *argv[]) | |
| { | |
| int rv; | |
| __attribute__((cleanup(lsd_freep))) struct lsd *lsdfd = NULL; | |
| __attribute__((cleanup(peer_freep))) struct peer *p = NULL; | |
| __attribute__((cleanup(sd_event_unrefp))) sd_event *ev = NULL; | |
| (void)argc; | |
| (void)argv; | |
| if((rv = lsd_init(&lsdfd, O_NONBLOCK)) < 0) | |
| errx(1, "lsd_init: %s", strerror(-rv)); | |
| if((rv = sd_event_default(&ev)) < 0) | |
| errx(1, "sd_event_default: %s", strerror(-rv)); | |
| if((rv = sd_event_add_io(ev, NULL, lsdfd->fd, EPOLLIN, lsd_handler, lsdfd)) < 0) | |
| errx(1, "sd_event_add_io: %s", strerror(-rv)); | |
| rv = sd_event_loop(ev); | |
| if(rv < 0) | |
| errx(1, "sd_event_loop: %s", strerror(-rv)); | |
| return 0; | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #include <unistd.h> | |
| #include <stdio.h> | |
| #include <stdlib.h> | |
| #include <string.h> | |
| #include <errno.h> | |
| #include <assert.h> | |
| #include <be.h> | |
| #ifndef __AFL_FUZZ_TESTCASE_LEN | |
| ssize_t fuzz_len; | |
| #define __AFL_FUZZ_TESTCASE_LEN fuzz_len | |
| unsigned char fuzz_buf[1024000]; | |
| #define __AFL_FUZZ_TESTCASE_BUF fuzz_buf | |
| #define __AFL_FUZZ_INIT() void sync(void); | |
| #define __AFL_LOOP(x) ((fuzz_len = read(0, fuzz_buf, sizeof(fuzz_buf))) > 0 ? 1 : 0) | |
| #define __AFL_INIT() sync() | |
| #endif | |
| __AFL_FUZZ_INIT(); | |
| int | |
| main(int argc, char *argv[]) | |
| { | |
| struct be_value *be; | |
| int rv; | |
| (void) argc; | |
| (void) argv; | |
| #ifdef __AFL_HAVE_MANUAL_CONTROL | |
| __AFL_INIT(); | |
| #endif | |
| // must be after __AFL_INIT | |
| // and before __AFL_LOOP! | |
| unsigned char *buf = __AFL_FUZZ_TESTCASE_BUF; | |
| while (__AFL_LOOP(10000)) { | |
| // don't use the macro directly in a call! | |
| int len = __AFL_FUZZ_TESTCASE_LEN; | |
| // check for a required/useful minimum input length | |
| if (len < 2) continue; | |
| /* Setup function call, e.g. struct target *tmp = libtarget_init() */ | |
| /* Call function to be fuzzed, e.g.: */ | |
| be = NULL; | |
| rv = be_value_parse(buf, len, &be); | |
| (void)rv; | |
| /* Reset state. e.g. libtarget_free(tmp) */ | |
| if(be) | |
| be_value_free(be); | |
| } | |
| return 0; | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #include <sys/types.h> | |
| #include <arpa/inet.h> | |
| #include <sys/socket.h> | |
| #include <netinet/in.h> | |
| #include <fcntl.h> | |
| #include <stdlib.h> | |
| #include <string.h> | |
| #include <unistd.h> | |
| #include <assert.h> | |
| #include <limits.h> | |
| #include <errno.h> | |
| #include <lsd.h> | |
| void | |
| lsd_freep(struct lsd **v) | |
| { | |
| if(*v){ | |
| close((*v)->fd); | |
| free(*v); | |
| } | |
| } | |
| void | |
| lsd_frame_freep(struct lsd_frame **v) | |
| { | |
| if(*v) | |
| free(*v); | |
| } | |
| void | |
| lsd_frame_info_freep(struct lsd_frame_info **v) | |
| { | |
| struct infohash *ih, *next; | |
| if(!*v) | |
| return; | |
| ih = STAILQ_FIRST(&(*v)->infohashes); | |
| while(ih){ | |
| next = STAILQ_NEXT(ih, next); | |
| free(ih->hash); | |
| free(ih); | |
| ih = next; | |
| } | |
| free((*v)->cookie); | |
| free(*v); | |
| } | |
| int | |
| lsd_init(struct lsd **out, int setfl) | |
| { | |
| struct lsd *lsd; | |
| int fd, serr, y = 1; | |
| struct sockaddr_in sa; | |
| struct ip_mreq mreq; | |
| fd = socket(AF_INET, SOCK_DGRAM, 0); | |
| if(fd < 0) | |
| return -errno; | |
| if(fcntl(fd, F_SETFD, FD_CLOEXEC) < 0) | |
| goto err; | |
| if(setfl) | |
| if(fcntl(fd, F_SETFL, setfl) < 0) | |
| goto err; | |
| if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &y, sizeof(y)) < 0) | |
| goto err; | |
| //memset(&sa, 0, sizeof(sa)); | |
| sa = (struct sockaddr_in){ | |
| .sin_family = AF_INET, | |
| .sin_addr = (struct in_addr){ .s_addr = htonl(INADDR_ANY) }, | |
| .sin_port = htons(6771), | |
| }; | |
| if(bind(fd, (struct sockaddr*)&sa, sizeof(sa)) < 0) | |
| goto err; | |
| mreq = (struct ip_mreq){ | |
| .imr_multiaddr = (struct in_addr){ .s_addr = inet_addr("239.192.152.143") }, | |
| .imr_interface = (struct in_addr){ .s_addr = htonl(INADDR_ANY) }, | |
| }; | |
| if(setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char*) &mreq, sizeof(mreq)) < 0) | |
| goto err; | |
| lsd = calloc(1, sizeof(*lsd)); | |
| if(!lsd) | |
| goto err; | |
| lsd->fd = fd; | |
| *out = lsd; | |
| return 0; | |
| err: | |
| serr = errno; | |
| close(fd); | |
| return -serr; | |
| } | |
| int | |
| lsd_recv(struct lsd *lsd, struct lsd_frame **out) | |
| { | |
| int rv; | |
| socklen_t salen; | |
| struct lsd_frame *frame; | |
| assert(lsd); | |
| assert(out); | |
| frame = calloc(1, sizeof(*frame)); | |
| if(!frame) | |
| return -ENOMEM; | |
| salen = sizeof(frame->sa); | |
| rv = recvfrom(lsd->fd, frame->frame, sizeof(frame->frame)-1, 0, (struct sockaddr*)&frame->sa, &salen); | |
| if(rv < 0){ | |
| free(frame); | |
| return -errno; | |
| } | |
| frame->len = rv; | |
| frame->frame[rv] = '\0'; | |
| assert(strlcpy(frame->remote, "Unknown", sizeof(frame->remote)) < sizeof(frame->remote)); | |
| switch(frame->sa.ss_family){ | |
| case AF_INET: | |
| inet_ntop(frame->sa.ss_family, &((struct sockaddr_in*)&frame->sa)->sin_addr, frame->remote, salen); | |
| break; | |
| case AF_INET6: | |
| inet_ntop(frame->sa.ss_family, &((struct sockaddr_in6*)&frame->sa)->sin6_addr, frame->remote, salen); | |
| break; | |
| } | |
| *out = frame; | |
| return 0; | |
| } | |
| int | |
| lsd_frame_parse(struct lsd_frame *frame, struct lsd_frame_info **out) | |
| { | |
| unsigned long port; | |
| const char *p, *ep; | |
| struct lsd_frame_info *info; | |
| struct infohash *ih; | |
| p = frame->frame; | |
| ep = strstr(p, "\r\n"); | |
| if(!ep) | |
| return -ENOMSG; | |
| if(strncmp(p, "BT-SEARCH", 9) != 0) | |
| return -ENOMSG; | |
| p = ep+2; | |
| ep = strstr(p, "\r\n"); | |
| if(!ep) | |
| return -ENOMSG; | |
| if(strncmp(p, "Host: ", 6) != 0) | |
| return -ENOMSG; | |
| p = ep+2; | |
| ep = strstr(p, "\r\n"); | |
| if(!ep) | |
| return -ENOMSG; | |
| if(strncmp(p, "Port: ", 6) != 0) | |
| return -ENOMSG; | |
| p += 6; | |
| port = strtoul(p, NULL, 10); | |
| if(port > USHRT_MAX) | |
| return -ERANGE; | |
| p = ep+2; | |
| info = calloc(1, sizeof(*info)); | |
| info->sa = frame->sa; | |
| assert(strlcpy(info->remote, frame->remote, sizeof(info->remote)) <= sizeof(info->remote)); | |
| info->port = port; | |
| STAILQ_INIT(&info->infohashes); | |
| while(p && *p){ | |
| ep = strstr(p, "\r\n"); | |
| if(!ep) | |
| break; | |
| if(strncmp(p, "Infohash: ", 10) == 0){ | |
| /* malformed? TODO: decode hex */ | |
| if(ep - (p+10) != INFOHASH_SHA1HEXLEN) | |
| goto next; | |
| ih = calloc(1, sizeof(*ih)); | |
| if(!ih) | |
| goto err; | |
| ih->type = INFOHASH_SHA1; | |
| ih->hash = strndup(p+10, INFOHASH_SHA1HEXLEN); | |
| STAILQ_INSERT_TAIL(&info->infohashes, ih, next); | |
| } else if(strncmp(p, "cookie: ", 8) == 0 && !info->cookie){ | |
| info->cookie = strndup(p+8, ep - (p+8)); | |
| } | |
| next: | |
| p = ep+2; | |
| } | |
| *out = info; | |
| return 0; | |
| err: | |
| lsd_frame_info_freep(&info); | |
| return -ENOMEM; | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #include <net/ethernet.h> | |
| #include <arpa/inet.h> | |
| #include <bt.h> | |
| struct lsd | |
| { | |
| int fd; | |
| }; | |
| struct lsd_frame | |
| { | |
| struct sockaddr_storage sa; | |
| char remote[INET6_ADDRSTRLEN]; | |
| char frame[ETH_DATA_LEN]; | |
| int len; | |
| }; | |
| struct lsd_frame_info | |
| { | |
| struct sockaddr_storage sa; | |
| char remote[INET6_ADDRSTRLEN]; | |
| uint16_t port; | |
| char *cookie; | |
| struct infohash_head infohashes; | |
| }; | |
| void lsd_freep(struct lsd **v); | |
| void lsd_frame_freep(struct lsd_frame **v); | |
| void lsd_frame_info_freep(struct lsd_frame_info **v); | |
| int lsd_init(struct lsd **out, int setfl); | |
| int lsd_recv(struct lsd *lsd, struct lsd_frame **out); | |
| int lsd_frame_parse(struct lsd_frame *frame, struct lsd_frame_info **out); | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #include <fcntl.h> | |
| #include <err.h> | |
| #include <string.h> | |
| #include <netinet/in.h> | |
| #include <arpa/inet.h> | |
| #include <stdio.h> | |
| #include <systemd/sd-event.h> | |
| #include <lsd.h> | |
| static int | |
| lsd_handler(sd_event_source *s, int fd, uint32_t revents, void *v) | |
| { | |
| int rv; | |
| struct lsd *lsd = v; | |
| __attribute__((cleanup(lsd_frame_freep))) struct lsd_frame *frame = NULL; | |
| __attribute__((cleanup(lsd_frame_info_freep))) struct lsd_frame_info *info = NULL; | |
| struct infohash *ih; | |
| (void)s; | |
| (void)fd; | |
| (void)revents; | |
| rv = lsd_recv(lsd, &frame); | |
| if(rv < 0){ | |
| warnx("lsd_recv: %s", strerror(-rv)); | |
| sd_event_exit(sd_event_source_get_event(s), rv); | |
| return rv; | |
| } | |
| rv = lsd_frame_parse(frame, &info); | |
| if(rv < 0){ | |
| warnx("lsd_frame_parse: %s", strerror(-rv)); | |
| sd_event_exit(sd_event_source_get_event(s), rv); | |
| return rv; | |
| } | |
| printf("received LSD frame from %s\n", info->remote); | |
| printf("bt port %hu\n", info->port); | |
| printf("cookie: %s\n", info->cookie); | |
| printf("hashes:\n"); | |
| STAILQ_FOREACH(ih, &info->infohashes, next){ | |
| printf("%s\n", ih->hash); | |
| } | |
| return 0; | |
| } | |
| int | |
| main(int argc, char *argv[]) | |
| { | |
| int rv; | |
| __attribute__((cleanup(lsd_freep))) struct lsd *lsdfd = NULL; | |
| __attribute__((cleanup(sd_event_unrefp))) sd_event *ev = NULL; | |
| (void)argc; | |
| (void)argv; | |
| if((rv = lsd_init(&lsdfd, O_NONBLOCK)) < 0) | |
| errx(1, "lsd_init: %s", strerror(-rv)); | |
| if((rv = sd_event_default(&ev)) < 0) | |
| errx(1, "sd_event_default: %s", strerror(-rv)); | |
| if((rv = sd_event_add_io(ev, NULL, lsdfd->fd, EPOLLIN, lsd_handler, lsdfd)) < 0) | |
| errx(1, "sd_event_add_io: %s", strerror(-rv)); | |
| rv = sd_event_loop(ev); | |
| if(rv < 0) | |
| errx(1, "sd_event_loop: %s", strerror(-rv)); | |
| return 0; | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #include <sys/types.h> | |
| #include <sys/socket.h> | |
| #include <netinet/in.h> | |
| #include <unistd.h> | |
| #include <errno.h> | |
| #include <stdlib.h> | |
| #include <peer.h> | |
| int | |
| peer_new(struct peer **peer, uint16_t port) | |
| { | |
| struct peer *p; | |
| int rv, fd = -1, y = 1; | |
| struct sockaddr_in sa; | |
| p = calloc(1, sizeof(*p)); | |
| if(!p) | |
| return -ENOMEM; | |
| *p = (struct peer){ | |
| .port = port, | |
| }; | |
| fd = socket(AF_INET, SOCK_DGRAM, 0); | |
| if(fd < 0){ | |
| rv = -errno; | |
| goto err; | |
| } | |
| p->fd = fd; | |
| if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &y, sizeof(y)) < 0){ | |
| rv = -errno; | |
| goto err; | |
| } | |
| sa = (struct sockaddr_in){ | |
| .sin_family = AF_INET, | |
| .sin_addr = (struct in_addr){ .s_addr = htonl(INADDR_ANY) }, | |
| .sin_port = htons(port), | |
| }; | |
| if(bind(fd, (struct sockaddr*)&sa, sizeof(sa)) < 0){ | |
| rv = -errno; | |
| goto err; | |
| } | |
| *peer = p; | |
| return 0; | |
| err: | |
| if(fd > 0) | |
| close(fd); | |
| free(p); | |
| return rv; | |
| } | |
| void | |
| peer_freep(struct peer **p) | |
| { | |
| if(!*p) | |
| return; | |
| close((*p)->fd); | |
| free(*p); | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #include <sys/queue.h> | |
| #include <stdint.h> | |
| struct remote; | |
| STAILQ_HEAD(remote_head, remote); | |
| struct remote { | |
| STAILQ_ENTRY(remote) next; | |
| struct sockaddr_storage sa; | |
| socklen_t sasz; | |
| }; | |
| struct peer { | |
| uint16_t port; | |
| int fd; | |
| }; | |
| int peer_new(struct peer **p, uint16_t port); | |
| void peer_freep(struct peer **p); | |
| int peer_send(unsigned char *buf, size_t sz, struct sockaddr_storage *sa, socklen_t sasz); | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #include <sys/param.h> | |
| #include <stdio.h> | |
| #include <string.h> | |
| #include <errno.h> | |
| #include <assert.h> | |
| #include <be.h> | |
| static int | |
| be_count_depth(struct be_value *be) | |
| { | |
| int depth; | |
| depth = 0; | |
| if(!be) | |
| return 0; | |
| for(be = be->up; be; be = be->up) | |
| depth++; | |
| return depth; | |
| } | |
| static int | |
| be_pp(struct be_value *be, void *arg) | |
| { | |
| int depth; | |
| const char indent[] = " "; | |
| const int nindent = sizeof(indent); | |
| (void) arg; | |
| depth = be_count_depth(be); | |
| printf("%.*s", MIN(depth, nindent), indent); | |
| if(be->key) | |
| printf("%lu:%s = ", strlen(be->key), be->key); | |
| switch (be->type) { | |
| case BE_INTEGER: | |
| printf("i%llde\n", be->integer); | |
| break; | |
| case BE_STRING: | |
| printf("%lu:%s\n", be->string.iov_len, be->string.iov_base); | |
| break; | |
| case BE_LIST: | |
| printf("l\n"); | |
| break; | |
| case BE_DICTIONARY: | |
| printf("d\n"); | |
| break; | |
| default: | |
| assert(0); | |
| } | |
| return 0; | |
| } | |
| static void | |
| be_pp_errno(struct be_value *be, void *arg) | |
| { | |
| int *err = arg; | |
| *err = be->integer; | |
| } | |
| static void | |
| assert_errno(int v, int err) | |
| { | |
| assert(v == err); | |
| } | |
| int | |
| main(int argc, char *argv[]) | |
| { | |
| int type; | |
| struct be_message m; | |
| int rv; | |
| struct be_value *be; | |
| (void)argc; | |
| (void)argv; | |
| const char *buf = "4:eggs"; | |
| size_t len = strlen(buf); | |
| /* | |
| rv = be_value_parse(buf, len, &be); | |
| assert(rv > 0); | |
| assert(be->type == BE_STRING); | |
| assert(strcmp(be->string, "eggs") == 0); | |
| be_value_free(be); | |
| buf = "i42e"; | |
| len = strlen(buf); | |
| rv = be_value_parse(buf, len, &be); | |
| assert(rv > 0); | |
| assert(be->type == BE_INTEGER); | |
| assert(be->integer == 42); | |
| be_value_free(be); | |
| buf = "l4:spam4:eggse"; | |
| len = strlen(buf); | |
| rv = be_value_parse(buf, len, &be); | |
| assert(rv > 0); | |
| assert(be->type == BE_LIST); | |
| //assert(be->integer == 42); | |
| be_value_free(be); | |
| */ | |
| buf = "d3:cow3:moo4:spam4:eggs1:tld3:fooi42eei69eee"; | |
| len = strlen(buf); | |
| rv = be_value_parse(buf, len, &be); | |
| assert(rv > 0); | |
| assert(be->type == BE_DICTIONARY); | |
| struct be_value *cow; | |
| cow = be_find(be, "cow"); | |
| assert(cow); | |
| assert(cow->type == BE_STRING); | |
| assert(strcmp(cow->string.iov_base, "moo") == 0); | |
| be_walk(be, be_pp, NULL); | |
| be_value_free(be); | |
| return 0; | |
| assert(be_message_new(&m, "4:eggs") == 0); | |
| assert(be_peek_type(&m, &type) == 1); | |
| assert(type == BE_STRING); | |
| assert(be_message_new(&m, "9999999:eggs") == 0); | |
| assert_errno(be_peek_type(&m, &type), -EINVAL); | |
| assert(type == BE_STRING); | |
| assert(be_message_new(&m, "i40e") == 0); | |
| assert(be_peek_type(&m, &type) == 1); | |
| assert(type == BE_INTEGER); | |
| return 0; | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #include <sys/param.h> | |
| #include <stdio.h> | |
| #include <stdlib.h> | |
| #include <string.h> | |
| #include <err.h> | |
| #include <assert.h> | |
| #include <be.h> | |
| static int | |
| be_count_depth(struct be_value *be) | |
| { | |
| int depth; | |
| depth = 0; | |
| if(!be) | |
| return 0; | |
| for(be = be->up; be; be = be->up) | |
| depth++; | |
| return depth; | |
| } | |
| static int | |
| be_pp(struct be_value *be, void *arg) | |
| { | |
| int depth; | |
| const char indent[] = " "; | |
| const int nindent = sizeof(indent); | |
| (void) arg; | |
| depth = be_count_depth(be); | |
| printf("%.*s", MIN(depth, nindent), indent); | |
| if(be->key) | |
| printf("%lu:%s = ", strlen(be->key), be->key); | |
| switch (be->type) { | |
| case BE_INTEGER: | |
| printf("i%llde\n", be->integer); | |
| break; | |
| case BE_STRING: | |
| printf("%lu:%s\n", be->string.iov_len, be->string.iov_base); | |
| break; | |
| case BE_LIST: | |
| printf("l\n"); | |
| break; | |
| case BE_DICTIONARY: | |
| printf("d\n"); | |
| break; | |
| default: | |
| assert(0); | |
| } | |
| return 0; | |
| } | |
| int | |
| main(int argc, char *argv[]) | |
| { | |
| int rv; | |
| struct be_value *be; | |
| (void)argc; | |
| (void)argv; | |
| const char *buf; | |
| size_t len; | |
| FILE *fp; | |
| buf = calloc(1, 1024*1024); | |
| if(!buf) | |
| err(1, "calloc"); | |
| fp = fopen(argv[1], "rb"); | |
| if(!fp) | |
| err(1, "fopen"); | |
| len = fread(buf, 1, 1024*1024, fp); | |
| if(len <= 0) | |
| err(1, "fread"); | |
| rv = be_value_parse(buf, len, &be); | |
| if(rv < 0) | |
| errx(1, "be_value_parse: %s", strerror(-rv)); | |
| be_walk(be, be_pp, NULL); | |
| be_value_free(be); | |
| return 0; | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment