#include #include #include #include #include #include #include #include #include #include #include #include "benc.h" #include "btpd_if.h" #include "iobuf.h" #include "subr.h" struct ipc { int sd; }; int ipc_open(const char *dir, struct ipc **out) { int sd = -1, err = 0; size_t plen; struct ipc *res; struct sockaddr_un addr; plen = sizeof(addr.sun_path); if (snprintf(addr.sun_path, plen, "%s/sock", dir) >= plen) return ENAMETOOLONG; addr.sun_family = AF_UNIX; if ((sd = socket(PF_UNIX, SOCK_STREAM, 0)) == -1) return errno; if (connect(sd, (struct sockaddr *)&addr, sizeof(addr)) == -1) { err = errno; close(sd); return err; } if ((res = malloc(sizeof(*res))) == NULL) { close(sd); return ENOMEM; } res->sd = sd; *out = res; return 0; } int ipc_close(struct ipc *ipc) { int err; err = close(ipc->sd); free(ipc); return err; } static int ipc_response(struct ipc *ipc, char **out, uint32_t *len) { uint32_t size; char *buf; if ((errno = read_fully(ipc->sd, &size, sizeof(size))) != 0) return errno; if (size == 0) return ECONNRESET; if ((buf = malloc(size)) == NULL) return ENOMEM; if ((errno = read_fully(ipc->sd, buf, size)) != 0) { free(buf); return errno; } *out = buf; *len = size; return 0; } static int ipc_req_res(struct ipc *ipc, const char *req, uint32_t qlen, char **res, uint32_t *rlen) { if ((errno = write_fully(ipc->sd, &qlen, sizeof(qlen))) != 0) goto error; if ((errno = write_fully(ipc->sd, req, qlen)) != 0) goto error; if ((errno = ipc_response(ipc, res, rlen)) != 0) goto error; if ((errno = benc_validate(*res, *rlen)) != 0) goto error; if (!benc_isdct(*res)) errno = EINVAL; error: return errno; } static enum ipc_code ipc_buf_req(struct ipc *ipc, struct io_buffer *iob) { int err; char *res; uint32_t reslen; err = ipc_req_res(ipc, iob->buf, iob->buf_off, &res, &reslen); free(iob->buf); if (err != 0) return IPC_COMMERR; int code; code = benc_dget_int(res, "code"); free(res); return code; } enum ipc_code btpd_die(struct ipc *ipc, int seconds) { struct io_buffer iob; buf_init(&iob, 16); if (seconds >= 0) buf_print(&iob, "l3:diei%dee", seconds); else buf_swrite(&iob, "l3:diee"); return ipc_buf_req(ipc, &iob); } enum ipc_code parse_btstat(const uint8_t *res, struct btstat **out) { int code; unsigned ntorrents; const char *tlst; code = benc_dget_int(res, "code"); if (code != IPC_OK) return code; ntorrents = benc_dget_int(res, "ntorrents"); tlst = benc_dget_lst(res, "torrents"); struct btstat *st = malloc(sizeof(struct btstat) + sizeof(struct tpstat) * ntorrents); st->ntorrents = ntorrents; int i = 0; for (const char *tp = benc_first(tlst); tp != NULL; tp = benc_next(tp)) { struct tpstat *ts = &st->torrents[i]; ts->hash = benc_dget_mema(tp, "info hash", NULL); ts->name = benc_dget_str(tp, "name", NULL); ts->state = benc_dget_int(tp, "state"); ts->peers = benc_dget_int(tp, "peers"); ts->tr_errors = benc_dget_int(tp, "tracker errors"); ts->content_got = benc_dget_int(tp, "content got"); ts->content_size = benc_dget_int(tp, "content size"); ts->pieces_got = benc_dget_int(tp, "pieces got"); ts->pieces_seen = benc_dget_int(tp, "pieces seen"); ts->torrent_pieces = benc_dget_int(tp, "torrent pieces"); ts->downloaded = benc_dget_int(tp, "downloaded"); ts->uploaded = benc_dget_int(tp, "uploaded"); ts->rate_down = benc_dget_int(tp, "rate down"); ts->rate_up = benc_dget_int(tp, "rate up"); i++; } *out = st; return IPC_OK; } void free_btstat(struct btstat *st) { for (unsigned i = 0; i < st->ntorrents; i++) { if (st->torrents[i].hash != NULL) free(st->torrents[i].hash); if (st->torrents[i].name != NULL) free(st->torrents[i].name); } free(st); } enum ipc_code btpd_stat(struct ipc *ipc, struct btstat **out) { int err; const char cmd[] = "l4:state"; uint32_t cmdlen = sizeof(cmd) - 1; char *res; uint32_t reslen; if ((err = ipc_req_res(ipc, cmd, cmdlen, &res, &reslen)) != 0) return IPC_COMMERR; err = parse_btstat(res, out); free(res); return err; } enum ipc_code btpd_add(struct ipc *ipc, const uint8_t *hash, const char *torrent, const char *content) { struct io_buffer iob; buf_init(&iob, (1 << 10)); buf_print(&iob, "l3:addd7:content%d:%s4:hash20:", (int)strlen(content), content); buf_write(&iob, hash, 20); buf_print(&iob, "7:torrent%d:%see", (int)strlen(torrent), torrent); return ipc_buf_req(ipc, &iob); } enum ipc_code btpd_del(struct ipc *ipc, const uint8_t *hash) { struct io_buffer iob; buf_init(&iob, 32); buf_swrite(&iob, "l3:del20:"); buf_write(&iob, hash, 20); buf_write(&iob, "e", 1); return ipc_buf_req(ipc, &iob); }