unworkable-0.53/0000755000014500017510000000000011310321004013060 5ustar michaelstaffunworkable-0.53/trace.c0000644000014500017510000000335211065035677014356 0ustar michaelstaff/* $Id: trace.c,v 1.8 2008-09-19 23:54:39 niallo Exp $ */ /* * Copyright (c) 2006, 2007, 2008 Niall O'Higgins * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include "includes.h" char *unworkable_trace = NULL; FILE *out = NULL; struct torrent *mytorrent = NULL; static void vtrace(const char *, va_list); void trace(const char *fmt, ...) { va_list vap; va_start(vap, fmt); vtrace(fmt, vap); va_end(vap); } static void vtrace(const char *fmt, va_list vap) { time_t t; char tbuf[32]; if (unworkable_trace == NULL) return; if (out == NULL) if ((out = fopen(unworkable_trace, "w")) == NULL) err(1, "vtrace: fopen failure"); t = time(NULL); strftime(tbuf, sizeof(tbuf), "[%Y-%m-%d %T] ", gmtime(&t)); (void)fputs(tbuf, out); (void)fputs("-> ", out); (void)vfprintf(out, fmt, vap); fputc('\n', out); fflush(out); } int terminate_handler(void) { if (mytorrent != NULL) torrent_fastresume_dump(mytorrent); if (out != NULL) fclose(out); exit(1); } unworkable-0.53/parse.y0000644000014500017510000002034011061364624014405 0ustar michaelstaff/* $Id: parse.y,v 1.58 2008-09-09 03:12:20 niallo Exp $ */ /* * Copyright (c) 2006, 2007, 2008 Niall O'Higgins * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * Parser for BitTorrent `bencode' format. * See http://wiki.theory.org/BitTorrentSpecification */ %{ #include #include #include #include #include #include #include #include #include "includes.h" #ifndef LLONG_MAX #define LLONG_MAX 9223372036854775807LL #endif #ifndef LLONG_MIN #define LLONG_MIN (-LLONG_MAX - 1LL) #endif /* Assume no more than 16 nested dictionaries/lists. */ #define BENC_STACK_SIZE 16 /* Initial size of lexer buffer. */ #define BENC_BUFFER_SIZE 128 /* How much to grow lexer buffer by. */ #define BENC_BUFFER_INCREMENT 20480 /* Internal node-stack functions */ static struct benc_node *benc_stack_pop(void); static struct benc_node *benc_stack_peek(void); static void benc_stack_push(struct benc_node *); static long bstrlen = 0; static int bstrflag = 0; static int bdone = 0; static int bcdone = 0; static struct benc_node *bstack[BENC_STACK_SIZE]; static int bstackidx = 0; BUF *in = NULL; %} %union { long long number; char *string; struct benc_node *benc_node; size_t len; } %token COLON %token END %token INT_START %token DICT_START %token LIST_START %token STRING %type bstring %type bint %type number %type bdict_entries %type bdict_entry %type blist_entries %type bdict %type blist %start bencode %% bencode : /* empty */ | bencode bstring { benc_node_add(root, $2); } | bencode bint { benc_node_add(root, $2); } | bencode bdict { benc_node_add(root, $2); } | bencode blist { benc_node_add(root, $2); } ; number : STRING { long long lval; const char *errstr; lval = strtonum($1, LLONG_MIN, LLONG_MAX, &errstr); if (errstr) { yyerror("%s is %s", $1, errstr); xfree($1); YYERROR; } else { $$ = lval; if (bstrflag == 1) bstrlen = lval; } xfree($1); } ; /* * Special hack for bstrings. */ bstrflag : { bstrflag = 1; } ; bstring : bstrflag number COLON STRING { struct benc_node *node; node = benc_node_create(); node->body.string.len = $2; node->body.string.value = $4; node->flags = BSTRING; $$ = node; } ; bint : INT_START number END { struct benc_node *node; node = benc_node_create(); node->body.number = $2; node->flags = BINT; $$ = node; } ; blist : LIST_START { /* * Push the list node onto the stack before continuing * so that sub-elements can add themselves to it. */ struct benc_node *node; node = benc_node_create(); node->flags = BLIST; benc_stack_push(node); } blist_entries END { /* * Pop list node and link the remaining sub-element. */ struct benc_node *node; node = benc_stack_pop(); benc_node_add_head(node, $3); $$ = node; } ; blist_entries : bint { $$ = $1; } | bstring { $$ = $1; } | blist { $$ = $1; } | bdict { $$ = $1; } | blist_entries bint { benc_node_add(benc_stack_peek(), $2); } | blist_entries bstring { benc_node_add(benc_stack_peek(), $2); } | blist_entries blist { benc_node_add(benc_stack_peek(), $2); } | blist_entries bdict { benc_node_add(benc_stack_peek(), $2); } ; bdict_entry : bstring bint { struct benc_node *node; node = benc_node_create(); node->flags = BINT|BDICT_ENTRY; node->body.dict_entry.key = $1->body.string.value; node->body.dict_entry.value = $2; xfree($1); $$ = node; } | bstring bstring { struct benc_node *node; node = benc_node_create(); node->flags = BSTRING|BDICT_ENTRY; node->body.dict_entry.key = $1->body.string.value; node->body.dict_entry.value = $2; xfree($1); $$ = node; } | bstring blist { struct benc_node *node; node = benc_node_create(); node->flags = BLIST|BDICT_ENTRY; node->body.dict_entry.key = $1->body.string.value; node->body.dict_entry.value = $2; xfree($1); $$ = node; } | bstring bdict { struct benc_node *node; node = benc_node_create(); node->flags = BDICT|BDICT_ENTRY; node->body.dict_entry.key = $1->body.string.value; node->body.dict_entry.value = $2; xfree($1); $$ = node; } bdict_entries : bdict_entry { $$ = $1; } | bdict_entries bdict_entry { benc_node_add(benc_stack_peek(), $2); } ; bdict : DICT_START { /* * Push the dict node onto the stack before continuing * so that sub-elements can add themselves to it. */ struct benc_node *node; node = benc_node_create(); node->flags = BDICT; benc_stack_push(node); } bdict_entries END { /* * Pop dict node and link the remaining sub-element. */ struct benc_node *node; node = benc_stack_pop(); node->end = $4; benc_node_add_head(node, $3); $$ = node; } ; %% int yylex(void) { char *buf, *p; int c; long buflen = BENC_BUFFER_SIZE, i = 0; buf = xmalloc(buflen); p = buf; memset(buf, '\0', buflen); for (;;) { if (i == buflen) { ptrdiff_t p_offset = p - buf; buflen += BENC_BUFFER_INCREMENT; trace("yylex() realloc"); buf = xrealloc(buf, buflen); trace("yylex() realloc done"); /* ensure pointers are not invalidated after realloc */ p = buf + p_offset; /* NUL-fill the new memory */ memset(p, '\0', BENC_BUFFER_INCREMENT); } if (bstrlen == 0 && bstrflag == 1 && bcdone == 1) { yylval.string = buf; bstrlen = bstrflag = bcdone = 0; return (STRING); } if ((c = buf_getc(in)) == EOF) { xfree(buf); return (0); } /* if we are in string context, ignore special chars */ if ((c == ':' && bdone == 0 && bcdone == 1) || (c != ':' && bstrflag == 1)) goto skip; switch (c) { case ':': if (bdone == 0 && i > 0) { yylval.string = buf; bdone = 1; bcdone = 0; (void)buf_ungetc(in); return (STRING); } else { bdone = 0; bcdone = 1; xfree(buf); return (COLON); } case 'e': /* in other contexts, e is END */ if (bdone == 0 && i > 0) { yylval.string = buf; bdone = 1; (void)buf_ungetc(in); return (STRING); } else { bdone = 0; yylval.len = buf_pos(in); xfree(buf); return (END); } case 'i': xfree(buf); return (INT_START); case 'd': xfree(buf); return (DICT_START); case 'l': xfree(buf); return (LIST_START); } skip: /* add this character to the buffer */ *p = c; i++; if (i == bstrlen && bstrflag == 1) { yylval.string = buf; bstrlen = bstrflag = bcdone = 0; return (STRING); } p++; } } int yyerror(const char *fmt, ...) { va_list ap; va_start(ap, fmt); vfprintf(stderr, fmt, ap); fprintf(stderr, "\n"); va_end(ap); return (0); } struct benc_node* benc_parse_buf(BUF *b, struct benc_node *node) { root = node; in = b; if (yyparse() != 0) return (NULL); return (root); } static struct benc_node* benc_stack_pop(void) { struct benc_node *node; bstackidx--; node = bstack[bstackidx]; return (node); } static struct benc_node* benc_stack_peek(void) { struct benc_node *node; node = bstack[bstackidx - 1]; return (node); } static void benc_stack_push(struct benc_node *node) { if (bstackidx == BENC_STACK_SIZE - 1) errx(1, "benc_stack_push: stack overflow"); bstack[bstackidx] = node; bstackidx++; } unworkable-0.53/BSDmakefile0000644000014500017510000000424211072252051015125 0ustar michaelstaff# # Copyright (c) 2006, 2007, 2008 Niall O'Higgins # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. # # $Id: BSDmakefile,v 1.11 2008-10-05 23:56:25 niallo Exp $ CC?= cc CFLAGS+= -Wall CFLAGS+= -Wstrict-prototypes -Wmissing-prototypes CFLAGS+= -Wmissing-declarations CFLAGS+= -Wshadow -Wpointer-arith -Wcast-qual CFLAGS+= -Wsign-compare -g -ggdb # Uncomment when building shared library #CFLAGS+= -fPIC LDFLAGS+= -L. # # Uncomment if you like to use Boehm's garbage collector (/usr/ports/devel/boehm-gc). #LDFLAGS+= -L/usr/local/lib -lgc -lpthread #DPADD+= /usr/local/lib/libgc.a #CFLAGS+= -DUSE_BOEHM_GC -DGC_DEBUG -DFIND_LEAK -I/usr/local/include/gc # You can also use Boehm's garbage collector as a means to find leaks. # # export GC_FIND_LEAK=1 PROG= unworkable SRCS= announce.c bencode.c buf.c ctl_server.c network.c parse.y progressmeter.c scheduler.c torrent.c trace.c util.c xmalloc.c OBJS= ${SRCS:N*.h:N*.sh:R:S/$/.o/g} MAN= unworkable.1 all: ${PROG} unworkable.cat1 ${PROG}: libunworkable.a main.o ${CC} -o ${.TARGET} ${LDFLAGS} -levent -lcrypto main.o -lunworkable libunworkable.a: ${OBJS} ar rcs libunworkable.a ${OBJS} # shared library not built by default, but it can be produced. libunworkable.so.1: ${OBJS} gcc -shared -Wl,-soname,libunworkable.so.1 \ -o libunworkable.so.0.5.1 $(OBJS) unworkable.cat1: ${MAN} nroff -Tascii -mandoc $(MAN) > unworkable.cat1 clean: rm -rf *.o *.a *.so.1 openbsd-compat/*.o ${PROG} y.tab.h unworkable.cat1 unworkable-0.53/README0000644000014500017510000000325311067245621013766 0ustar michaelstaffUnworkable BitTorrent Implementation Author: Niall O'Higgins Homepage: http://p2presearch.com/unworkable/ ==================================================== Unworkable is a BSD-licensed BitTorrent implementation for UNIX written from-scratch in C. It uses libevent for scalable asynchronous networking and the mmap() system call for local data access. Some of the goals of the project include (in no particular order) high code quality, efficiency, simplicity and security. FEATURES ======== Unworkable is still in an early stage of development, and is far behind most other BitTorrent implementations. However, it is usable for some basic things and the source code is quite minimal (4,000 lines of C compared to rTorrent's 40,000+ of C++). Current state: Unworkable currently lacks support for the following: * HTTP/1.1 client. Many trackers do not correctly support HTTP/1.0 so downloads will not work. * Encryption. * Trackerless (DHT) operation. * Probably lots of other stuff. BUILDING ======== Unworkable will build cleanly on OpenBSD (tested on i386, amd64, zaurus, sparc64, macppc) without any additional dependencies beyond the base install. Simply type 'make'. On other platforms, you must use the SCons (http://www.scons.org) build tool. After installing scons, type 'scons' to compile. Make sure you have libevent and OpenSSL installed. If you get it working on other platforms, please let me know and send along any portability diffs. It is known to work on at least FreeBSD 6.2, Ubuntu Linux 8.04, Centos 5, Fedora 7 (on EC 2), Gentoo Linux, Arch Linux, Mandriva Linux 2008.1. Mac OS X, Solaris 10 and Windows XP (cygwin). unworkable-0.53/unworkable.10000644000014500017510000000363411140242714015334 0ustar michaelstaff.\" $Id: unworkable.1,v 1.7 2009-01-29 05:59:40 niallo Exp $ .\" .\" Copyright (c) 2007 Niall O'Higgins .\" All rights reserved. .\" .\" Permission to use, copy, modify, and distribute this software for any .\" purpose with or without fee is hereby granted, provided that the above .\" copyright notice and this permission notice appear in all copies. .\" .\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES .\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF .\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR .\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES .\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .Dd $Mdocdate: October 4 2008 $ .Dt UNWORKABLE 1 .Os .Sh NAME .Nm unworkable .Nd download files via BitTorrent .Sh SYNOPSIS .Nm .Bk -words .Op Fl s .Op Fl g Ar port .Op Fl p Ar port .Op Fl t Ar tracefile .Ar torrent .Ek .Sh DESCRIPTION The .Nm program is used to download files via the BitTorrent protocol. .Pp When .Nm is executed with a valid .torrent file as an argument, it will proceed to announce to the tracker, connect to peers and download the data. Upon completion of the download, the program will exit, unless seed-mode is enabled. .Bl -tag -width Ds .It Fl g Ar port If specified, run the GUI control server on port .Ar port . By default, no GUI control server will run. .It Fl p Ar port If specified, listen for incoming BitTorrent peer connections on .Ar port . By default, .Nm does not accept incoming connections. .It Fl s Enable seed-mode, that is, keep running and seed after download is complete. .It Fl t Ar tracefile Trace execution, outputting to .Ar tracefile . .El .Sh AUTHORS The .Nm program was written by .An Niall O'Higgins. unworkable-0.53/includes.h0000644000014500017510000004153111104135264015057 0ustar michaelstaff/* $Id: includes.h,v 1.62 2008-11-04 21:12:20 niallo Exp $ */ /* * Copyright (c) 2006, 2007, 2008 Niall O'Higgins * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef INCLUDES_H #define INCLUDES_H #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define UNWORKABLE_VERSION "0.5" #define BSTRING (1 << 0) #define BINT (1 << 1) #define BDICT (1 << 2) #define BLIST (1 << 3) #define BDICT_ENTRY (1 << 4) #define PEER_STATE_HANDSHAKE1 (1<<0) #define PEER_STATE_BITFIELD (1<<1) #define PEER_STATE_ESTABLISHED (1<<2) #define PEER_STATE_AMCHOKING (1<<3) #define PEER_STATE_CHOKED (1<<4) #define PEER_STATE_AMINTERESTED (1<<5) #define PEER_STATE_INTERESTED (1<<6) #define PEER_STATE_DEAD (1<<7) #define PEER_STATE_GOTLEN (1<<8) #define PEER_STATE_CRYPTED (1<<9) #define PEER_STATE_HANDSHAKE2 (1<<10) #define PEER_STATE_SENDBITFIELD (1<<11) #define PEER_STATE_FAST (1<<12) #define PEER_MSG_ID_CHOKE 0x00 #define PEER_MSG_ID_UNCHOKE 0x01 #define PEER_MSG_ID_INTERESTED 0x02 #define PEER_MSG_ID_NOTINTERESTED 0x03 #define PEER_MSG_ID_HAVE 0x04 #define PEER_MSG_ID_BITFIELD 0x05 #define PEER_MSG_ID_REQUEST 0x06 #define PEER_MSG_ID_PIECE 0x07 #define PEER_MSG_ID_CANCEL 0x08 #define PEER_MSG_ID_PORT 0x09 /* Fast extension - see BEP 6 http://bittorrent.org/beps/bep_0006.html */ #define PEER_MSG_ID_REJECT 0x10 #define PEER_MSG_ID_ALLOWEDFAST 0x11 #define PEER_MSG_ID_SUGGEST 0x0D #define PEER_MSG_ID_HAVEALL 0x0E #define PEER_MSG_ID_HAVENONE 0x0F #define PEER_COMMS_THRESHOLD 300 /* five minutes */ #define PEER_KEEPALIVE_SECONDS 60 #define BLOCK_SIZE 16384 /* 16KB */ #define MAX_BACKLOG 65536 /* 64KB */ #define LENGTH_FIELD 4 /* peer messages use a 4byte len field */ #define MAX_MESSAGE_LEN 0xffffff /* 16M */ #define DEFAULT_ANNOUNCE_INTERVAL 1800/* */ #define MAX_REQUESTS 100 /* max request queue length per peer */ /* MSE defines * see http://www.azureuswiki.com/index.php/Message_Stream_Encryption */ #define CRYPTO_PRIME 0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A63A36210000000000090563 #define CRYPTO_GENERATOR 2 #define CRYPTO_PLAINTEXT 0x01 #define CRYPTO_RC4 0x02 #define CRYPTO_INT_LEN 160 #define CRYPTO_MAX_BYTES1 608 #define CRYPTO_MIN_BYTES1 96 #define BT_PROTOCOL "BitTorrent protocol" #define BT_PSTRLEN 19 #define BT_INITIAL_LEN 20 /* try to keep this many peer connections at all times */ #define PEERS_WANTED 10 /* when trying to fetch more peers, make sure we don't announce * more often than this interval allows */ #define MIN_ANNOUNCE_INTERVAL 60 #define PEER_ID_LEN 20 /* these are used in the HTTP client */ #define GETSTRINGLEN 2048 #define HTTPLEN 7 #define RESBUFLEN 1024 #define HTTP_1_0 "HTTP/1.0" #define HTTP_1_1 "HTTP/1.1" #define HTTP_OK "200" #define HTTP_END "\r\n\r\n" #define DEFAULT_PORT "6668" #define PIECE_GIMME_NOCREATE (1<<0) #define CTL_MESSAGE_LEN 64 struct benc_node { /* * Having this HEAD in every node is slightly wasteful of memory, * but I can't figure out how to put it in the union. */ TAILQ_HEAD(children, benc_node) children; TAILQ_ENTRY(benc_node) benc_nodes; unsigned int flags; union { long long number; struct { char *value; size_t len; } string; struct { char *key; struct benc_node *value; } dict_entry; } body; /* in dictionaries, absolute offset of dict end from start of input buffer */ size_t end; }; enum type { MULTIFILE, SINGLEFILE }; struct torrent_mmap { u_int8_t *addr; u_int8_t *aligned_addr; u_int32_t len; struct torrent_file *tfp; TAILQ_ENTRY(torrent_mmap) mmaps; }; #define TORRENT_PIECE_CKSUMOK (1<<0) #define TORRENT_PIECE_MAPPED (1<<1) struct torrent_piece { /* misc info about the piece */ int flags; /* how many blocks we currently have */ u_int32_t blocks; /* how long the piece actually is */ u_int32_t len; /* index of this piece in the torrent */ u_int32_t index; /* list of low-level mmaps containing the blocks */ TAILQ_HEAD(mmaps, torrent_mmap) mmaps; /* pointer to containing torrent */ struct torrent *tp; }; struct torrent_file { TAILQ_ENTRY(torrent_file) files; off_t file_length; char *md5sum; char *path; int fd; size_t refs; }; struct torrent { union { struct { char *pieces; struct torrent_file tfp; } singlefile; struct { TAILQ_HEAD(files, torrent_file) files; char *name; char *pieces; off_t total_length; } multifile; } body; char *announce; time_t creation_date; char *comment; char *created_by; u_int8_t *info_hash; u_int32_t num_pieces; u_int32_t piece_length; u_int32_t good_pieces; enum type type; off_t uploaded; off_t downloaded; off_t left; struct benc_node *broot; u_int32_t interval; char *trackerid; char *name; short isnew; u_int32_t complete; u_int32_t incomplete; struct torrent_piece *piece_array; }; /* Control server */ struct ctl_server { struct session *sc; struct bufferevent *bev; off_t started; int fd; TAILQ_HEAD(ctl_server_conns, ctl_server_conn) conns; }; /* Connections to control server */ struct ctl_server_conn { struct bufferevent *bev; struct sockaddr_in sa; struct ctl_server *cs; int fd; TAILQ_ENTRY(ctl_server_conn) conn_list; }; /* data for a http response */ struct http_response { /* response buffer */ u_int8_t *rxmsg; /* size of buffer so far */ u_int32_t rxread, rxmsglen; }; /* bittorrent peer */ struct peer { TAILQ_ENTRY(peer) peer_list; RB_ENTRY(peer_idxnode) entry; TAILQ_HEAD(peer_piece_dls, piece_dl) peer_piece_dls; TAILQ_HEAD(peer_piece_uls, piece_ul) peer_piece_uls; struct sockaddr_in sa; int connfd; int state; u_int32_t rxpending; u_int32_t txpending; struct bufferevent *bufev; u_int32_t rxmsglen; u_int8_t *rxmsg; u_int8_t *bitfield; /* from peer's handshake message */ u_int8_t pstrlen; u_int8_t id[PEER_ID_LEN]; u_int8_t info_hash[20]; struct session *sc; /* last time we rx'd something from this peer */ time_t lastrecv; /* time we connected this peer (ie start of its life) */ time_t connected; /* last time we sent something to this peer */ time_t lastsend; /* how many bytes have we rx'd from the peer since it was connected */ u_int64_t totalrx; /* how many bytes have we tx'd to the peer since it was connected */ u_int64_t totaltx; /* block request queue length*/ u_int32_t dl_queue_len; /* block upload queue length*/ u_int32_t ul_queue_len; /* keep alive timer event */ struct event keepalive_event; }; /* piece download transaction */ struct piece_dl { TAILQ_ENTRY(piece_dl) peer_piece_dl_list; TAILQ_ENTRY(piece_dl) idxnode_piece_dl_list; struct peer *pc; /* peer we're requesting from */ u_int32_t idx; /* piece index */ u_int32_t off; /* offset within this piece */ u_int32_t len; /* length of this request */ u_int32_t bytes; /* how many bytes have we read so far */ }; /* piece upload request */ struct piece_ul { TAILQ_ENTRY(piece_ul) peer_piece_ul_list; struct peer *pc; /* peer we're requesting from */ u_int32_t idx; /* piece index */ u_int32_t off; /* offset within this piece */ u_int32_t len; /* length of this request */ u_int32_t bytes; /* how many bytes have we read so far */ }; /* For the binary tree which does lookups based on piece dl index and offset, * we do not guarantee that key to be unique - ie there may be multiple piece_dls * in progress for the same block. Instead, we have a list of piece_dls. */ struct piece_dl_idxnode { RB_ENTRY(piece_dl_idxnode) entry; u_int32_t idx; /* piece index */ u_int32_t off; /* offset within this piece */ TAILQ_HEAD(idxnode_piece_dls, piece_dl) idxnode_piece_dls; }; struct piececounter { u_int32_t count; u_int32_t idx; }; struct peercounter { u_int64_t rate; struct peer *peer; }; /* data associated with a bittorrent session */ struct session { /* don't expect to have huge numbers of peers, or be searching very often, so linked list * should be fine for storage */ TAILQ_HEAD(peers, peer) peers; /* index piece_dls by block index / offset */ RB_HEAD(piece_dl_by_idxoff, piece_dl_idxnode) piece_dl_by_idxoff; int connfd; int servfd; char *key; char *ip; char *numwant; char *peerid; char *port; char *trackerid; char *request; struct event announce_event; struct event scheduler_event; struct torrent *tp; struct http_response *res; rlim_t maxfds; int announce_underway; u_int32_t tracker_num_peers; u_int32_t num_peers; time_t last_announce; struct piececounter *rarity_array; time_t last_rarity; struct ctl_server *ctl_server; u_int32_t txlimit; u_int32_t rxlimit; }; void benc_node_add(struct benc_node *, struct benc_node *); void benc_node_add_head(struct benc_node *, struct benc_node *); struct benc_node *benc_node_create(void); struct benc_node *benc_node_find(struct benc_node *node, char *); void benc_node_print(struct benc_node *, int); struct benc_node *benc_root_create(void); void benc_node_freeall(struct benc_node *); extern struct benc_node *root; /* flags */ #define BUF_AUTOEXT 1 /* autoextend on append */ typedef struct buf BUF; BUF *buf_alloc(size_t, u_int); BUF *buf_load(const char *, u_int); void buf_free(BUF *); void *buf_release(BUF *); int buf_getc(BUF *); ssize_t buf_set(BUF *, const void *, size_t, size_t); size_t buf_len(BUF *); int buf_write_fd(BUF *, int); int buf_write(BUF *, const char *, mode_t); void buf_write_stmp(BUF *, char *, mode_t); void buf_ungetc(BUF *); size_t buf_pos(BUF *); int mkpath(const char *, mode_t); void util_setbit(u_int8_t *, u_int32_t); int util_getbit(u_int8_t *, u_int32_t); void network_init(void); int network_start_torrent(struct torrent *, rlim_t); int yyerror(const char *, ...); int yyparse(void); int yylex(void); struct benc_node *benc_parse_buf(BUF *b, struct benc_node *); extern BUF *in; void *torrent_block_read(struct torrent_piece *, off_t, u_int32_t, int *); void torrent_block_write(struct torrent_piece *, off_t, u_int32_t, void *); struct torrent_mmap *torrent_mmap_create(struct torrent *, struct torrent_file *, off_t, u_int32_t); struct torrent *torrent_parse_file(const char *); u_int8_t *torrent_parse_infohash(const char *, size_t); int torrent_piece_checkhash(struct torrent *, struct torrent_piece *); struct torrent_piece *torrent_piece_find(struct torrent *, u_int32_t); struct torrent_piece *torrent_pieces_create(struct torrent *); int torrent_piece_map(struct torrent_piece *); void torrent_piece_unmap(struct torrent_piece *); void torrent_print(struct torrent *); u_int8_t *torrent_bitfield_get(struct torrent *); int torrent_empty(struct torrent *); void torrent_piece_sync(struct torrent *, u_int32_t); void torrent_fastresume_dump(struct torrent *); int torrent_fastresume_load(struct torrent *); /* * Support for Boehm's garbage collector, useful for finding leaks. */ #if defined(USE_BOEHM_GC) #include #define xmalloc(n) GC_MALLOC(n) #define xcalloc(m,n) GC_MALLOC((m)*(n)) #define xfree(p) GC_FREE(p) #define xrealloc(p,n) GC_REALLOC((p),(n)) #define xstrdup(n) gc_strdup(n) #define CHECK_LEAKS() GC_gcollect() char *gc_strdup(const char *); #else void *xmalloc(size_t); void *xrealloc(void *, size_t); void *xcalloc(size_t, size_t); void xfree(void *); char *xstrdup(const char *); #endif /* USE_BOEHM_GC */ #endif /* INCLUDES_H */ /* * Ensure all of data on socket comes through. f==read || f==vwrite */ size_t atomicio(ssize_t (*)(int, void *, size_t), int, void *, size_t); #define vwrite (ssize_t (*)(int, void *, size_t))write void refresh_progress_meter(void); void start_progress_meter(char *, off_t, off_t *, u_int32_t *, u_int32_t, off_t); void stop_progress_meter(void); void trace(const char *, ...); int terminate_handler(void); void print_len(void *, size_t); extern char *unworkable_trace; extern char *user_port; extern char *gui_port; extern int seed; static const u_int8_t mse_P[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34, 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, 0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22, 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD, 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, 0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37, 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45, 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, 0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x3A, 0x36, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x05, 0x63 }; static const u_int8_t mse_G[] = { 2 }; static const u_int8_t mse_VC[] = { 0, 0, 0, 0, 0, 0, 0, 0 }; #ifdef NO_STRLCPY size_t strlcpy(char *, const char *, size_t); #endif #ifdef NO_STRLCAT size_t strlcat(char *, const char *, size_t); #endif #ifdef NO_STRTONUM long long strtonum(const char *, long long, long long, const char **); #endif #ifdef NO_ERR void err(int, const char *, ...); void verr(int, const char *, __va_list); void errx(int, const char *, ...); void verrx(int, const char *, __va_list); void warn(const char *, ...); void vwarn(const char *, __va_list); void warnx(const char *, ...); void vwarnx(const char *, __va_list); #else #include #endif /* solaris 10 specific */ #if defined(__SVR4) && defined(__sun) char *__progname; #endif int announce(struct session *, const char *); int network_listen(char *, char *); void network_peerlist_add_peer(struct session *, struct peer *); void network_peerlist_update(struct session *, struct benc_node *); void network_peerlist_connect(struct session *); struct piece_dl *network_piece_dl_find(struct session *, struct peer *, u_int32_t, u_int32_t); int network_connect_tracker(const char *, const char *); void network_peer_write_piece(struct peer *, u_int32_t, u_int32_t, u_int32_t); void network_peer_read_piece(struct peer *, u_int32_t, off_t, u_int32_t, void *); void network_peer_write_bitfield(struct peer *); void network_peer_write_interested(struct peer *); void network_peer_free(struct peer *); struct peer * network_peer_create(void); void network_handle_peer_write(struct bufferevent *, void *); void network_handle_peer_error(struct bufferevent *, short, void *); void network_handle_peer_connect(struct bufferevent *, short, void *); void network_peer_request_block(struct peer *, u_int32_t, u_int32_t, u_int32_t); void network_peer_write_choke(struct peer *); void network_peer_write_unchoke(struct peer *); void network_peer_cancel_piece(struct piece_dl *); void network_peer_write_have(struct peer *, u_int32_t); void network_peer_write_keepalive(struct peer *); void network_peer_write_havenone(struct peer *); void network_peer_write_haveall(struct peer *); void network_peer_reject_block(struct peer *, u_int32_t, u_int32_t, u_int32_t); void network_peer_write_choke(struct peer *); DH *network_crypto_dh(void); long network_peer_lastcomms(struct peer *); u_int64_t network_peer_rxrate(struct peer *); u_int64_t network_peer_txrate(struct peer *); struct piece_dl * network_piece_dl_create(struct peer *, u_int32_t, u_int32_t, u_int32_t); void network_piece_dl_free(struct session *, struct piece_dl *); int piece_dl_idxnode_cmp(struct piece_dl_idxnode *, struct piece_dl_idxnode *); struct piece_ul *network_piece_ul_enqueue(struct peer *, u_int32_t, u_int32_t, u_int32_t); struct piece_ul *network_piece_ul_dequeue(struct peer *); /* index of piece dls by block index and offset */ RB_PROTOTYPE(piece_dl_by_idxoff, piece_dl_idxnode, entry, piece_dl_idxnode_cmp) void scheduler(int, short, void *); struct piece_dl * scheduler_piece_gimme(struct peer *, int, int *); void ctl_server_start(struct session *, char *, off_t); void ctl_server_notify_bytes(struct session *, off_t); void ctl_server_notify_pieces(struct session *); void ctl_server_notify_peers(struct session *); /* global needs to change when we have multi-torrent support */ extern struct torrent *mytorrent; unworkable-0.53/openbsd-compat/0000755000014500017510000000000011310321004015773 5ustar michaelstaffunworkable-0.53/openbsd-compat/vwarn.c0000644000014500017510000000374010725067751017331 0ustar michaelstaff/* $OpenBSD: vwarn.c,v 1.8 2005/08/08 08:05:34 espie Exp $ */ /*- * Copyright (c) 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include "../includes.h" extern char *__progname; /* Program name, from crt0. */ void vwarn(const char *fmt, va_list ap) { int sverrno; sverrno = errno; (void)fprintf(stderr, "%s: ", __progname); if (fmt != NULL) { (void)vfprintf(stderr, fmt, ap); (void)fprintf(stderr, ": "); } (void)fprintf(stderr, "%s\n", strerror(sverrno)); } unworkable-0.53/openbsd-compat/vwarnx.c0000644000014500017510000000356510725067751017526 0ustar michaelstaff/* $OpenBSD: vwarnx.c,v 1.8 2005/08/08 08:05:34 espie Exp $ */ /*- * Copyright (c) 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "../includes.h" extern char *__progname; /* Program name, from crt0. */ void vwarnx(const char *fmt, va_list ap) { (void)fprintf(stderr, "%s: ", __progname); if (fmt != NULL) (void)vfprintf(stderr, fmt, ap); (void)fprintf(stderr, "\n"); } unworkable-0.53/openbsd-compat/warn.c0000644000014500017510000000340510725067751017141 0ustar michaelstaff/* $OpenBSD: warn.c,v 1.8 2005/08/08 08:05:34 espie Exp $ */ /*- * Copyright (c) 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "../includes.h" #include void warn(const char *fmt, ...) { va_list ap; va_start(ap, fmt); vwarn(fmt, ap); va_end(ap); } unworkable-0.53/openbsd-compat/getaddrinfo.h0000644000014500017510000001204510726630455020463 0ustar michaelstaff/* * Copyright (c) 2001, 02 Motoyuki Kasahara * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef GETADDRINFO_H #define GETADDRINFO_H #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include /********************************************************************/ /* * Undefine all the macros. * might defines some of them. */ #ifdef EAI_ADDRFAMILY #undef EAI_ADDRFAMILY #endif #ifdef EAI_AGAIN #undef EAI_AGAIN #endif #ifdef EAI_BADFLAGS #undef EAI_BADFLAGS #endif #ifdef EAI_FAIL #undef EAI_FAIL #endif #ifdef EAI_FAMILY #undef EAI_FAMILY #endif #ifdef EAI_MEMORY #undef EAI_MEMORY #endif #ifdef EAI_NONAME #undef EAI_NONAME #endif #ifdef EAI_OVERFLOW #undef EAI_OVERFLOW #endif #ifdef EAI_SERVICE #undef EAI_SERVICE #endif #ifdef EAI_SOCKTYPE #undef EAI_SOCKTYPE #endif #ifdef EAI_SYSTEM #undef EAI_SYSTEM #endif #ifdef AI_PASSIVE #undef AI_PASSIVE #endif #ifdef AI_CANONNAME #undef AI_CANONNAME #endif #ifdef AI_NUMERICHOST #undef AI_NUMERICHOST #endif #ifdef AI_NUMERICSERV #undef AI_NUMERICSERV #endif #ifdef AI_V4MAPPED #undef AI_V4MAPPED #endif #ifdef AI_ALL #undef AI_ALL #endif #ifdef AI_ADDRCONFIG #undef AI_ADDRCONFIG #endif #ifdef AI_DEFAULT #undef AI_DEFAULT #endif #ifdef NI_NOFQDN #undef NI_NOFQDN #endif #ifdef NI_NUMERICHOST #undef NI_NUMERICHOST #endif #ifdef NI_NAMEREQD #undef NI_NAMEREQD #endif #ifdef NI_NUMERICSERV #undef NI_NUMERICSERV #endif #ifdef NI_NUMERICSCOPE #undef NI_NUMERICSCOPE #endif #ifdef NI_DGRAM #undef NI_DGRAM #endif #ifdef NI_MAXHOST #undef NI_MAXHOST #endif #ifdef NI_MAXSERV #undef NI_MAXSERV #endif /* * Fake struct and function names. * might declares all or some of them. */ #if defined(HAVE_GETADDRINFO) || defined(HAVE_GETNAMEINFO) #define addrinfo my_addrinfo #define gai_strerror my_gai_strerror #define freeaddrinfo my_freeaddrinfo #define getaddrinfo my_getaddrinfo #define getnameinfo my_getnameinfo #endif /********************************************************************/ /* * Error codes. */ #define EAI_ADDRFAMILY 1 #define EAI_AGAIN 2 #define EAI_BADFLAGS 3 #define EAI_FAIL 4 #define EAI_FAMILY 5 #define EAI_MEMORY 6 #define EAI_NONAME 7 #define EAI_OVERFLOW 8 #define EAI_SERVICE 9 #define EAI_SOCKTYPE 10 #define EAI_SYSTEM 11 /* * Flags for getaddrinfo(). */ #define AI_ADDRCONFIG 0x0001 #define AI_ALL 0x0002 #define AI_CANONNAME 0x0004 #define AI_NUMERICHOST 0x0008 #define AI_NUMERICSERV 0x0010 #define AI_PASSIVE 0x0020 #define AI_V4MAPPED 0x0040 #define AI_DEFAULT (AI_V4MAPPED | AI_ADDRCONFIG) /* * Flags for getnameinfo(). */ #define NI_DGRAM 0x0001 #define NI_NAMEREQD 0x0002 #define NI_NOFQDN 0x0004 #define NI_NUMERICHOST 0x0008 #define NI_NUMERICSCOPE 0x0010 #define NI_NUMERICSERV 0x0020 /* * Maximum length of FQDN and servie name for getnameinfo(). */ #define NI_MAXHOST 1025 #define NI_MAXSERV 32 /* * Address families and Protocol families. */ #ifndef AF_UNSPEC #define AF_UNSPEC AF_INET #endif #ifndef PF_UNSPEC #define PF_UNSPEC PF_INET #endif /* * struct addrinfo. */ struct addrinfo { int ai_flags; int ai_family; int ai_socktype; int ai_protocol; socklen_t ai_addrlen; char *ai_canonname; struct sockaddr *ai_addr; struct addrinfo *ai_next; }; /* * Functions. */ #ifdef __STDC__ const char *gai_strerror(int); void freeaddrinfo(struct addrinfo *); int getaddrinfo(const char *, const char *, const struct addrinfo *, struct addrinfo **); int getnameinfo(const struct sockaddr *, socklen_t, char *, socklen_t, char *, socklen_t, int); #else const char *gai_strerror(); void freeaddrinfo(); int getaddrinfo(); int getnameinfo(); #endif #endif /* not GETADDRINFO_H */ unworkable-0.53/openbsd-compat/getaddrinfo.c0000644000014500017510000003315610726630455020464 0ustar michaelstaff/* * Copyright (c) 2001, 02 Motoyuki Kasahara * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * This program provides getaddrinfo() and getnameinfo() described in * RFC2133, 2553 and 3493. These functions are mainly used for IPv6 * application to resolve hostname or address. * * This program is designed to be working on traditional IPv4 systems * which don't have those functions. Therefore, this implementation * supports IPv4 only. * * This program is useful for application which should support both IPv6 * and traditional IPv4 systems. Use genuine getaddrinfo() and getnameinfo() * provided by system if the system supports IPv6. Otherwise, use this * implementation. * * This program is intended to be used in combination with GNU Autoconf. * * This program also provides freeaddrinfo() and gai_strerror(). * * To use this program in your application, insert the following lines to * C source files after including `sys/types.h', `sys/socket.h' and * `netdb.h'. `getaddrinfo.h' defines `struct addrinfo' and AI_, NI_, * EAI_ macros. * * #ifndef HAVE_GETADDRINFO * #include "getaddrinfo.h" * #endif * * Restriction: * getaddrinfo() and getnameinfo() of this program are NOT thread * safe, unless the cpp macro ENABLE_PTHREAD is defined. */ /* * Add the following code to your configure.ac (or configure.in). * AC_C_CONST * AC_HEADER_STDC * AC_CHECK_HEADERS(string.h memory.h stdlib.h) * AC_CHECK_FUNCS(memcpy) * AC_REPLACE_FUNCS(memset) * AC_TYPE_SOCKLEN_T * AC_TYPE_IN_PORT_T * AC_DECL_H_ERRNO * * AC_CHECK_FUNCS(getaddrinfo getnameinfo) * if test "$ac_cv_func_getaddrinfo$ac_cv_func_getnameinfo" != yesyes ; then * LIBOBJS="$LIBOBJS getaddrinfo.$ac_objext" * fi */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #if defined(STDC_HEADERS) || defined(HAVE_STRING_H) #include #if !defined(STDC_HEADERS) && defined(HAVE_MEMORY_H) #include #endif /* not STDC_HEADERS and HAVE_MEMORY_H */ #else /* not STDC_HEADERS and not HAVE_STRING_H */ #include #endif /* not STDC_HEADERS and not HAVE_STRING_H */ #ifdef HAVE_STDLIB_H #include #endif #ifdef ENABLE_PTHREAD #include #endif #ifdef ENABLE_NLS #include #endif #ifndef HAVE_MEMCPY #define memcpy(d, s, n) bcopy((s), (d), (n)) #ifdef __STDC__ void *memchr(const void *, int, size_t); int memcmp(const void *, const void *, size_t); void *memmove(void *, const void *, size_t); void *memset(void *, int, size_t); #else /* not __STDC__ */ char *memchr(); int memcmp(); char *memmove(); char *memset(); #endif /* not __STDC__ */ #endif /* not HAVE_MEMCPY */ #ifndef H_ERRNO_DECLARED extern int h_errno; #endif #include "getaddrinfo.h" #ifdef ENABLE_NLS #define _(string) gettext(string) #ifdef gettext_noop #define N_(string) gettext_noop(string) #else #define N_(string) (string) #endif #else #define gettext(string) (string) #define _(string) (string) #define N_(string) (string) #endif /* * Error messages for gai_strerror(). */ static char *eai_errlist[] = { N_("Success"), /* EAI_ADDRFAMILY */ N_("Address family for hostname not supported"), /* EAI_AGAIN */ N_("Temporary failure in name resolution"), /* EAI_BADFLAGS */ N_("Invalid value for ai_flags"), /* EAI_FAIL */ N_("Non-recoverable failure in name resolution"), /* EAI_FAMILY */ N_("ai_family not supported"), /* EAI_MEMORY */ N_("Memory allocation failure"), /* EAI_NONAME */ N_("hostname nor servname provided, or not known"), /* EAI_OVERFLOW */ N_("An argument buffer overflowed"), /* EAI_SERVICE */ N_("servname not supported for ai_socktype"), /* EAI_SOCKTYPE */ N_("ai_socktype not supported"), /* EAI_SYSTEM */ N_("System error returned in errno") }; /* * Default hints for getaddrinfo(). */ static struct addrinfo default_hints = { 0, PF_UNSPEC, 0, 0, 0, NULL, NULL, NULL }; /* * Mutex. */ #ifdef ENABLE_PTHREAD static pthread_mutex_t gai_mutex = PTHREAD_MUTEX_INITIALIZER; #endif /* * Declaration of static functions. */ #ifdef __STDC__ static int is_integer(const char *); static int is_address(const char *); static int itoa_length(int); #else static int is_integer(); static int is_address(); static int itoa_length(); #endif /* * gai_strerror(). */ const char * gai_strerror(ecode) int ecode; { if (ecode < 0 || ecode > EAI_SYSTEM) return _("Unknown error"); return gettext(eai_errlist[ecode]); } /* * freeaddrinfo(). */ void freeaddrinfo(ai) struct addrinfo *ai; { struct addrinfo *next_ai; while (ai != NULL) { if (ai->ai_canonname != NULL) free(ai->ai_canonname); if (ai->ai_addr != NULL) free(ai->ai_addr); next_ai = ai->ai_next; free(ai); ai = next_ai; } } /* * Return 1 if the string `s' represents an integer. */ static int is_integer(s) const char *s; { if (*s == '-' || *s == '+') s++; if (*s < '0' || '9' < *s) return 0; s++; while ('0' <= *s && *s <= '9') s++; return (*s == '\0'); } /* * Return 1 if the string `s' represents an IPv4 address. * Unlike inet_addr(), it doesn't permit malformed nortation such * as "192.168". */ static int is_address(s) const char *s; { const static char delimiters[] = {'.', '.', '.', '\0'}; int i, j; int octet; for (i = 0; i < 4; i++) { if (*s == '0' && *(s + 1) != delimiters[i]) return 0; for (j = 0, octet = 0; '0' <= *s && *s <= '9' && j < 3; s++, j++) octet = octet * 10 + (*s - '0'); if (j == 0 || octet > 255 || *s != delimiters[i]) return 0; s++; } return 1; } /* * Calcurate length of the string `s', where `s' is set by * sprintf(s, "%d", n). */ static int itoa_length(n) int n; { int result = 1; if (n < 0) { n = -n; result++; } while (n >= 10) { result++; n /= 10; } return result; } /* * getaddrinfo(). */ int getaddrinfo(nodename, servname, hints, res) const char *nodename; const char *servname; const struct addrinfo *hints; struct addrinfo **res; { struct addrinfo *head_res = NULL; struct addrinfo *tail_res = NULL; struct addrinfo *new_res; struct sockaddr_in *sa_in; struct in_addr **addr_list; struct in_addr *addr_list_buf[2]; struct in_addr addr_buf; struct in_addr **ap; struct servent *servent; struct hostent *hostent; const char *canonname = NULL; in_port_t port; int saved_h_errno; int result = 0; #ifdef ENABLE_PTHREAD pthread_mutex_lock(&gai_mutex); #endif saved_h_errno = h_errno; if (nodename == NULL && servname == NULL) { result = EAI_NONAME; goto end; } if (hints != NULL) { if (hints->ai_family != PF_INET && hints->ai_family != PF_UNSPEC) { result = EAI_FAMILY; goto end; } if (hints->ai_socktype != SOCK_DGRAM && hints->ai_socktype != SOCK_STREAM && hints->ai_socktype != 0) { result = EAI_SOCKTYPE; goto end; } } else { hints = &default_hints; } if (servname != NULL) { if (is_integer(servname)) port = htons(atoi(servname)); else { if (hints->ai_flags & AI_NUMERICSERV) { result = EAI_NONAME; goto end; } if (hints->ai_socktype == SOCK_DGRAM) servent = getservbyname(servname, "udp"); else if (hints->ai_socktype == SOCK_STREAM) servent = getservbyname(servname, "tcp"); else if (hints->ai_socktype == 0) servent = getservbyname(servname, "tcp"); else { result = EAI_SOCKTYPE; goto end; } if (servent == NULL) { result = EAI_SERVICE; goto end; } port = servent->s_port; } } else { port = htons(0); } if (nodename != NULL) { if (is_address(nodename)) { addr_buf.s_addr = inet_addr(nodename); addr_list_buf[0] = &addr_buf; addr_list_buf[1] = NULL; addr_list = addr_list_buf; if (hints->ai_flags & AI_CANONNAME && !(hints->ai_flags & AI_NUMERICHOST)) { hostent = gethostbyaddr((char *)&addr_buf, sizeof(struct in_addr), AF_INET); if (hostent != NULL) canonname = hostent->h_name; else canonname = nodename; } } else { if (hints->ai_flags & AI_NUMERICHOST) { result = EAI_NONAME; goto end; } hostent = gethostbyname(nodename); if (hostent == NULL) { switch (h_errno) { case HOST_NOT_FOUND: case NO_DATA: result = EAI_NONAME; goto end; case TRY_AGAIN: result = EAI_AGAIN; goto end; default: result = EAI_FAIL; goto end; } } addr_list = (struct in_addr **)hostent->h_addr_list; if (hints->ai_flags & AI_CANONNAME) canonname = hostent->h_name; } } else { if (hints->ai_flags & AI_PASSIVE) addr_buf.s_addr = htonl(INADDR_ANY); else addr_buf.s_addr = htonl(0x7F000001); addr_list_buf[0] = &addr_buf; addr_list_buf[1] = NULL; addr_list = addr_list_buf; } for (ap = addr_list; *ap != NULL; ap++) { new_res = (struct addrinfo *)malloc(sizeof(struct addrinfo)); if (new_res == NULL) { if (head_res != NULL) freeaddrinfo(head_res); result = EAI_MEMORY; goto end; } new_res->ai_family = PF_INET; new_res->ai_socktype = hints->ai_socktype; new_res->ai_protocol = hints->ai_protocol; new_res->ai_addr = NULL; new_res->ai_addrlen = sizeof(struct sockaddr_in); new_res->ai_canonname = NULL; new_res->ai_next = NULL; new_res->ai_addr = (struct sockaddr *) malloc(sizeof(struct sockaddr_in)); if (new_res->ai_addr == NULL) { free(new_res); if (head_res != NULL) freeaddrinfo(head_res); result = EAI_MEMORY; goto end; } sa_in = (struct sockaddr_in *)new_res->ai_addr; memset(sa_in, 0, sizeof(struct sockaddr_in)); sa_in->sin_family = PF_INET; sa_in->sin_port = port; memcpy(&sa_in->sin_addr, *ap, sizeof(struct in_addr)); if (head_res == NULL) head_res = new_res; else tail_res->ai_next = new_res; tail_res = new_res; } if (canonname != NULL && head_res != NULL) { head_res->ai_canonname = (char *)malloc(strlen(canonname) + 1); if (head_res->ai_canonname != NULL) strcpy(head_res->ai_canonname, canonname); } *res = head_res; end: h_errno = saved_h_errno; #ifdef ENABLE_PTHREAD pthread_mutex_unlock(&gai_mutex); #endif return result; } /* * getnameinfo(). */ int getnameinfo(sa, salen, node, nodelen, serv, servlen, flags) const struct sockaddr *sa; socklen_t salen; char *node; socklen_t nodelen; char *serv; socklen_t servlen; int flags; { const struct sockaddr_in *sa_in = (const struct sockaddr_in *)sa; struct hostent *hostent; struct servent *servent; char *ntoa_address; int saved_h_errno; int result = 0; #ifdef ENABLE_PTHREAD pthread_mutex_lock(&gai_mutex); #endif saved_h_errno = h_errno; if (sa_in->sin_family != PF_INET) { result = EAI_FAMILY; goto end; } else if (node == NULL && serv == NULL) { result = EAI_NONAME; goto end; } if (serv != NULL && servlen > 0) { if (flags & NI_NUMERICSERV) servent = NULL; else if (flags & NI_DGRAM) servent = getservbyport(sa_in->sin_port, "udp"); else servent = getservbyport(sa_in->sin_port, "tcp"); if (servent != NULL) { if (servlen <= strlen(servent->s_name)) { result = EAI_OVERFLOW; goto end; } strcpy(serv, servent->s_name); } else { if (servlen <= itoa_length(ntohs(sa_in->sin_port))) { result = EAI_OVERFLOW; goto end; } sprintf(serv, "%d", ntohs(sa_in->sin_port)); } } if (node != NULL && nodelen > 0) { if (flags & NI_NUMERICHOST) hostent = NULL; else { hostent = gethostbyaddr((char *)&sa_in->sin_addr, sizeof(struct in_addr), AF_INET); } if (hostent != NULL) { if (nodelen <= strlen(hostent->h_name)) { result = EAI_OVERFLOW; goto end; } strcpy(node, hostent->h_name); } else { if (flags & NI_NAMEREQD) { result = EAI_NONAME; goto end; } ntoa_address = inet_ntoa(sa_in->sin_addr); if (nodelen <= strlen(ntoa_address)) { result = EAI_OVERFLOW; goto end; } strcpy(node, ntoa_address); } } end: h_errno = saved_h_errno; #ifdef ENABLE_PTHREAD pthread_mutex_unlock(&gai_mutex); #endif return result; } unworkable-0.53/openbsd-compat/sha1.h0000644000014500017510000000321610725147527017033 0ustar michaelstaff/* $OpenBSD: sha1.h,v 1.1 2007/10/26 02:59:07 niallo Exp $ */ /* * SHA-1 in C * By Steve Reid * 100% Public Domain */ #ifndef _SHA1_H #define _SHA1_H #define SHA1_BLOCK_LENGTH 64 #define SHA1_DIGEST_LENGTH 20 #define SHA1_DIGEST_STRING_LENGTH (SHA1_DIGEST_LENGTH * 2 + 1) typedef struct { u_int32_t state[5]; u_int64_t count; u_int8_t buffer[SHA1_BLOCK_LENGTH]; } SHA1_CTX; void SHA1Init(SHA1_CTX *); void SHA1Pad(SHA1_CTX *); void SHA1Transform(u_int32_t [5], const u_int8_t [SHA1_BLOCK_LENGTH]); void SHA1Update(SHA1_CTX *, const u_int8_t *, size_t); void SHA1Final(u_int8_t [SHA1_DIGEST_LENGTH], SHA1_CTX *); char *SHA1End(SHA1_CTX *, char *); char *SHA1File(const char *, char *); char *SHA1FileChunk(const char *, char *, off_t, off_t); char *SHA1Data(const u_int8_t *, size_t, char *); #define HTONDIGEST(x) do { \ x[0] = htonl(x[0]); \ x[1] = htonl(x[1]); \ x[2] = htonl(x[2]); \ x[3] = htonl(x[3]); \ x[4] = htonl(x[4]); } while (0) #define NTOHDIGEST(x) do { \ x[0] = ntohl(x[0]); \ x[1] = ntohl(x[1]); \ x[2] = ntohl(x[2]); \ x[3] = ntohl(x[3]); \ x[4] = ntohl(x[4]); } while (0) #endif /* _SHA1_H */ unworkable-0.53/openbsd-compat/sys/0000755000014500017510000000000011310321004016611 5ustar michaelstaffunworkable-0.53/openbsd-compat/sys/tree.h0000644000014500017510000005420210710255600017737 0ustar michaelstaff/* $OpenBSD: tree.h,v 1.9 2004/11/24 18:10:42 tdeval Exp $ */ /* * Copyright 2002 Niels Provos * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef _SYS_TREE_H_ #define _SYS_TREE_H_ /* * This file defines data structures for different types of trees: * splay trees and red-black trees. * * A splay tree is a self-organizing data structure. Every operation * on the tree causes a splay to happen. The splay moves the requested * node to the root of the tree and partly rebalances it. * * This has the benefit that request locality causes faster lookups as * the requested nodes move to the top of the tree. On the other hand, * every lookup causes memory writes. * * The Balance Theorem bounds the total access time for m operations * and n inserts on an initially empty tree as O((m + n)lg n). The * amortized cost for a sequence of m accesses to a splay tree is O(lg n); * * A red-black tree is a binary search tree with the node color as an * extra attribute. It fulfills a set of conditions: * - every search path from the root to a leaf consists of the * same number of black nodes, * - each red node (except for the root) has a black parent, * - each leaf node is black. * * Every operation on a red-black tree is bounded as O(lg n). * The maximum height of a red-black tree is 2lg (n+1). */ #define SPLAY_HEAD(name, type) \ struct name { \ struct type *sph_root; /* root of the tree */ \ } #define SPLAY_INITIALIZER(root) \ { NULL } #define SPLAY_INIT(root) do { \ (root)->sph_root = NULL; \ } while (0) #define SPLAY_ENTRY(type) \ struct { \ struct type *spe_left; /* left element */ \ struct type *spe_right; /* right element */ \ } #define SPLAY_LEFT(elm, field) (elm)->field.spe_left #define SPLAY_RIGHT(elm, field) (elm)->field.spe_right #define SPLAY_ROOT(head) (head)->sph_root #define SPLAY_EMPTY(head) (SPLAY_ROOT(head) == NULL) /* SPLAY_ROTATE_{LEFT,RIGHT} expect that tmp hold SPLAY_{RIGHT,LEFT} */ #define SPLAY_ROTATE_RIGHT(head, tmp, field) do { \ SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(tmp, field); \ SPLAY_RIGHT(tmp, field) = (head)->sph_root; \ (head)->sph_root = tmp; \ } while (0) #define SPLAY_ROTATE_LEFT(head, tmp, field) do { \ SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(tmp, field); \ SPLAY_LEFT(tmp, field) = (head)->sph_root; \ (head)->sph_root = tmp; \ } while (0) #define SPLAY_LINKLEFT(head, tmp, field) do { \ SPLAY_LEFT(tmp, field) = (head)->sph_root; \ tmp = (head)->sph_root; \ (head)->sph_root = SPLAY_LEFT((head)->sph_root, field); \ } while (0) #define SPLAY_LINKRIGHT(head, tmp, field) do { \ SPLAY_RIGHT(tmp, field) = (head)->sph_root; \ tmp = (head)->sph_root; \ (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field); \ } while (0) #define SPLAY_ASSEMBLE(head, node, left, right, field) do { \ SPLAY_RIGHT(left, field) = SPLAY_LEFT((head)->sph_root, field); \ SPLAY_LEFT(right, field) = SPLAY_RIGHT((head)->sph_root, field);\ SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(node, field); \ SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(node, field); \ } while (0) /* Generates prototypes and inline functions */ #define SPLAY_PROTOTYPE(name, type, field, cmp) \ void name##_SPLAY(struct name *, struct type *); \ void name##_SPLAY_MINMAX(struct name *, int); \ struct type *name##_SPLAY_INSERT(struct name *, struct type *); \ struct type *name##_SPLAY_REMOVE(struct name *, struct type *); \ \ /* Finds the node with the same key as elm */ \ static __inline struct type * \ name##_SPLAY_FIND(struct name *head, struct type *elm) \ { \ if (SPLAY_EMPTY(head)) \ return(NULL); \ name##_SPLAY(head, elm); \ if ((cmp)(elm, (head)->sph_root) == 0) \ return (head->sph_root); \ return (NULL); \ } \ \ static __inline struct type * \ name##_SPLAY_NEXT(struct name *head, struct type *elm) \ { \ name##_SPLAY(head, elm); \ if (SPLAY_RIGHT(elm, field) != NULL) { \ elm = SPLAY_RIGHT(elm, field); \ while (SPLAY_LEFT(elm, field) != NULL) { \ elm = SPLAY_LEFT(elm, field); \ } \ } else \ elm = NULL; \ return (elm); \ } \ \ static __inline struct type * \ name##_SPLAY_MIN_MAX(struct name *head, int val) \ { \ name##_SPLAY_MINMAX(head, val); \ return (SPLAY_ROOT(head)); \ } /* Main splay operation. * Moves node close to the key of elm to top */ #define SPLAY_GENERATE(name, type, field, cmp) \ struct type * \ name##_SPLAY_INSERT(struct name *head, struct type *elm) \ { \ if (SPLAY_EMPTY(head)) { \ SPLAY_LEFT(elm, field) = SPLAY_RIGHT(elm, field) = NULL; \ } else { \ int __comp; \ name##_SPLAY(head, elm); \ __comp = (cmp)(elm, (head)->sph_root); \ if(__comp < 0) { \ SPLAY_LEFT(elm, field) = SPLAY_LEFT((head)->sph_root, field);\ SPLAY_RIGHT(elm, field) = (head)->sph_root; \ SPLAY_LEFT((head)->sph_root, field) = NULL; \ } else if (__comp > 0) { \ SPLAY_RIGHT(elm, field) = SPLAY_RIGHT((head)->sph_root, field);\ SPLAY_LEFT(elm, field) = (head)->sph_root; \ SPLAY_RIGHT((head)->sph_root, field) = NULL; \ } else \ return ((head)->sph_root); \ } \ (head)->sph_root = (elm); \ return (NULL); \ } \ \ struct type * \ name##_SPLAY_REMOVE(struct name *head, struct type *elm) \ { \ struct type *__tmp; \ if (SPLAY_EMPTY(head)) \ return (NULL); \ name##_SPLAY(head, elm); \ if ((cmp)(elm, (head)->sph_root) == 0) { \ if (SPLAY_LEFT((head)->sph_root, field) == NULL) { \ (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field);\ } else { \ __tmp = SPLAY_RIGHT((head)->sph_root, field); \ (head)->sph_root = SPLAY_LEFT((head)->sph_root, field);\ name##_SPLAY(head, elm); \ SPLAY_RIGHT((head)->sph_root, field) = __tmp; \ } \ return (elm); \ } \ return (NULL); \ } \ \ void \ name##_SPLAY(struct name *head, struct type *elm) \ { \ struct type __node, *__left, *__right, *__tmp; \ int __comp; \ \ SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\ __left = __right = &__node; \ \ while ((__comp = (cmp)(elm, (head)->sph_root))) { \ if (__comp < 0) { \ __tmp = SPLAY_LEFT((head)->sph_root, field); \ if (__tmp == NULL) \ break; \ if ((cmp)(elm, __tmp) < 0){ \ SPLAY_ROTATE_RIGHT(head, __tmp, field); \ if (SPLAY_LEFT((head)->sph_root, field) == NULL)\ break; \ } \ SPLAY_LINKLEFT(head, __right, field); \ } else if (__comp > 0) { \ __tmp = SPLAY_RIGHT((head)->sph_root, field); \ if (__tmp == NULL) \ break; \ if ((cmp)(elm, __tmp) > 0){ \ SPLAY_ROTATE_LEFT(head, __tmp, field); \ if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\ break; \ } \ SPLAY_LINKRIGHT(head, __left, field); \ } \ } \ SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \ } \ \ /* Splay with either the minimum or the maximum element \ * Used to find minimum or maximum element in tree. \ */ \ void name##_SPLAY_MINMAX(struct name *head, int __comp) \ { \ struct type __node, *__left, *__right, *__tmp; \ \ SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\ __left = __right = &__node; \ \ while (1) { \ if (__comp < 0) { \ __tmp = SPLAY_LEFT((head)->sph_root, field); \ if (__tmp == NULL) \ break; \ if (__comp < 0){ \ SPLAY_ROTATE_RIGHT(head, __tmp, field); \ if (SPLAY_LEFT((head)->sph_root, field) == NULL)\ break; \ } \ SPLAY_LINKLEFT(head, __right, field); \ } else if (__comp > 0) { \ __tmp = SPLAY_RIGHT((head)->sph_root, field); \ if (__tmp == NULL) \ break; \ if (__comp > 0) { \ SPLAY_ROTATE_LEFT(head, __tmp, field); \ if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\ break; \ } \ SPLAY_LINKRIGHT(head, __left, field); \ } \ } \ SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \ } #define SPLAY_NEGINF -1 #define SPLAY_INF 1 #define SPLAY_INSERT(name, x, y) name##_SPLAY_INSERT(x, y) #define SPLAY_REMOVE(name, x, y) name##_SPLAY_REMOVE(x, y) #define SPLAY_FIND(name, x, y) name##_SPLAY_FIND(x, y) #define SPLAY_NEXT(name, x, y) name##_SPLAY_NEXT(x, y) #define SPLAY_MIN(name, x) (SPLAY_EMPTY(x) ? NULL \ : name##_SPLAY_MIN_MAX(x, SPLAY_NEGINF)) #define SPLAY_MAX(name, x) (SPLAY_EMPTY(x) ? NULL \ : name##_SPLAY_MIN_MAX(x, SPLAY_INF)) #define SPLAY_FOREACH(x, name, head) \ for ((x) = SPLAY_MIN(name, head); \ (x) != NULL; \ (x) = SPLAY_NEXT(name, head, x)) /* Macros that define a red-black tree */ #define RB_HEAD(name, type) \ struct name { \ struct type *rbh_root; /* root of the tree */ \ } #define RB_INITIALIZER(root) \ { NULL } #define RB_INIT(root) do { \ (root)->rbh_root = NULL; \ } while (0) #define RB_BLACK 0 #define RB_RED 1 #define RB_ENTRY(type) \ struct { \ struct type *rbe_left; /* left element */ \ struct type *rbe_right; /* right element */ \ struct type *rbe_parent; /* parent element */ \ int rbe_color; /* node color */ \ } #define RB_LEFT(elm, field) (elm)->field.rbe_left #define RB_RIGHT(elm, field) (elm)->field.rbe_right #define RB_PARENT(elm, field) (elm)->field.rbe_parent #define RB_COLOR(elm, field) (elm)->field.rbe_color #define RB_ROOT(head) (head)->rbh_root #define RB_EMPTY(head) (RB_ROOT(head) == NULL) #define RB_SET(elm, parent, field) do { \ RB_PARENT(elm, field) = parent; \ RB_LEFT(elm, field) = RB_RIGHT(elm, field) = NULL; \ RB_COLOR(elm, field) = RB_RED; \ } while (0) #define RB_SET_BLACKRED(black, red, field) do { \ RB_COLOR(black, field) = RB_BLACK; \ RB_COLOR(red, field) = RB_RED; \ } while (0) #ifndef RB_AUGMENT #define RB_AUGMENT(x) #endif #define RB_ROTATE_LEFT(head, elm, tmp, field) do { \ (tmp) = RB_RIGHT(elm, field); \ if ((RB_RIGHT(elm, field) = RB_LEFT(tmp, field))) { \ RB_PARENT(RB_LEFT(tmp, field), field) = (elm); \ } \ RB_AUGMENT(elm); \ if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field))) { \ if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) \ RB_LEFT(RB_PARENT(elm, field), field) = (tmp); \ else \ RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \ } else \ (head)->rbh_root = (tmp); \ RB_LEFT(tmp, field) = (elm); \ RB_PARENT(elm, field) = (tmp); \ RB_AUGMENT(tmp); \ if ((RB_PARENT(tmp, field))) \ RB_AUGMENT(RB_PARENT(tmp, field)); \ } while (0) #define RB_ROTATE_RIGHT(head, elm, tmp, field) do { \ (tmp) = RB_LEFT(elm, field); \ if ((RB_LEFT(elm, field) = RB_RIGHT(tmp, field))) { \ RB_PARENT(RB_RIGHT(tmp, field), field) = (elm); \ } \ RB_AUGMENT(elm); \ if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field))) { \ if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) \ RB_LEFT(RB_PARENT(elm, field), field) = (tmp); \ else \ RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \ } else \ (head)->rbh_root = (tmp); \ RB_RIGHT(tmp, field) = (elm); \ RB_PARENT(elm, field) = (tmp); \ RB_AUGMENT(tmp); \ if ((RB_PARENT(tmp, field))) \ RB_AUGMENT(RB_PARENT(tmp, field)); \ } while (0) /* Generates prototypes and inline functions */ #define RB_PROTOTYPE(name, type, field, cmp) \ void name##_RB_INSERT_COLOR(struct name *, struct type *); \ void name##_RB_REMOVE_COLOR(struct name *, struct type *, struct type *);\ struct type *name##_RB_REMOVE(struct name *, struct type *); \ struct type *name##_RB_INSERT(struct name *, struct type *); \ struct type *name##_RB_FIND(struct name *, struct type *); \ struct type *name##_RB_NEXT(struct type *); \ struct type *name##_RB_MINMAX(struct name *, int); \ \ /* Main rb operation. * Moves node close to the key of elm to top */ #define RB_GENERATE(name, type, field, cmp) \ void \ name##_RB_INSERT_COLOR(struct name *head, struct type *elm) \ { \ struct type *parent, *gparent, *tmp; \ while ((parent = RB_PARENT(elm, field)) && \ RB_COLOR(parent, field) == RB_RED) { \ gparent = RB_PARENT(parent, field); \ if (parent == RB_LEFT(gparent, field)) { \ tmp = RB_RIGHT(gparent, field); \ if (tmp && RB_COLOR(tmp, field) == RB_RED) { \ RB_COLOR(tmp, field) = RB_BLACK; \ RB_SET_BLACKRED(parent, gparent, field);\ elm = gparent; \ continue; \ } \ if (RB_RIGHT(parent, field) == elm) { \ RB_ROTATE_LEFT(head, parent, tmp, field);\ tmp = parent; \ parent = elm; \ elm = tmp; \ } \ RB_SET_BLACKRED(parent, gparent, field); \ RB_ROTATE_RIGHT(head, gparent, tmp, field); \ } else { \ tmp = RB_LEFT(gparent, field); \ if (tmp && RB_COLOR(tmp, field) == RB_RED) { \ RB_COLOR(tmp, field) = RB_BLACK; \ RB_SET_BLACKRED(parent, gparent, field);\ elm = gparent; \ continue; \ } \ if (RB_LEFT(parent, field) == elm) { \ RB_ROTATE_RIGHT(head, parent, tmp, field);\ tmp = parent; \ parent = elm; \ elm = tmp; \ } \ RB_SET_BLACKRED(parent, gparent, field); \ RB_ROTATE_LEFT(head, gparent, tmp, field); \ } \ } \ RB_COLOR(head->rbh_root, field) = RB_BLACK; \ } \ \ void \ name##_RB_REMOVE_COLOR(struct name *head, struct type *parent, struct type *elm) \ { \ struct type *tmp; \ while ((elm == NULL || RB_COLOR(elm, field) == RB_BLACK) && \ elm != RB_ROOT(head)) { \ if (RB_LEFT(parent, field) == elm) { \ tmp = RB_RIGHT(parent, field); \ if (RB_COLOR(tmp, field) == RB_RED) { \ RB_SET_BLACKRED(tmp, parent, field); \ RB_ROTATE_LEFT(head, parent, tmp, field);\ tmp = RB_RIGHT(parent, field); \ } \ if ((RB_LEFT(tmp, field) == NULL || \ RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\ (RB_RIGHT(tmp, field) == NULL || \ RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\ RB_COLOR(tmp, field) = RB_RED; \ elm = parent; \ parent = RB_PARENT(elm, field); \ } else { \ if (RB_RIGHT(tmp, field) == NULL || \ RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK) {\ struct type *oleft; \ if ((oleft = RB_LEFT(tmp, field)))\ RB_COLOR(oleft, field) = RB_BLACK;\ RB_COLOR(tmp, field) = RB_RED; \ RB_ROTATE_RIGHT(head, tmp, oleft, field);\ tmp = RB_RIGHT(parent, field); \ } \ RB_COLOR(tmp, field) = RB_COLOR(parent, field);\ RB_COLOR(parent, field) = RB_BLACK; \ if (RB_RIGHT(tmp, field)) \ RB_COLOR(RB_RIGHT(tmp, field), field) = RB_BLACK;\ RB_ROTATE_LEFT(head, parent, tmp, field);\ elm = RB_ROOT(head); \ break; \ } \ } else { \ tmp = RB_LEFT(parent, field); \ if (RB_COLOR(tmp, field) == RB_RED) { \ RB_SET_BLACKRED(tmp, parent, field); \ RB_ROTATE_RIGHT(head, parent, tmp, field);\ tmp = RB_LEFT(parent, field); \ } \ if ((RB_LEFT(tmp, field) == NULL || \ RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\ (RB_RIGHT(tmp, field) == NULL || \ RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\ RB_COLOR(tmp, field) = RB_RED; \ elm = parent; \ parent = RB_PARENT(elm, field); \ } else { \ if (RB_LEFT(tmp, field) == NULL || \ RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) {\ struct type *oright; \ if ((oright = RB_RIGHT(tmp, field)))\ RB_COLOR(oright, field) = RB_BLACK;\ RB_COLOR(tmp, field) = RB_RED; \ RB_ROTATE_LEFT(head, tmp, oright, field);\ tmp = RB_LEFT(parent, field); \ } \ RB_COLOR(tmp, field) = RB_COLOR(parent, field);\ RB_COLOR(parent, field) = RB_BLACK; \ if (RB_LEFT(tmp, field)) \ RB_COLOR(RB_LEFT(tmp, field), field) = RB_BLACK;\ RB_ROTATE_RIGHT(head, parent, tmp, field);\ elm = RB_ROOT(head); \ break; \ } \ } \ } \ if (elm) \ RB_COLOR(elm, field) = RB_BLACK; \ } \ \ struct type * \ name##_RB_REMOVE(struct name *head, struct type *elm) \ { \ struct type *child, *parent, *old = elm; \ int color; \ if (RB_LEFT(elm, field) == NULL) \ child = RB_RIGHT(elm, field); \ else if (RB_RIGHT(elm, field) == NULL) \ child = RB_LEFT(elm, field); \ else { \ struct type *left; \ elm = RB_RIGHT(elm, field); \ while ((left = RB_LEFT(elm, field))) \ elm = left; \ child = RB_RIGHT(elm, field); \ parent = RB_PARENT(elm, field); \ color = RB_COLOR(elm, field); \ if (child) \ RB_PARENT(child, field) = parent; \ if (parent) { \ if (RB_LEFT(parent, field) == elm) \ RB_LEFT(parent, field) = child; \ else \ RB_RIGHT(parent, field) = child; \ RB_AUGMENT(parent); \ } else \ RB_ROOT(head) = child; \ if (RB_PARENT(elm, field) == old) \ parent = elm; \ (elm)->field = (old)->field; \ if (RB_PARENT(old, field)) { \ if (RB_LEFT(RB_PARENT(old, field), field) == old)\ RB_LEFT(RB_PARENT(old, field), field) = elm;\ else \ RB_RIGHT(RB_PARENT(old, field), field) = elm;\ RB_AUGMENT(RB_PARENT(old, field)); \ } else \ RB_ROOT(head) = elm; \ RB_PARENT(RB_LEFT(old, field), field) = elm; \ if (RB_RIGHT(old, field)) \ RB_PARENT(RB_RIGHT(old, field), field) = elm; \ if (parent) { \ left = parent; \ do { \ RB_AUGMENT(left); \ } while ((left = RB_PARENT(left, field))); \ } \ goto color; \ } \ parent = RB_PARENT(elm, field); \ color = RB_COLOR(elm, field); \ if (child) \ RB_PARENT(child, field) = parent; \ if (parent) { \ if (RB_LEFT(parent, field) == elm) \ RB_LEFT(parent, field) = child; \ else \ RB_RIGHT(parent, field) = child; \ RB_AUGMENT(parent); \ } else \ RB_ROOT(head) = child; \ color: \ if (color == RB_BLACK) \ name##_RB_REMOVE_COLOR(head, parent, child); \ return (old); \ } \ \ /* Inserts a node into the RB tree */ \ struct type * \ name##_RB_INSERT(struct name *head, struct type *elm) \ { \ struct type *tmp; \ struct type *parent = NULL; \ int comp = 0; \ tmp = RB_ROOT(head); \ while (tmp) { \ parent = tmp; \ comp = (cmp)(elm, parent); \ if (comp < 0) \ tmp = RB_LEFT(tmp, field); \ else if (comp > 0) \ tmp = RB_RIGHT(tmp, field); \ else \ return (tmp); \ } \ RB_SET(elm, parent, field); \ if (parent != NULL) { \ if (comp < 0) \ RB_LEFT(parent, field) = elm; \ else \ RB_RIGHT(parent, field) = elm; \ RB_AUGMENT(parent); \ } else \ RB_ROOT(head) = elm; \ name##_RB_INSERT_COLOR(head, elm); \ return (NULL); \ } \ \ /* Finds the node with the same key as elm */ \ struct type * \ name##_RB_FIND(struct name *head, struct type *elm) \ { \ struct type *tmp = RB_ROOT(head); \ int comp; \ while (tmp) { \ comp = cmp(elm, tmp); \ if (comp < 0) \ tmp = RB_LEFT(tmp, field); \ else if (comp > 0) \ tmp = RB_RIGHT(tmp, field); \ else \ return (tmp); \ } \ return (NULL); \ } \ \ struct type * \ name##_RB_NEXT(struct type *elm) \ { \ if (RB_RIGHT(elm, field)) { \ elm = RB_RIGHT(elm, field); \ while (RB_LEFT(elm, field)) \ elm = RB_LEFT(elm, field); \ } else { \ if (RB_PARENT(elm, field) && \ (elm == RB_LEFT(RB_PARENT(elm, field), field))) \ elm = RB_PARENT(elm, field); \ else { \ while (RB_PARENT(elm, field) && \ (elm == RB_RIGHT(RB_PARENT(elm, field), field)))\ elm = RB_PARENT(elm, field); \ elm = RB_PARENT(elm, field); \ } \ } \ return (elm); \ } \ \ struct type * \ name##_RB_MINMAX(struct name *head, int val) \ { \ struct type *tmp = RB_ROOT(head); \ struct type *parent = NULL; \ while (tmp) { \ parent = tmp; \ if (val < 0) \ tmp = RB_LEFT(tmp, field); \ else \ tmp = RB_RIGHT(tmp, field); \ } \ return (parent); \ } #define RB_NEGINF -1 #define RB_INF 1 #define RB_INSERT(name, x, y) name##_RB_INSERT(x, y) #define RB_REMOVE(name, x, y) name##_RB_REMOVE(x, y) #define RB_FIND(name, x, y) name##_RB_FIND(x, y) #define RB_NEXT(name, x, y) name##_RB_NEXT(y) #define RB_MIN(name, x) name##_RB_MINMAX(x, RB_NEGINF) #define RB_MAX(name, x) name##_RB_MINMAX(x, RB_INF) #define RB_FOREACH(x, name, head) \ for ((x) = RB_MIN(name, head); \ (x) != NULL; \ (x) = name##_RB_NEXT(x)) #endif /* _SYS_TREE_H_ */ unworkable-0.53/openbsd-compat/sys/queue.h0000644000014500017510000004330410710255600020125 0ustar michaelstaff/* $OpenBSD: queue.h,v 1.32 2007/04/30 18:42:34 pedro Exp $ */ /* $NetBSD: queue.h,v 1.11 1996/05/16 05:17:14 mycroft Exp $ */ /* * Copyright (c) 1991, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)queue.h 8.5 (Berkeley) 8/20/94 */ #ifndef _SYS_QUEUE_H_ #define _SYS_QUEUE_H_ /* * This file defines five types of data structures: singly-linked lists, * lists, simple queues, tail queues, and circular queues. * * * A singly-linked list is headed by a single forward pointer. The elements * are singly linked for minimum space and pointer manipulation overhead at * the expense of O(n) removal for arbitrary elements. New elements can be * added to the list after an existing element or at the head of the list. * Elements being removed from the head of the list should use the explicit * macro for this purpose for optimum efficiency. A singly-linked list may * only be traversed in the forward direction. Singly-linked lists are ideal * for applications with large datasets and few or no removals or for * implementing a LIFO queue. * * A list is headed by a single forward pointer (or an array of forward * pointers for a hash table header). The elements are doubly linked * so that an arbitrary element can be removed without a need to * traverse the list. New elements can be added to the list before * or after an existing element or at the head of the list. A list * may only be traversed in the forward direction. * * A simple queue is headed by a pair of pointers, one the head of the * list and the other to the tail of the list. The elements are singly * linked to save space, so elements can only be removed from the * head of the list. New elements can be added to the list before or after * an existing element, at the head of the list, or at the end of the * list. A simple queue may only be traversed in the forward direction. * * A tail queue is headed by a pair of pointers, one to the head of the * list and the other to the tail of the list. The elements are doubly * linked so that an arbitrary element can be removed without a need to * traverse the list. New elements can be added to the list before or * after an existing element, at the head of the list, or at the end of * the list. A tail queue may be traversed in either direction. * * A circle queue is headed by a pair of pointers, one to the head of the * list and the other to the tail of the list. The elements are doubly * linked so that an arbitrary element can be removed without a need to * traverse the list. New elements can be added to the list before or after * an existing element, at the head of the list, or at the end of the list. * A circle queue may be traversed in either direction, but has a more * complex end of list detection. * * For details on the use of these macros, see the queue(3) manual page. */ #if defined(QUEUE_MACRO_DEBUG) || (defined(_KERNEL) && defined(DIAGNOSTIC)) #define _Q_INVALIDATE(a) (a) = ((void *)-1) #else #define _Q_INVALIDATE(a) #endif /* * Singly-linked List definitions. */ #define SLIST_HEAD(name, type) \ struct name { \ struct type *slh_first; /* first element */ \ } #define SLIST_HEAD_INITIALIZER(head) \ { NULL } #define SLIST_ENTRY(type) \ struct { \ struct type *sle_next; /* next element */ \ } /* * Singly-linked List access methods. */ #define SLIST_FIRST(head) ((head)->slh_first) #define SLIST_END(head) NULL #define SLIST_EMPTY(head) (SLIST_FIRST(head) == SLIST_END(head)) #define SLIST_NEXT(elm, field) ((elm)->field.sle_next) #define SLIST_FOREACH(var, head, field) \ for((var) = SLIST_FIRST(head); \ (var) != SLIST_END(head); \ (var) = SLIST_NEXT(var, field)) #define SLIST_FOREACH_PREVPTR(var, varp, head, field) \ for ((varp) = &SLIST_FIRST((head)); \ ((var) = *(varp)) != SLIST_END(head); \ (varp) = &SLIST_NEXT((var), field)) /* * Singly-linked List functions. */ #define SLIST_INIT(head) { \ SLIST_FIRST(head) = SLIST_END(head); \ } #define SLIST_INSERT_AFTER(slistelm, elm, field) do { \ (elm)->field.sle_next = (slistelm)->field.sle_next; \ (slistelm)->field.sle_next = (elm); \ } while (0) #define SLIST_INSERT_HEAD(head, elm, field) do { \ (elm)->field.sle_next = (head)->slh_first; \ (head)->slh_first = (elm); \ } while (0) #define SLIST_REMOVE_NEXT(head, elm, field) do { \ (elm)->field.sle_next = (elm)->field.sle_next->field.sle_next; \ } while (0) #define SLIST_REMOVE_HEAD(head, field) do { \ (head)->slh_first = (head)->slh_first->field.sle_next; \ } while (0) #define SLIST_REMOVE(head, elm, type, field) do { \ if ((head)->slh_first == (elm)) { \ SLIST_REMOVE_HEAD((head), field); \ } else { \ struct type *curelm = (head)->slh_first; \ \ while (curelm->field.sle_next != (elm)) \ curelm = curelm->field.sle_next; \ curelm->field.sle_next = \ curelm->field.sle_next->field.sle_next; \ _Q_INVALIDATE((elm)->field.sle_next); \ } \ } while (0) /* * List definitions. */ #define LIST_HEAD(name, type) \ struct name { \ struct type *lh_first; /* first element */ \ } #define LIST_HEAD_INITIALIZER(head) \ { NULL } #define LIST_ENTRY(type) \ struct { \ struct type *le_next; /* next element */ \ struct type **le_prev; /* address of previous next element */ \ } /* * List access methods */ #define LIST_FIRST(head) ((head)->lh_first) #define LIST_END(head) NULL #define LIST_EMPTY(head) (LIST_FIRST(head) == LIST_END(head)) #define LIST_NEXT(elm, field) ((elm)->field.le_next) #define LIST_FOREACH(var, head, field) \ for((var) = LIST_FIRST(head); \ (var)!= LIST_END(head); \ (var) = LIST_NEXT(var, field)) /* * List functions. */ #define LIST_INIT(head) do { \ LIST_FIRST(head) = LIST_END(head); \ } while (0) #define LIST_INSERT_AFTER(listelm, elm, field) do { \ if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \ (listelm)->field.le_next->field.le_prev = \ &(elm)->field.le_next; \ (listelm)->field.le_next = (elm); \ (elm)->field.le_prev = &(listelm)->field.le_next; \ } while (0) #define LIST_INSERT_BEFORE(listelm, elm, field) do { \ (elm)->field.le_prev = (listelm)->field.le_prev; \ (elm)->field.le_next = (listelm); \ *(listelm)->field.le_prev = (elm); \ (listelm)->field.le_prev = &(elm)->field.le_next; \ } while (0) #define LIST_INSERT_HEAD(head, elm, field) do { \ if (((elm)->field.le_next = (head)->lh_first) != NULL) \ (head)->lh_first->field.le_prev = &(elm)->field.le_next;\ (head)->lh_first = (elm); \ (elm)->field.le_prev = &(head)->lh_first; \ } while (0) #define LIST_REMOVE(elm, field) do { \ if ((elm)->field.le_next != NULL) \ (elm)->field.le_next->field.le_prev = \ (elm)->field.le_prev; \ *(elm)->field.le_prev = (elm)->field.le_next; \ _Q_INVALIDATE((elm)->field.le_prev); \ _Q_INVALIDATE((elm)->field.le_next); \ } while (0) #define LIST_REPLACE(elm, elm2, field) do { \ if (((elm2)->field.le_next = (elm)->field.le_next) != NULL) \ (elm2)->field.le_next->field.le_prev = \ &(elm2)->field.le_next; \ (elm2)->field.le_prev = (elm)->field.le_prev; \ *(elm2)->field.le_prev = (elm2); \ _Q_INVALIDATE((elm)->field.le_prev); \ _Q_INVALIDATE((elm)->field.le_next); \ } while (0) /* * Simple queue definitions. */ #define SIMPLEQ_HEAD(name, type) \ struct name { \ struct type *sqh_first; /* first element */ \ struct type **sqh_last; /* addr of last next element */ \ } #define SIMPLEQ_HEAD_INITIALIZER(head) \ { NULL, &(head).sqh_first } #define SIMPLEQ_ENTRY(type) \ struct { \ struct type *sqe_next; /* next element */ \ } /* * Simple queue access methods. */ #define SIMPLEQ_FIRST(head) ((head)->sqh_first) #define SIMPLEQ_END(head) NULL #define SIMPLEQ_EMPTY(head) (SIMPLEQ_FIRST(head) == SIMPLEQ_END(head)) #define SIMPLEQ_NEXT(elm, field) ((elm)->field.sqe_next) #define SIMPLEQ_FOREACH(var, head, field) \ for((var) = SIMPLEQ_FIRST(head); \ (var) != SIMPLEQ_END(head); \ (var) = SIMPLEQ_NEXT(var, field)) /* * Simple queue functions. */ #define SIMPLEQ_INIT(head) do { \ (head)->sqh_first = NULL; \ (head)->sqh_last = &(head)->sqh_first; \ } while (0) #define SIMPLEQ_INSERT_HEAD(head, elm, field) do { \ if (((elm)->field.sqe_next = (head)->sqh_first) == NULL) \ (head)->sqh_last = &(elm)->field.sqe_next; \ (head)->sqh_first = (elm); \ } while (0) #define SIMPLEQ_INSERT_TAIL(head, elm, field) do { \ (elm)->field.sqe_next = NULL; \ *(head)->sqh_last = (elm); \ (head)->sqh_last = &(elm)->field.sqe_next; \ } while (0) #define SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL)\ (head)->sqh_last = &(elm)->field.sqe_next; \ (listelm)->field.sqe_next = (elm); \ } while (0) #define SIMPLEQ_REMOVE_HEAD(head, field) do { \ if (((head)->sqh_first = (head)->sqh_first->field.sqe_next) == NULL) \ (head)->sqh_last = &(head)->sqh_first; \ } while (0) /* * Tail queue definitions. */ #define TAILQ_HEAD(name, type) \ struct name { \ struct type *tqh_first; /* first element */ \ struct type **tqh_last; /* addr of last next element */ \ } #define TAILQ_HEAD_INITIALIZER(head) \ { NULL, &(head).tqh_first } #define TAILQ_ENTRY(type) \ struct { \ struct type *tqe_next; /* next element */ \ struct type **tqe_prev; /* address of previous next element */ \ } /* * tail queue access methods */ #define TAILQ_FIRST(head) ((head)->tqh_first) #define TAILQ_END(head) NULL #define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next) #define TAILQ_LAST(head, headname) \ (*(((struct headname *)((head)->tqh_last))->tqh_last)) /* XXX */ #define TAILQ_PREV(elm, headname, field) \ (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last)) #define TAILQ_EMPTY(head) \ (TAILQ_FIRST(head) == TAILQ_END(head)) #define TAILQ_FOREACH(var, head, field) \ for((var) = TAILQ_FIRST(head); \ (var) != TAILQ_END(head); \ (var) = TAILQ_NEXT(var, field)) #define TAILQ_FOREACH_REVERSE(var, head, headname, field) \ for((var) = TAILQ_LAST(head, headname); \ (var) != TAILQ_END(head); \ (var) = TAILQ_PREV(var, headname, field)) /* * Tail queue functions. */ #define TAILQ_INIT(head) do { \ (head)->tqh_first = NULL; \ (head)->tqh_last = &(head)->tqh_first; \ } while (0) #define TAILQ_INSERT_HEAD(head, elm, field) do { \ if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \ (head)->tqh_first->field.tqe_prev = \ &(elm)->field.tqe_next; \ else \ (head)->tqh_last = &(elm)->field.tqe_next; \ (head)->tqh_first = (elm); \ (elm)->field.tqe_prev = &(head)->tqh_first; \ } while (0) #define TAILQ_INSERT_TAIL(head, elm, field) do { \ (elm)->field.tqe_next = NULL; \ (elm)->field.tqe_prev = (head)->tqh_last; \ *(head)->tqh_last = (elm); \ (head)->tqh_last = &(elm)->field.tqe_next; \ } while (0) #define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \ if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\ (elm)->field.tqe_next->field.tqe_prev = \ &(elm)->field.tqe_next; \ else \ (head)->tqh_last = &(elm)->field.tqe_next; \ (listelm)->field.tqe_next = (elm); \ (elm)->field.tqe_prev = &(listelm)->field.tqe_next; \ } while (0) #define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \ (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \ (elm)->field.tqe_next = (listelm); \ *(listelm)->field.tqe_prev = (elm); \ (listelm)->field.tqe_prev = &(elm)->field.tqe_next; \ } while (0) #define TAILQ_REMOVE(head, elm, field) do { \ if (((elm)->field.tqe_next) != NULL) \ (elm)->field.tqe_next->field.tqe_prev = \ (elm)->field.tqe_prev; \ else \ (head)->tqh_last = (elm)->field.tqe_prev; \ *(elm)->field.tqe_prev = (elm)->field.tqe_next; \ _Q_INVALIDATE((elm)->field.tqe_prev); \ _Q_INVALIDATE((elm)->field.tqe_next); \ } while (0) #define TAILQ_REPLACE(head, elm, elm2, field) do { \ if (((elm2)->field.tqe_next = (elm)->field.tqe_next) != NULL) \ (elm2)->field.tqe_next->field.tqe_prev = \ &(elm2)->field.tqe_next; \ else \ (head)->tqh_last = &(elm2)->field.tqe_next; \ (elm2)->field.tqe_prev = (elm)->field.tqe_prev; \ *(elm2)->field.tqe_prev = (elm2); \ _Q_INVALIDATE((elm)->field.tqe_prev); \ _Q_INVALIDATE((elm)->field.tqe_next); \ } while (0) /* * Circular queue definitions. */ #define CIRCLEQ_HEAD(name, type) \ struct name { \ struct type *cqh_first; /* first element */ \ struct type *cqh_last; /* last element */ \ } #define CIRCLEQ_HEAD_INITIALIZER(head) \ { CIRCLEQ_END(&head), CIRCLEQ_END(&head) } #define CIRCLEQ_ENTRY(type) \ struct { \ struct type *cqe_next; /* next element */ \ struct type *cqe_prev; /* previous element */ \ } /* * Circular queue access methods */ #define CIRCLEQ_FIRST(head) ((head)->cqh_first) #define CIRCLEQ_LAST(head) ((head)->cqh_last) #define CIRCLEQ_END(head) ((void *)(head)) #define CIRCLEQ_NEXT(elm, field) ((elm)->field.cqe_next) #define CIRCLEQ_PREV(elm, field) ((elm)->field.cqe_prev) #define CIRCLEQ_EMPTY(head) \ (CIRCLEQ_FIRST(head) == CIRCLEQ_END(head)) #define CIRCLEQ_FOREACH(var, head, field) \ for((var) = CIRCLEQ_FIRST(head); \ (var) != CIRCLEQ_END(head); \ (var) = CIRCLEQ_NEXT(var, field)) #define CIRCLEQ_FOREACH_REVERSE(var, head, field) \ for((var) = CIRCLEQ_LAST(head); \ (var) != CIRCLEQ_END(head); \ (var) = CIRCLEQ_PREV(var, field)) /* * Circular queue functions. */ #define CIRCLEQ_INIT(head) do { \ (head)->cqh_first = CIRCLEQ_END(head); \ (head)->cqh_last = CIRCLEQ_END(head); \ } while (0) #define CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ (elm)->field.cqe_next = (listelm)->field.cqe_next; \ (elm)->field.cqe_prev = (listelm); \ if ((listelm)->field.cqe_next == CIRCLEQ_END(head)) \ (head)->cqh_last = (elm); \ else \ (listelm)->field.cqe_next->field.cqe_prev = (elm); \ (listelm)->field.cqe_next = (elm); \ } while (0) #define CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) do { \ (elm)->field.cqe_next = (listelm); \ (elm)->field.cqe_prev = (listelm)->field.cqe_prev; \ if ((listelm)->field.cqe_prev == CIRCLEQ_END(head)) \ (head)->cqh_first = (elm); \ else \ (listelm)->field.cqe_prev->field.cqe_next = (elm); \ (listelm)->field.cqe_prev = (elm); \ } while (0) #define CIRCLEQ_INSERT_HEAD(head, elm, field) do { \ (elm)->field.cqe_next = (head)->cqh_first; \ (elm)->field.cqe_prev = CIRCLEQ_END(head); \ if ((head)->cqh_last == CIRCLEQ_END(head)) \ (head)->cqh_last = (elm); \ else \ (head)->cqh_first->field.cqe_prev = (elm); \ (head)->cqh_first = (elm); \ } while (0) #define CIRCLEQ_INSERT_TAIL(head, elm, field) do { \ (elm)->field.cqe_next = CIRCLEQ_END(head); \ (elm)->field.cqe_prev = (head)->cqh_last; \ if ((head)->cqh_first == CIRCLEQ_END(head)) \ (head)->cqh_first = (elm); \ else \ (head)->cqh_last->field.cqe_next = (elm); \ (head)->cqh_last = (elm); \ } while (0) #define CIRCLEQ_REMOVE(head, elm, field) do { \ if ((elm)->field.cqe_next == CIRCLEQ_END(head)) \ (head)->cqh_last = (elm)->field.cqe_prev; \ else \ (elm)->field.cqe_next->field.cqe_prev = \ (elm)->field.cqe_prev; \ if ((elm)->field.cqe_prev == CIRCLEQ_END(head)) \ (head)->cqh_first = (elm)->field.cqe_next; \ else \ (elm)->field.cqe_prev->field.cqe_next = \ (elm)->field.cqe_next; \ _Q_INVALIDATE((elm)->field.cqe_prev); \ _Q_INVALIDATE((elm)->field.cqe_next); \ } while (0) #define CIRCLEQ_REPLACE(head, elm, elm2, field) do { \ if (((elm2)->field.cqe_next = (elm)->field.cqe_next) == \ CIRCLEQ_END(head)) \ (head).cqh_last = (elm2); \ else \ (elm2)->field.cqe_next->field.cqe_prev = (elm2); \ if (((elm2)->field.cqe_prev = (elm)->field.cqe_prev) == \ CIRCLEQ_END(head)) \ (head).cqh_first = (elm2); \ else \ (elm2)->field.cqe_prev->field.cqe_next = (elm2); \ _Q_INVALIDATE((elm)->field.cqe_prev); \ _Q_INVALIDATE((elm)->field.cqe_next); \ } while (0) #endif /* !_SYS_QUEUE_H_ */ unworkable-0.53/openbsd-compat/verr.c0000644000014500017510000000410510725067751017146 0ustar michaelstaff/* $OpenBSD: verr.c,v 1.8 2005/08/08 08:05:34 espie Exp $ */ /*- * Copyright (c) 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "../includes.h" #include #include #include #include #include extern char *__progname; /* Program name, from crt0. */ void verr(int eval, const char *fmt, va_list ap) { int sverrno; sverrno = errno; (void)fprintf(stderr, "%s: ", __progname); if (fmt != NULL) { (void)vfprintf(stderr, fmt, ap); (void)fprintf(stderr, ": "); } (void)fprintf(stderr, "%s\n", strerror(sverrno)); exit(eval); } unworkable-0.53/openbsd-compat/strlcat.c0000644000014500017510000000330210710253773017635 0ustar michaelstaff/* $OpenBSD: strlcat.c,v 1.13 2005/08/08 08:05:37 espie Exp $ */ /* * Copyright (c) 1998 Todd C. Miller * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include size_t strlcat(char *, const char *, size_t); /* * Appends src to string dst of size siz (unlike strncat, siz is the * full size of dst, not space left). At most siz-1 characters * will be copied. Always NUL terminates (unless siz <= strlen(dst)). * Returns strlen(src) + MIN(siz, strlen(initial dst)). * If retval >= siz, truncation occurred. */ size_t strlcat(char *dst, const char *src, size_t siz) { char *d = dst; const char *s = src; size_t n = siz; size_t dlen; /* Find the end of dst and adjust bytes left but don't go past end */ while (n-- != 0 && *d != '\0') d++; dlen = d - dst; n = siz - dlen; if (n == 0) return(dlen + strlen(s)); while (*s != '\0') { if (n != 1) { *d++ = *s; n--; } s++; } *d = '\0'; return(dlen + (s - src)); /* count does not include NUL */ } unworkable-0.53/openbsd-compat/strlcpy.c0000644000014500017510000000310710710253773017664 0ustar michaelstaff/* $OpenBSD: strlcpy.c,v 1.11 2006/05/05 15:27:38 millert Exp $ */ /* * Copyright (c) 1998 Todd C. Miller * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include size_t strlcpy(char *, const char *, size_t); /* * Copy src to string dst of size siz. At most siz-1 characters * will be copied. Always NUL terminates (unless siz == 0). * Returns strlen(src); if retval >= siz, truncation occurred. */ size_t strlcpy(char *dst, const char *src, size_t siz) { char *d = dst; const char *s = src; size_t n = siz; /* Copy as many bytes as will fit */ if (n != 0) { while (--n != 0) { if ((*d++ = *s++) == '\0') break; } } /* Not enough room in dst, add NUL and traverse rest of src */ if (n == 0) { if (siz != 0) *d = '\0'; /* NUL-terminate dst */ while (*s++) ; } return(s - src - 1); /* count does not include NUL */ } unworkable-0.53/openbsd-compat/warnx.c0000644000014500017510000000341010725067751017325 0ustar michaelstaff/* $OpenBSD: warnx.c,v 1.7 2005/08/08 08:05:34 espie Exp $ */ /*- * Copyright (c) 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "../includes.h" #include void warnx(const char *fmt, ...) { va_list ap; va_start(ap, fmt); vwarnx(fmt, ap); va_end(ap); } unworkable-0.53/openbsd-compat/verrx.c0000644000014500017510000000361210725067751017340 0ustar michaelstaff/* $OpenBSD: verrx.c,v 1.8 2005/08/08 08:05:34 espie Exp $ */ /*- * Copyright (c) 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "../includes.h" extern char *__progname; /* Program name, from crt0. */ void verrx(int eval, const char *fmt, va_list ap) { (void)fprintf(stderr, "%s: ", __progname); if (fmt != NULL) (void)vfprintf(stderr, fmt, ap); (void)fprintf(stderr, "\n"); exit(eval); } unworkable-0.53/openbsd-compat/errx.c0000644000014500017510000000342610725067751017155 0ustar michaelstaff/* $OpenBSD: errx.c,v 1.8 2005/08/08 08:05:34 espie Exp $ */ /*- * Copyright (c) 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "../includes.h" #include void errx(int eval, const char *fmt, ...) { va_list ap; va_start(ap, fmt); verrx(eval, fmt, ap); va_end(ap); } unworkable-0.53/openbsd-compat/err.c0000644000014500017510000000342210725067751016761 0ustar michaelstaff/* $OpenBSD: err.c,v 1.9 2005/08/08 08:05:33 espie Exp $ */ /*- * Copyright (c) 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "../includes.h" #include void err(int eval, const char *fmt, ...) { va_list ap; va_start(ap, fmt); verr(eval, fmt, ap); va_end(ap); } unworkable-0.53/openbsd-compat/sha1.c0000644000014500017510000001427110710253773017024 0ustar michaelstaff/* $OpenBSD: sha1.c,v 1.20 2005/08/08 08:05:35 espie Exp $ */ /* * SHA-1 in C * By Steve Reid * 100% Public Domain * * Test Vectors (from FIPS PUB 180-1) * "abc" * A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D * "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" * 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1 * A million repetitions of "a" * 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F */ #include #include #include #include "sha1.h" #define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits)))) /* * blk0() and blk() perform the initial expand. * I got the idea of expanding during the round function from SSLeay */ #if BYTE_ORDER == LITTLE_ENDIAN # define blk0(i) (block->l[i] = (rol(block->l[i],24)&0xFF00FF00) \ |(rol(block->l[i],8)&0x00FF00FF)) #else # define blk0(i) block->l[i] #endif #define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \ ^block->l[(i+2)&15]^block->l[i&15],1)) /* * (R0+R1), R2, R3, R4 are the different operations (rounds) used in SHA1 */ #define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30); #define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30); #define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30); #define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30); #define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30); /* * Hash a single 512-bit block. This is the core of the algorithm. */ void SHA1Transform(u_int32_t state[5], const u_int8_t buffer[SHA1_BLOCK_LENGTH]) { u_int32_t a, b, c, d, e; u_int8_t workspace[SHA1_BLOCK_LENGTH]; typedef union { u_int8_t c[64]; u_int32_t l[16]; } CHAR64LONG16; CHAR64LONG16 *block = (CHAR64LONG16 *)workspace; (void)memcpy(block, buffer, SHA1_BLOCK_LENGTH); /* Copy context->state[] to working vars */ a = state[0]; b = state[1]; c = state[2]; d = state[3]; e = state[4]; /* 4 rounds of 20 operations each. Loop unrolled. */ R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3); R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7); R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11); R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15); R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19); R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23); R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27); R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31); R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35); R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39); R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43); R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47); R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51); R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55); R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59); R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63); R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67); R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71); R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75); R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79); /* Add the working vars back into context.state[] */ state[0] += a; state[1] += b; state[2] += c; state[3] += d; state[4] += e; /* Wipe variables */ a = b = c = d = e = 0; } /* * SHA1Init - Initialize new context */ void SHA1Init(SHA1_CTX *context) { /* SHA1 initialization constants */ context->count = 0; context->state[0] = 0x67452301; context->state[1] = 0xEFCDAB89; context->state[2] = 0x98BADCFE; context->state[3] = 0x10325476; context->state[4] = 0xC3D2E1F0; } /* * Run your data through this. */ void SHA1Update(SHA1_CTX *context, const u_int8_t *data, size_t len) { size_t i, j; j = (size_t)((context->count >> 3) & 63); context->count += (len << 3); if ((j + len) > 63) { (void)memcpy(&context->buffer[j], data, (i = 64-j)); SHA1Transform(context->state, context->buffer); for ( ; i + 63 < len; i += 64) SHA1Transform(context->state, (u_int8_t *)&data[i]); j = 0; } else { i = 0; } (void)memcpy(&context->buffer[j], &data[i], len - i); } /* * Add padding and return the message digest. */ void SHA1Pad(SHA1_CTX *context) { u_int8_t finalcount[8]; u_int i; for (i = 0; i < 8; i++) { finalcount[i] = (u_int8_t)((context->count >> ((7 - (i & 7)) * 8)) & 255); /* Endian independent */ } SHA1Update(context, (u_int8_t *)"\200", 1); while ((context->count & 504) != 448) SHA1Update(context, (u_int8_t *)"\0", 1); SHA1Update(context, finalcount, 8); /* Should cause a SHA1Transform() */ } void SHA1Final(u_int8_t digest[SHA1_DIGEST_LENGTH], SHA1_CTX *context) { u_int i; SHA1Pad(context); if (digest) { for (i = 0; i < SHA1_DIGEST_LENGTH; i++) { digest[i] = (u_int8_t) ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255); } memset(context, 0, sizeof(*context)); } } /* sha1hl.c * ---------------------------------------------------------------------------- * "THE BEER-WARE LICENSE" (Revision 42): * wrote this file. As long as you retain this notice you * can do whatever you want with this stuff. If we meet some day, and you think * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp * ---------------------------------------------------------------------------- */ char * SHA1Data (const u_int8_t *data, size_t len, char *buf) { SHA1_CTX ctx; /* XXX: buf may be NULL ? */ SHA1Init(&ctx); SHA1Update(&ctx, data, len); return(SHA1End(&ctx, buf)); } char * SHA1End(SHA1_CTX *ctx, char *buf) { int i; char *p = buf; u_char digest[20]; static const char hex[]="0123456789abcdef"; /* buf may be NULL */ if (p == NULL && (p = malloc(41)) == NULL) return 0; SHA1Final(digest,ctx); for (i = 0; i < 20; i++) { p[i + i] = hex[((u_int32_t)digest[i]) >> 4]; p[i + i + 1] = hex[digest[i] & 0x0f]; } p[i + i] = '\0'; return(p); } unworkable-0.53/openbsd-compat/strtonum.c0000644000014500017510000000377410710253773020071 0ustar michaelstaff/* $OpenBSD: strtonum.c,v 1.6 2004/08/03 19:38:01 millert Exp $ */ /* * Copyright (c) 2004 Ted Unangst and Todd Miller * All rights reserved. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #define INVALID 1 #define TOOSMALL 2 #define TOOLARGE 3 /* From OpenBSD/i386, could be different on amd64, but this should be * sufficient for our uses here. */ #define LLONG_MIN (-0x7fffffffffffffffLL-1) #define LLONG_MAX 0x7fffffffffffffffLL long long strtonum(const char *, long long, long long, const char **); long long strtonum(const char *numstr, long long minval, long long maxval, const char **errstrp) { long long ll = 0; char *ep; int error = 0; struct errval { const char *errstr; int err; } sev[4] = { { NULL, 0 }, { "invalid", EINVAL }, { "too small", ERANGE }, { "too large", ERANGE }, }; sev[0].err = errno; errno = 0; if (minval > maxval) error = INVALID; else { ll = strtoll(numstr, &ep, 10); if (numstr == ep || *ep != '\0') error = INVALID; else if ((ll == LLONG_MIN && errno == ERANGE) || ll < minval) error = TOOSMALL; else if ((ll == LLONG_MAX && errno == ERANGE) || ll > maxval) error = TOOLARGE; } if (errstrp != NULL) *errstrp = sev[error].errstr; errno = sev[error].err; if (error) ll = 0; return (ll); } unworkable-0.53/scheduler.c0000644000014500017510000004407411072270163015231 0ustar michaelstaff/* $Id: scheduler.c,v 1.17 2008-10-06 01:57:07 niallo Exp $ */ /* * Copyright (c) 2006, 2007, 2008 Niall O'Higgins * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include /* solaris 10 */ #if defined(__SVR4) && defined(__sun) #include #endif #include "includes.h" static int scheduler_piece_assigned(struct session *, struct torrent_piece *); static u_int32_t scheduler_piece_find_rarest(struct peer *, int, int *); static int scheduler_is_endgame(struct session *); static int scheduler_threshold_kill(struct peer *); static void scheduler_dequeue_uploads(struct peer *); static int scheduler_reap_dead(struct session *, struct peer *); static void scheduler_fill_requests(struct session *, struct peer *); static void scheduler_choke_algorithm(struct session *, time_t *); static void scheduler_endgame_algorithm(struct session *); /* * scheduler_is_endgame() * * Are we in the end game? * Returns 1 if true, zero if false. */ static int scheduler_is_endgame(struct session *sc) { struct torrent_piece *tpp; u_int32_t i; int not_queued = 0; for (i = 0; i < sc->tp->num_pieces; i++) { if ((tpp = torrent_piece_find(sc->tp, i)) == NULL) errx(1, "scheduler_is_endgame(): torrent_piece_find"); if (!(tpp->flags & TORRENT_PIECE_CKSUMOK)) { /* are all the blocks in the queue? */ if (!scheduler_piece_assigned(sc, tpp)) { not_queued = 1; break; } } } return (!not_queued); } /* * scheduler_piece_assigned() * * Are all this piece's blocks in the download queue and assigned to a peer? * Returns 1 on success, 0 on failure */ static int scheduler_piece_assigned(struct session *sc, struct torrent_piece *tpp) { u_int32_t off; struct piece_dl *pd; /* if this piece and all its blocks are already in our download queue, * skip it */ for (off = 0; ; off += BLOCK_SIZE) { if (off >= tpp->len) return (1); pd = network_piece_dl_find(sc, NULL, tpp->index, off); /* if a piece doesn't exist, or has been orphaned, then its not * done */ if (pd == NULL || (pd->bytes != pd->len && pd->pc == NULL)) return (0); } } /* * scheduler_piece_cmp() * * Used by qsort() */ static int scheduler_piece_cmp(const void *a, const void *b) { const struct piececounter *x, *y; x = a; y = b; return (x->count - y->count); } /* * scheduler_peer_cmp() * * Used by qsort() */ static int scheduler_peer_cmp(const void *a, const void *b) { const struct peercounter *x, *y; x = a; y = b; return (y->rate - x->rate); } /* * scheduler_peer_speedrank() * * For a given session return an array of peers sorted by their download speeds. */ static struct peercounter * scheduler_peer_speedrank(struct session *sc) { struct peer *p; struct peercounter *peers; u_int32_t i = 0; peers = xcalloc(sc->num_peers, sizeof(*peers)); TAILQ_FOREACH(p, &sc->peers, peer_list) { peers[i].peer = p; if (p->state & PEER_STATE_INTERESTED) { peers[i].rate = network_peer_rxrate(p); /* kind of a hack so we don't unchoke * un-interested peers */ if (peers[i].rate == 0) peers[i].rate = 1; } i++; } if (i != sc->num_peers) errx(1, "scheduler_peer_speedrank: peer number mismatch (i: %u num_peers: %u)", i, sc->num_peers); qsort(peers, sc->num_peers, sizeof(*peers), scheduler_peer_cmp); return (peers); } /* * scheduler_piece_rarityarray() * * For a given session return sorted array of piece counts. */ static void scheduler_piece_rarityarray(struct session *sc) { struct piececounter *pieces; struct peer *p; u_int32_t i, count, pos, len; pos = 0; len = sc->tp->num_pieces; pieces = xcalloc(len, sizeof(*pieces)); /* counts for each piece */ for (i = 0; i < len; i++) { count = 0; /* otherwise count it */ TAILQ_FOREACH(p, &sc->peers, peer_list) { if (!(p->state & PEER_STATE_ESTABLISHED)) continue; if (p->bitfield != NULL && util_getbit(p->bitfield, i)) count++; } if (pos > len) errx(1, "scheduler_piece_rarityarray: pos is %u should be %u\n", pos, (sc->tp->num_pieces - sc->tp->good_pieces - 1)); pieces[pos].count = count; pieces[pos].idx = i; pos++; } /* sort the rarity array */ qsort(pieces, len, sizeof(*pieces), scheduler_piece_cmp); /* set the session timestamp */ sc->last_rarity = time(NULL); if (sc->rarity_array != NULL) xfree(sc->rarity_array); sc->rarity_array = pieces; } #define FIND_RAREST_IGNORE_ASSIGNED 0 #define FIND_RAREST_ABSOLUTE 1 /* * scheduler_piece_find_rarest() * * Find the rarest piece, allowing for certain conditions. */ static u_int32_t scheduler_piece_find_rarest(struct peer *p, int flag, int *res) { struct torrent_piece *tpp; struct piececounter *pieces; u_int32_t i; int found = 0; tpp = NULL; *res = 1; #define RARITY_AGE 5 if (time(NULL) - p->sc->last_rarity > RARITY_AGE) scheduler_piece_rarityarray(p->sc); pieces = p->sc->rarity_array; /* find the rarest piece amongst our peers */ for (i = 0; i < p->sc->tp->num_pieces; i++) { /* if this peer doesn't have this piece, skip it */ if (p->bitfield != NULL && !util_getbit(p->bitfield, i)) continue; tpp = torrent_piece_find(p->sc->tp, pieces[i].idx); /* if we have this piece, skip it */ if (tpp->flags & TORRENT_PIECE_CKSUMOK) { continue; } if (flag == FIND_RAREST_IGNORE_ASSIGNED) { /* if this piece and all its blocks are already * assigned to a peer and worked on skip it */ if (scheduler_piece_assigned(p->sc, tpp)) { continue; } else { found = 1; break; } } else { found = 1; break; } } *res = found; if (tpp == NULL) return (0); return (tpp->index); } /* * scheduler_threshold_kill() * * If we have not received data in PEER_COMMS_THRESHOLD, * remove the block requests from our list and kill the peer. */ static int scheduler_threshold_kill(struct peer *p) { if ((p->state & PEER_STATE_BITFIELD || p->state & PEER_STATE_ESTABLISHED) && network_peer_lastcomms(p) >= PEER_COMMS_THRESHOLD) { trace("comms threshold exceeded for peer %s:%d", inet_ntoa(p->sa.sin_addr), ntohs(p->sa.sin_port)); p->state = 0; p->state |= PEER_STATE_DEAD; return (0); } return (-1); } /* * scheduler_dequeue_uploads() * * Dequeue a piece to peer, if there is one waiting. */ static void scheduler_dequeue_uploads(struct peer *p) { struct piece_ul *pu; /* honour one upload */ if ((pu = network_piece_ul_dequeue(p)) != NULL) { trace("dequeuing piece to peer %s:%d", inet_ntoa(p->sa.sin_addr), ntohs(p->sa.sin_port)); network_peer_write_piece(p, pu->idx, pu->off, pu->len); xfree(pu); pu = NULL; } } /* * scheduler_reap_dead() * * Reap peer if it is marked dead. */ static int scheduler_reap_dead(struct session *sc, struct peer *p) { /* if peer is marked dead, free it */ if (p->state & PEER_STATE_DEAD) { TAILQ_REMOVE(&sc->peers, p, peer_list); network_peer_free(p); sc->num_peers--; return (0); } return (-1); } /* * scheduler_fill_requests() * * If peer is not choked, make sure it has enough requests in its queue. */ static void scheduler_fill_requests(struct session *sc, struct peer *p) { struct piece_dl *pd; u_int64_t peer_rate; u_int32_t pieces_left, queue_len, i; int hint = 0; pieces_left = sc->tp->num_pieces - sc->tp->good_pieces; if (!(p->state & PEER_STATE_CHOKED) && pieces_left > 0) { peer_rate = network_peer_rxrate(p); /* for each 10k/sec on this peer, add a request. */ /* minimum queue length is 2, max is MAX_REQUESTS */ /* right shift by 13 is the same as div by 10240 */ queue_len = (u_int32_t) peer_rate >> 13; if (queue_len < 2) { queue_len = 2; } else if (queue_len > MAX_REQUESTS) { queue_len = MAX_REQUESTS; } /* test for overflow */ if (queue_len < p->dl_queue_len) { queue_len = 0; } else { /* queue_len is what the peer's queue length should be */ queue_len -= p->dl_queue_len; } for (i = 0; i < queue_len; i++) { pd = scheduler_piece_gimme(p, 0, &hint); /* probably means no bitfield from this peer yet, or all requests are in transit. give it some time. */ if (pd == NULL) continue; network_peer_request_block(pd->pc, pd->idx, pd->off, pd->len); p->dl_queue_len++; } } } /* * scheduler_choke_algorithm() * * Every 10 seconds, sort peers by speed and unchoke the 3 fastest. */ static void scheduler_choke_algorithm(struct session *sc, time_t *now) { struct peer *p, *p2; struct peercounter *pc; u_int32_t i; int j, k, num_interested; p = p2 = NULL; if ((*now % 10) != 0) return; pc = scheduler_peer_speedrank(sc); for (i = 0; i < MIN(3, sc->num_peers); i++) { /* if this peer is already unchoked, leave it */ if (!(pc[i].peer->state & PEER_STATE_AMCHOKING)) continue; /* don't unchoke peers who have not expressed interest */ if (!(pc[i].peer->state & PEER_STATE_INTERESTED)) continue; /* now we can unchoke this one */ trace("fastest unchoke"); network_peer_write_unchoke(pc[i].peer); } if ((*now % 30) == 0 ) { num_interested = 0; TAILQ_FOREACH(p2, &sc->peers, peer_list) { if (p2->state & PEER_STATE_INTERESTED) num_interested++; } if (num_interested > 0) { j = random() % num_interested; p2 = TAILQ_FIRST(&sc->peers); for (k = 0; k < j; k++) { if (p2 == NULL) errx(1, "NULL peer"); if (!(pc->peer->state & PEER_STATE_INTERESTED)) { p2 = TAILQ_NEXT(p2, peer_list); continue; } } trace("opportunistic unchoke"); network_peer_write_unchoke(p2); } } /* choke any peers except for three fastest, and the one randomly selected */ TAILQ_FOREACH(p, &sc->peers, peer_list) { int c = 0; /* don't try to choke any of the peers * we just unchoked above */ for (i = 0; i < MIN(3, sc->num_peers); i++) { if (p == pc[i].peer || p == p2) { c = 1; break; } } if (c) continue; if (!(p->state & PEER_STATE_AMCHOKING)) network_peer_write_choke(p); } xfree(pc); } /* * scheduler_endgame_algorithm() * * Endgame handling. */ static void scheduler_endgame_algorithm(struct session *sc) { struct torrent_piece *tpp; struct peer *p; struct piece_dl *pd; u_int32_t i, off, len; int found; /* find incomplete pieces */ for (i = 0; i < sc->tp->num_pieces; i++) { if ((tpp = torrent_piece_find(sc->tp, i)) == NULL) errx(1, "scheduler(): torrent_piece_find"); if (tpp->flags & TORRENT_PIECE_CKSUMOK) continue; /* which peers have it? */ trace("we still need piece idx %u", i); TAILQ_FOREACH(p, &sc->peers, peer_list) { if (p->bitfield == NULL || util_getbit(p->bitfield, i)) continue; if (p->state & PEER_STATE_CHOKED) { trace(" (choked) peer %s:%d has it", inet_ntoa(p->sa.sin_addr), ntohs(p->sa.sin_port)); continue; } trace(" (unchoked) peer %s:%d has it", inet_ntoa(p->sa.sin_addr), ntohs(p->sa.sin_port)); /* find the un-queued pieces */ for (off = 0; off < tpp->len; off += BLOCK_SIZE) { found = 0; /* is this block offset already queued on this peer? */ TAILQ_FOREACH(pd, &p->peer_piece_dls, peer_piece_dl_list) { if (pd->idx == i && pd->off == off) { found = 1; break; } } if (found) continue; if (BLOCK_SIZE > tpp->len - off) { len = tpp->len - off; } else { len = BLOCK_SIZE; } pd = network_piece_dl_create(p, i, off, len); trace("choosing endgame dl (tpp->len %u) len %u idx %u off %u", tpp->len, len, i, off); network_peer_request_block(pd->pc, pd->idx, pd->off, pd->len); p->dl_queue_len++; } } } } /* * scheduler_piece_gimme() * * According to various selection strategies, hand me something to download. */ struct piece_dl * scheduler_piece_gimme(struct peer *peer, int flags, int *hint) { struct torrent_piece *tpp; struct piece_dl *pd; struct piece_dl_idxnode *pdin; u_int32_t i, j, idx, len, off, *pieces, peerpieces; int res; res = 0; idx = off = 0; tpp = NULL; /* if we have some blocks in a piece, try to complete that same piece */ RB_FOREACH(pdin, piece_dl_by_idxoff, &peer->sc->piece_dl_by_idxoff) { tpp = torrent_piece_find(peer->sc->tp, pdin->idx); if (!(tpp->flags & TORRENT_PIECE_CKSUMOK)) { /* if not all this piece's blocks are in the download * queue and this peer actually has this piece */ if (!scheduler_piece_assigned(peer->sc, tpp) && peer->bitfield != NULL && util_getbit(peer->bitfield, pdin->idx)) { idx = pdin->idx; goto get_block; } } } /* first 4 pieces should be chosen randomly */ if (peer->sc->tp->good_pieces < 4 && peer->sc->tp->num_pieces > 4) { /* check how many useful pieces this peer has */ peerpieces = 0; for (i = 0; i < peer->sc->tp->num_pieces; i++) { if (peer->bitfield != NULL && util_getbit(peer->bitfield, i)) { tpp = torrent_piece_find(peer->sc->tp, i); /* do we already have this piece? */ if (tpp->flags & TORRENT_PIECE_CKSUMOK) continue; /* is it already assigned? */ if (scheduler_piece_assigned(peer->sc, tpp)) continue; peerpieces++; } } /* peer has no pieces */ if (peerpieces == 0) return (NULL); /* build array of pieces this peer has */ pieces = xcalloc(peerpieces, sizeof(*pieces)); j = 0; for (i = 0; i < peer->sc->tp->num_pieces; i++) { if (peer->bitfield != NULL && util_getbit(peer->bitfield, i)) { tpp = torrent_piece_find(peer->sc->tp, i); /* do we already have this piece? */ if (tpp->flags & TORRENT_PIECE_CKSUMOK) continue; /* is it already assigned? */ if (scheduler_piece_assigned(peer->sc, tpp)) continue; pieces[j] = i; j++; } } /* select piece randomly */ idx = pieces[random() % peerpieces]; xfree(pieces); tpp = torrent_piece_find(peer->sc->tp, idx); } else { /* find the rarest piece that does not have all its blocks * already in the download queue */ idx = scheduler_piece_find_rarest(peer, FIND_RAREST_IGNORE_ASSIGNED, &res); /* there are no more pieces right now */ if (!res) return (NULL); tpp = torrent_piece_find(peer->sc->tp, idx); } get_block: if (flags & PIECE_GIMME_NOCREATE) { *hint = 1; return (NULL); } /* find the next block (by offset) in the piece, which is not already * assigned to a peer */ for (off = 0; ; off += BLOCK_SIZE) { if (off >= tpp->len) errx(1, "gone to a bad offset %u in idx %u, len %u", off, idx, tpp->len); pd = network_piece_dl_find(peer->sc, NULL, idx, off); /* no piece dl at all */ if (pd == NULL) { break; } else if (pd->pc == NULL && pd->bytes != pd->len) { /* piece dl exists, but it has been orphaned -> recycle * it */ trace("recycling dl (tpp->len %u) len %u idx %u off %u", tpp->len, pd->len, pd->idx, pd->off); pd->pc = peer; /* put it in this peer's list */ TAILQ_INSERT_TAIL(&peer->peer_piece_dls, pd, peer_piece_dl_list); return (pd); } } if (BLOCK_SIZE > tpp->len - off) { len = tpp->len - off; } else { len = BLOCK_SIZE; } pd = network_piece_dl_create(peer, idx, off, len); trace("choosing next dl (tpp->len %u) len %u (tpp->idx %u) idx %u off %u", tpp->len, len, tpp->index, idx, off); return (pd); } /* * scheduler() * * Bulk of decision making happens here. Runs every second, once announce is * complete. */ void scheduler(int fd, short type, void *arg) { struct peer *p, *nxt; struct session *sc = arg; struct timeval tv; /* piece rarity array */ struct piece_dl *pd; struct piece_dl_idxnode *pdin; u_int32_t pieces_left, reqs_outstanding, reqs_completed, reqs_orphaned; u_int32_t choked, unchoked; char tbuf[64]; time_t now; reqs_outstanding = reqs_completed = reqs_orphaned = choked = unchoked = 0; p = NULL; pd = NULL; timerclear(&tv); tv.tv_sec = 1; evtimer_set(&sc->scheduler_event, scheduler, sc); evtimer_add(&sc->scheduler_event, &tv); pieces_left = sc->tp->num_pieces - sc->tp->good_pieces; if (!TAILQ_EMPTY(&sc->peers)) { for (p = TAILQ_FIRST(&sc->peers); p; p = nxt) { nxt = TAILQ_NEXT(p, peer_list); if (p->state & PEER_STATE_CHOKED) { choked++; } else { unchoked++; } if (scheduler_reap_dead(sc, p) == 0) continue; if (scheduler_threshold_kill(p) == 0) continue; scheduler_dequeue_uploads(p); scheduler_fill_requests(sc, p); } } now = time(NULL); scheduler_choke_algorithm(sc, &now); if (scheduler_is_endgame(sc)) scheduler_endgame_algorithm(sc); /* try to get some more peers */ if (sc->num_peers < PEERS_WANTED && pieces_left > 0 && !sc->announce_underway && (now - sc->last_announce) > MIN_ANNOUNCE_INTERVAL) { /* XXX: But what if the tracker really only has a small number of peers? * We will keep asking over and over, wasting resources. * This should be fixed */ announce(sc, NULL); } /* print some trace info, if trace is enabled */ if (unworkable_trace == NULL) return; RB_FOREACH(pdin, piece_dl_by_idxoff, &sc->piece_dl_by_idxoff) { pd = TAILQ_FIRST(&pdin->idxnode_piece_dls); if (pd->pc == NULL) { reqs_orphaned++; strlcpy(tbuf, " [orphaned] ", sizeof(tbuf)); } else if (pd->pc->connfd != 0) { snprintf(tbuf, sizeof(tbuf), "assigned to: %s:%d", inet_ntoa(pd->pc->sa.sin_addr), ntohs(pd->pc->sa.sin_port)); } if (pd->bytes != pd->len) { reqs_outstanding++; } else { reqs_completed++; strlcat(tbuf, " [done] ", sizeof(tbuf)); } if ((now % 60) == 0) { trace("piece_dl: idx %u off: %u len: %u %s", pd->idx, pd->off, pd->len, tbuf); } } trace("Peers: %u (c %u/u %u) Good pieces: %u/%u Reqs outstanding/orphaned/completed: %u/%u/%u", sc->num_peers, choked, unchoked, sc->tp->good_pieces, sc->tp->num_pieces, reqs_outstanding, reqs_orphaned, reqs_completed); } unworkable-0.53/gui/0000755000014500017510000000000011310321004013644 5ustar michaelstaffunworkable-0.53/gui/conn.py0000644000014500017510000000622010764772434015210 0ustar michaelstaff#!/usr/local/bin/python # $Id: conn.py,v 1.3 2008-03-09 14:33:00 slander Exp $ # Copyright (c) 2007 Niall O'Higgins # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. import getopt import socket import sys import threading import time HOST = 'localhost' PORT = 6099 def usage(): print >> sys.stderr, "%s: [-h host] [-p port]" %(sys.argv[0]) os._exit(1) def status(ctl): print "peers: %s pieces: %s/%s downloaded: %s" %(len(ctl.peers), len(ctl.pieces), ctl.num_pieces, ctl.torrent_bytes + ctl.bytes) class CTLconnection(threading.Thread): ''' Connection to the control server, runs in its own thread ''' def __init__(self, host, port): self.host = host self.port = port self.num_peers = 0 self.num_pieces = 0 self.torrent_size = 0 self.torrent_bytes = 0 self.pieces = [] self.peers = [] self.bytes = 0 self.done = False self._socket = None self._f = None threading.Thread.__init__(self) def stop(self): self._f.close() self._done = True def run(self): try: self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self._socket.connect_ex((self.host, self.port)) self._f = self._socket.makefile() except socket.error, e: #ignore all socket errors pass try: for l in self._f: try: d = l.strip().split(':', 1) except: # ignore malformed line continue if d[0] == 'num_peers': if not isinstance(d[1], int): continue self.num_peers = int(d[1]) elif d[0] == 'num_pieces': self.num_pieces = int(d[1]) elif d[0] == 'torrent_size': self.torrent_size = int(d[1]) elif d[0] == 'torrent_bytes': self.torrent_bytes = int(d[1]) elif d[0] == 'pieces': try: new_pieces = d[1].split(',') new_pieces.sort() self.pieces = new_pieces except: # no pieces yet continue elif d[0] == 'bytes': self.bytes = int(d[1]) elif d[0] == 'peers': try: new_peers = d[1].split(',') new_peers.sort() self.peers = new_peers except: # no peers yet continue else: print "unkown message: %s" %(l) except socket.error, e: #ignore all socket errors sleep for 5 seconds and run again time.sleep(5) self.run() self._f.close() self.done = True try: opts, args = getopt.getopt(sys.argv[1:], "h:p:") except getopt.GetoptError: usage() for o, a in opts: if o == "-h": HOST = a if o == "-p": PORT = int(a) ctl = CTLconnection(HOST, PORT) ctl.start() while not ctl.done: status(ctl) time.sleep(5) unworkable-0.53/GNUmakefile0000644000014500017510000000550511246661315015163 0ustar michaelstaff# # Copyright (c) 2008 Michael Stapelberg # Copyright (c) 2006 Niall O'Higgins # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. # CC?= cc CFLAGS+= -Wall CFLAGS+= -Wstrict-prototypes -Wmissing-prototypes CFLAGS+= -Wmissing-declarations CFLAGS+= -Wshadow -Wpointer-arith -Wcast-qual CFLAGS+= -Wsign-compare -g -ggdb CFLAGS+= -Iopenbsd-compat PROG=unworkable SRCS=announce.c bencode.c buf.c ctl_server.c main.c network.c \ parse.y progressmeter.c scheduler.c torrent.c trace.c util.c xmalloc.c LIBS=-levent -lcrypto -lpthread UNAME=$(shell uname) ifneq (, $(filter Linux GNU GNU/%, $(UNAME))) SRCS+=openbsd-compat/strlcpy.c SRCS+=openbsd-compat/strlcat.c SRCS+=openbsd-compat/sha1.c SRCS+=openbsd-compat/strtonum.c CFLAGS+=-DNO_STRLCPY CFLAGS+=-DNO_STRLCAT CFLAGS+=-DNO_STRTONUM else ifeq ($(UNAME),sunos) SRCS+=openbsd-compat/err.c SRCS+=openbsd-compat/errx.c SRCS+=openbsd-compat/warn.c SRCS+=openbsd-compat/warnx.c SRCS+=openbsd-compat/verr.c SRCS+=openbsd-compat/verrx.c SRCS+=openbsd-compat/vwarnx.c SRCS+=openbsd-compat/vwarn.c CFLAGS+=-DNO_ERR CFLAGS+=-I/usr/local/ssl/include LIBS+=-L/usr/local/ssl/lib LIBS+=-L/usr/ucblib LIBS+=-lsocket LIBS+=-lnsl LIBS+=-lucb else ifeq ($(UNAME),Darwin) LIBS+=-L/opt/local/lib CFLAGS+=-I/opt/local/include endif endif endif OBJS=$(patsubst %.y,%.o,$(patsubst %.c,%.o,${SRCS})) MAN=unworkable.1 all: ${PROG} ${PROG}: ${OBJS} ${CC} -o $@ ${LDFLAGS} ${OBJS} ${LIBS} clean: rm -rf *.o openbsd-compat/*.o *.so ${PROG} y.tab.h distclean: clean rm -rf unworkable libunworkable.so: CFLAGS=-fPIC make unworkable ${CC} -o $@ ${LDFLAGS} -shared ${OBJS} ${LIBS} install: install -m 755 -d $(DESTDIR)/usr/bin install -m 755 -d $(DESTDIR)/usr/share/man/man1 install -m 755 ${PROG} $(DESTDIR)/usr/bin install -m 644 ${MAN} $(DESTDIR)/usr/share/man/man1 install-library: install -m 755 -d /usr/local/lib install -m 755 -d /usr/local/include/unworkable/sys install -m 755 libunworkable.so /usr/local/lib install -m 644 unworkable.h /usr/local/include/unworkable install -m 644 openbsd-compat/sys/tree.h /usr/local/include/unworkable/sys install -m 644 unworkable.pc /usr/local/lib/pkgconfig/unworkable.pc unworkable-0.53/ctl_server.c0000644000014500017510000002230411061134670015413 0ustar michaelstaff/* $Id: ctl_server.c,v 1.6 2008-09-08 05:35:52 niallo Exp $ */ /* * Copyright (c) 2007, 2008 Niall O'Higgins * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "includes.h" char *gui_port; static void ctl_server_handle_connect(struct bufferevent *, short, void *); static void ctl_server_conn_bootstrap(struct ctl_server_conn *); static struct ctl_server_conn * ctl_server_conn_create(struct ctl_server *); static void ctl_server_conn_free(struct ctl_server_conn *); static void ctl_server_handle_conn_message(struct bufferevent *, void *); static void ctl_server_handle_conn_error(struct bufferevent *, short, void *); static void ctl_server_broadcast_message(struct ctl_server *, char *); static void ctl_server_write_message(struct ctl_server_conn *, char *); static char * ctl_server_pieces(struct session *); static char * ctl_server_peers(struct session *); /* * ctl_server_start() * * Start a new control server for the specified session on the specified port. */ void ctl_server_start(struct session *sc, char *port, off_t started) { struct ctl_server *cs; cs = xmalloc(sizeof(*cs)); memset(cs, 0, sizeof(*cs)); TAILQ_INIT(&cs->conns); sc->ctl_server = cs; cs->started = started; cs->sc = sc; cs->fd = network_listen("0.0.0.0", port); cs->bev = bufferevent_new(cs->fd, NULL, NULL, ctl_server_handle_connect, cs); if (cs->bev == NULL) errx(1, "ctl_server_start: bufferevent_new failure"); bufferevent_enable(cs->bev, EV_PERSIST|EV_READ); } /* * ctl_server_notify_bytes() * * Notify control connections of number of bytes received. */ void ctl_server_notify_bytes(struct session *sc, off_t bytes) { char *msg; int l; if (sc->ctl_server == NULL) return; msg = xmalloc(CTL_MESSAGE_LEN); memset(msg, '\0', CTL_MESSAGE_LEN); l = snprintf(msg, CTL_MESSAGE_LEN, "bytes:%jd\r\n", (intmax_t)bytes); if (l == -1 || l >= (int)CTL_MESSAGE_LEN) errx(1, "ctl_server_notify_bytes() string truncation"); ctl_server_broadcast_message(sc->ctl_server, msg); xfree(msg); } /* * ctl_server_notify_pieces() * * Notify control connections of pieces received. */ void ctl_server_notify_pieces(struct session *sc) { char *msg; if (sc->ctl_server == NULL) return; msg = ctl_server_pieces(sc); ctl_server_broadcast_message(sc->ctl_server, msg); xfree(msg); } /* * ctl_server_notify_peers() * * Notify control connections of connected peers. */ void ctl_server_notify_peers(struct session *sc) { char *msg; if (sc->ctl_server == NULL) return; msg = ctl_server_peers(sc); ctl_server_broadcast_message(sc->ctl_server, msg); xfree(msg); } /* * ctl_server_handle_connect() * * Handle incoming connections to the control server. */ static void ctl_server_handle_connect(struct bufferevent *bufev, short error, void *data) { struct ctl_server *cs; struct ctl_server_conn *csc; socklen_t addrlen; trace("ctl_server_handle_connect() called"); if (error & EVBUFFER_TIMEOUT) errx(1, "timeout"); if (error & EVBUFFER_EOF) errx(1, "eof"); cs = data; csc = ctl_server_conn_create(cs); addrlen = sizeof(csc->sa); trace("ctl_server_handle_connect() accepting connection"); if ((csc->fd = accept(cs->fd, (struct sockaddr *) &csc->sa, &addrlen)) == -1) { trace("ctl_server_handle_connect() accept error"); ctl_server_conn_free(csc); return; } trace("ctl_server_handle_connectt() accepted connection: %s:%d", inet_ntoa(csc->sa.sin_addr), ntohs(csc->sa.sin_port)); csc->bev = bufferevent_new(csc->fd, ctl_server_handle_conn_message, NULL, ctl_server_handle_conn_error, csc); if (csc->bev == NULL) errx(1, "ctl_server_handle_connect(): bufferevent_new failure"); bufferevent_enable(csc->bev, EV_READ|EV_WRITE); cs->bev = bufferevent_new(cs->fd, NULL, NULL, ctl_server_handle_connect, cs); if (cs->bev == NULL) errx(1, "ctl_server_start: bufferevent_new failure"); bufferevent_enable(cs->bev, EV_PERSIST|EV_READ); TAILQ_INSERT_TAIL(&cs->conns, csc, conn_list); ctl_server_conn_bootstrap(csc); } /* * ctl_server_conn_create() * * Create a connection to the control server. */ static struct ctl_server_conn * ctl_server_conn_create(struct ctl_server *cs) { struct ctl_server_conn *csc; csc = xmalloc(sizeof(*csc)); memset(csc, 0, sizeof(*csc)); csc->cs = cs; return (csc); } /* * ctl_server_conn_free() * * Free a connection to the control server. */ static void ctl_server_conn_free(struct ctl_server_conn *csc) { (void) close(csc->fd); xfree(csc); } /* * ctl_server_conn_bootstrap() * * Send initial state data to this new connection. */ static void ctl_server_conn_bootstrap(struct ctl_server_conn *csc) { off_t len; char *msg; int l; trace("bootstrapping"); #define BOOTSTRAP_LEN 1024 if (csc->cs->sc->tp->type == SINGLEFILE) { len = csc->cs->sc->tp->body.singlefile.tfp.file_length; } else { len = csc->cs->sc->tp->body.multifile.total_length; } msg = xmalloc(BOOTSTRAP_LEN); memset(msg, '\0', BOOTSTRAP_LEN); l = snprintf(msg, BOOTSTRAP_LEN, "num_peers:%u\r\nnum_pieces:%u\r\ntorrent_size:%jd\r\ntorrent_bytes:%jd\r\n", csc->cs->sc->num_peers, csc->cs->sc->tp->num_pieces, (intmax_t)len, (intmax_t)csc->cs->started); if (l == -1 || l >= (int)BOOTSTRAP_LEN) errx(1, "ctl_server_conn_bootstrap() string truncation"); ctl_server_write_message(csc, msg); xfree(msg); msg = ctl_server_pieces(csc->cs->sc); ctl_server_write_message(csc, msg); xfree(msg); msg = ctl_server_peers(csc->cs->sc); ctl_server_write_message(csc, msg); xfree(msg); trace("bootstrapped"); } /* * ctl_server_handle_conn_message() * * Handle a message from a connection to the control server. */ static void ctl_server_handle_conn_message(struct bufferevent *bufev, void *data) { /* TODO */ } /* * ctl_server_handle_conn_error() * * Handle a message from a connection to the control server. */ static void ctl_server_handle_conn_error(struct bufferevent *bufev, short error, void *data) { struct ctl_server_conn *csc; csc = data; trace("ctl_server_handle_conn_error() freeing connection"); TAILQ_REMOVE(&csc->cs->conns, csc, conn_list); ctl_server_conn_free(csc); } /* * ctl_server_write_message() * * Write a message to a connection. */ static void ctl_server_write_message(struct ctl_server_conn *csc, char *msg) { if (bufferevent_write(csc->bev, msg, strlen(msg)) != 0) errx(1, "ctl_server_write_message() failure"); } /* * ctl_server_broadcast_message() * * Broadcast a message to all connections. */ static void ctl_server_broadcast_message(struct ctl_server *cs, char *msg) { struct ctl_server_conn *csc; TAILQ_FOREACH(csc, &cs->conns, conn_list) ctl_server_write_message(csc, msg); } /* * ctl_server_pieces() * * Allocate and return string containing pieces message. */ static char * ctl_server_pieces(struct session *sc) { struct torrent_piece *tpp; u_int32_t count, i, msglen; char *msg, piece[8]; /* almost certainly too much space, but who cares */ msglen = CTL_MESSAGE_LEN + (5 * sc->tp->good_pieces); msg = xmalloc(msglen); memset(msg, '\0', msglen); snprintf(msg, msglen, "pieces:"); count = 0; for (i = 0; i < sc->tp->num_pieces; i++) { tpp = torrent_piece_find(sc->tp, i); if (tpp->flags & TORRENT_PIECE_CKSUMOK) { count++; if (count == sc->tp->good_pieces) { snprintf(piece, sizeof(piece), "%u\r\n", i); } else { snprintf(piece, sizeof(piece), "%u,", i); } if (strlcat(msg, piece, msglen) >= msglen) errx(1, "ctl_server_pieces() string truncation"); } } return (msg); } /* * ctl_server_peers() * * Allocate and return string containing peers message. */ static char * ctl_server_peers(struct session *sc) { struct peer *p; u_int32_t count, msglen; char *msg, peer[32]; /* almost certainly too much space, but who cares */ msglen = CTL_MESSAGE_LEN + (32 * sc->num_peers); msg = xmalloc(msglen); memset(msg, '\0', msglen); snprintf(msg, msglen, "peers:"); count = 0; TAILQ_FOREACH(p, &sc->peers, peer_list) { count++; if (count == sc->num_peers) { snprintf(peer, sizeof(peer), "%s:%d\r\n", inet_ntoa(p->sa.sin_addr), ntohs(p->sa.sin_port)); } else { snprintf(peer, sizeof(peer), "%s:%d,", inet_ntoa(p->sa.sin_addr), ntohs(p->sa.sin_port)); } if (strlcat(msg, peer, msglen) >= msglen) errx(1, "ctl_server_peers() string truncation"); } return (msg); } unworkable-0.53/torrent.c0000644000014500017510000006313411246661315014754 0ustar michaelstaff/* $Id: torrent.c,v 1.108 2008-10-06 18:47:11 niallo Exp $ */ /* * Copyright (c) 2006, 2007 Niall O'Higgins * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined(__linux__) || defined(__CYGWIN__) || defined(__GLIBC__) #include #endif /* solaris 10 */ #if defined(__SVR4) && defined(__sun) #include "/usr/ucbinclude/sys/file.h" #endif #include "includes.h" /* * torrent_parse_infohash() * * Compute torrent file infohash, returning a 20 byte array. */ u_int8_t * torrent_parse_infohash(const char *file, size_t infoend) { SHA1_CTX sha; u_int8_t result[SHA1_DIGEST_LENGTH], *ret; char *p, *buf; BUF *b; size_t len; if (infoend == 0) errx(1, "torrent_parse_infohash: infoend is zero - error parsing torrent file"); if ((b = buf_load(file, BUF_AUTOEXT)) == NULL) exit(1); len = buf_len(b); buf = buf_release(b); p = buf; #define INFO_STR "4:info" p = strstr(buf, INFO_STR); if (p == NULL) errx(1, "torrent_parse_infohash: no info key found"); p += strlen(INFO_STR); SHA1Init(&sha); SHA1Update(&sha, p, (infoend - (p - buf))); SHA1Final(result, &sha); len = SHA1_DIGEST_LENGTH; ret = xmalloc(len); memcpy(ret, result, SHA1_DIGEST_LENGTH); xfree(buf); return (ret); } /* * torrent_parse_file() * * Parse a .torrent data file, returning a discrete torrent structure. */ struct torrent * torrent_parse_file(const char *file) { struct torrent_file *multi_file; struct torrent *torrent; struct benc_node *troot, *node, *lnode, *tnode; struct benc_node *filenode, *childnode; BUF *buf; int l; size_t ret; torrent = xmalloc(sizeof(*torrent)); memset(torrent, 0, sizeof(*torrent)); torrent->name = xstrdup(file); /* XXX need a way to free torrents and their node trees */ torrent->broot = benc_root_create(); if ((buf = buf_load(file, 0)) == NULL) err(1, "torrent_parse_file: buf_load"); if ((troot = benc_parse_buf(buf, torrent->broot)) == NULL) errx(1, "torrent_parse_file: yyparse of %s", file); buf_free(in); if ((node = benc_node_find(troot, "info")) == NULL) errx(1, "no info data found in torrent"); torrent->info_hash = torrent_parse_infohash(file, node->end); if ((node = benc_node_find(troot, "announce")) == NULL) errx(1, "no announce data found in torrent"); if (node->flags & BSTRING) torrent->announce = node->body.string.value; else errx(1, "announce value is not a string"); if ((node = benc_node_find(troot, "comment")) != NULL && node->flags & BSTRING) torrent->comment = node->body.string.value; if ((filenode = benc_node_find(troot, "files")) == NULL) { torrent->type = SINGLEFILE; if ((node = benc_node_find(troot, "length")) == NULL) errx(1, "no length field"); if (!(node->flags & BINT)) errx(1, "length is not a number"); torrent->body.singlefile.tfp.file_length = node->body.number; torrent->left = torrent->body.singlefile.tfp.file_length; if ((node = benc_node_find(troot, "name")) == NULL) errx(1, "no name field"); if (!(node->flags & BSTRING)) errx(1, "name is not a string"); torrent->body.singlefile.tfp.path = node->body.string.value; if ((node = benc_node_find(troot, "piece length")) == NULL) errx(1, "no piece length field"); if (!(node->flags & BINT)) errx(1, "piece length is not a number"); torrent->piece_length = node->body.number; if ((node = benc_node_find(troot, "pieces")) == NULL) errx(1, "no pieces field"); if (!(node->flags & BSTRING)) errx(1, "pieces is not a string"); torrent->body.singlefile.pieces = node->body.string.value; torrent->num_pieces = node->body.string.len / SHA1_DIGEST_LENGTH; if ((node = benc_node_find(troot, "md5sum")) != NULL) { if (!(node->flags & BSTRING)) errx(1, "md5sum is not a string"); else torrent->body.singlefile.tfp.md5sum = node->body.string.value; } } else { torrent->type = MULTIFILE; if ((node = benc_node_find(troot, "name")) == NULL) errx(1, "no name field"); if (!(node->flags & BSTRING)) errx(1, "name is not a string"); torrent->body.multifile.name = node->body.string.value; if ((node = benc_node_find(troot, "piece length")) == NULL) errx(1, "no piece length field"); if (!(node->flags & BINT)) errx(1, "piece length is not a number"); torrent->piece_length = node->body.number; if ((node = benc_node_find(troot, "pieces")) == NULL) errx(1, "no pieces field"); if (!(node->flags & BSTRING)) errx(1, "pieces is not a string"); torrent->body.multifile.pieces = node->body.string.value; torrent->body.singlefile.pieces = node->body.string.value; torrent->num_pieces = node->body.string.len / SHA1_DIGEST_LENGTH; TAILQ_INIT(&torrent->body.multifile.files); /* iterate through sub-dictionaries */ TAILQ_FOREACH(childnode, &filenode->children, benc_nodes) { multi_file = xmalloc(sizeof(*multi_file)); memset(multi_file, 0, sizeof(*multi_file)); if ((tnode = benc_node_find(childnode, "length")) == NULL) errx(1, "no length field"); if (!(tnode->flags & BINT)) errx(1, "length is not a number"); multi_file->file_length = tnode->body.number; torrent->body.multifile.total_length += tnode->body.number; torrent->left = torrent->body.multifile.total_length; if ((tnode = benc_node_find(childnode, "md5sum")) != NULL && tnode->flags & BSTRING) multi_file->md5sum = tnode->body.string.value; if ((tnode = benc_node_find(childnode, "path")) == NULL) errx(1, "no path field"); if (!(tnode->flags & BLIST)) errx(1, "path is not a list"); multi_file->path = xmalloc(MAXPATHLEN); memset(multi_file->path, '\0', MAXPATHLEN); TAILQ_FOREACH(lnode, &tnode->children, benc_nodes) { if (!(lnode->flags & BSTRING)) errx(1, "path element is not a string"); if (*multi_file->path == '\0') { ret = strlcpy(multi_file->path, lnode->body.string.value, MAXPATHLEN); if (ret >= MAXPATHLEN) errx(1, "path too large"); } else { l = snprintf(multi_file->path, MAXPATHLEN, "%s/%s", multi_file->path, lnode->body.string.value); if (l == -1 || l >= MAXPATHLEN) errx(1, "path too large"); } } TAILQ_INSERT_TAIL(&torrent->body.multifile.files, multi_file, files); } } if ((node = benc_node_find(troot, "created by")) != NULL && node->flags & BSTRING) torrent->created_by = node->body.string.value; if ((node = benc_node_find(troot, "creation date")) != NULL && node->flags & BINT) torrent->creation_date = node->body.number; return (torrent); } /* * torrent_print() * * Util function to print various details about the parsed torrent. */ void torrent_print(struct torrent *torrent) { struct torrent_file *tfile; int i; printf("announce url:\t%s\n", torrent->announce); printf("created by:\t"); if (torrent->created_by == NULL) printf("NONE\n"); else printf("%s\n", torrent->created_by); printf("creation date:\t"); if (torrent->creation_date == 0) printf("NONE\n"); else printf("%s", ctime(&torrent->creation_date)); printf("comment:\t"); if (torrent->comment == NULL) printf("NONE\n"); else printf("%s\n", torrent->comment); printf("info hash:\t0x"); for (i = 0; i < SHA1_DIGEST_LENGTH; i++) printf("%02x", torrent->info_hash[i]); putchar('\n'); printf("pieces:\t\t%u\n", torrent->num_pieces); printf("type:\t\t"); if (torrent->type == SINGLEFILE) { tfile = &torrent->body.singlefile.tfp; printf("single file\n"); printf("length:\t\t%jd bytes\n", (intmax_t)tfile->file_length); printf("file name:\t%s\n", tfile->path); printf("piece length:\t%u bytes\n", torrent->piece_length); printf("md5sum:\t\t"); if (tfile->md5sum == NULL) printf("NONE\n"); else printf("%s\n", tfile->md5sum); } else { printf("multi file\n"); printf("base path:\t%s\n", torrent->body.multifile.name); printf("piece length:\t%u bytes\n", torrent->piece_length); printf("--files--\n"); TAILQ_FOREACH(tfile, &torrent->body.multifile.files, files) { printf("file name:\t%s\n", tfile->path); printf("length:\t\t%jd bytes\n", (intmax_t)tfile->file_length); printf("md5sum:\t\t"); if (tfile->md5sum == NULL) printf("NONE\n"); else printf("%s\n", tfile->md5sum); } } } /* * torrent_block_write() * * Write arbitrary binary data to given piece at supplied offset. */ void torrent_block_write(struct torrent_piece *tpp, off_t off, u_int32_t len, void *d) { struct torrent_mmap *tmmp; off_t cntlen = 0, cntbase = 0; u_int8_t *aptr; u_int32_t tlen, bytesleft = len, diff = 0; trace("torrent_block_write tpp->idx: %u off: %u len: %u", tpp->index, off, len); TAILQ_FOREACH(tmmp, &tpp->mmaps, mmaps) { if (bytesleft < len) { diff = len - bytesleft; /* write as much as we can here and jump to next * mapping if required*/ if (bytesleft > tmmp->len) { memcpy(tmmp->addr, (u_int8_t *)d + diff, tmmp->len); bytesleft -= tmmp->len; continue; } /* done once we make it here */ memcpy(tmmp->addr, (u_int8_t *)d + diff, bytesleft); return; } cntlen += tmmp->len; if (cntlen < off) { cntbase += tmmp->len; continue; } else { aptr = tmmp->addr; for (; cntbase < off; cntbase++) aptr++; tlen = tmmp->len - (aptr - (u_int8_t *)tmmp->addr); /* its possible that we are writing more bytes than * remain in this single mapping. in this case, * we need to write the remainder to the next * mapping(s)*/ if (len > tlen) { memcpy(aptr, d, tlen); bytesleft -= tlen; continue; } memcpy(aptr, d, len); return; } } } /* * torrent_block_read() * * Read a given block of a piece. This handles pieces which overlap * multiple files. Data copies may be necessary under certain circumstances. * For this reason, caller should check the value of "hint". If it is 1, * caller must free the data returned when done with it. */ void * torrent_block_read(struct torrent_piece *tpp, off_t off, u_int32_t len, int *hint) { void *block; u_int8_t *aptr, *bptr; struct torrent_mmap *tmmp; off_t cntlen = 0, cntbase = 0; u_int32_t tlen = len, ilen; *hint = 0; block = NULL; bptr = NULL; trace("torrent_block_read tpp->idx: %u off: %u len: %u", tpp->index, off, len); TAILQ_FOREACH(tmmp, &tpp->mmaps, mmaps) { /* sum the lengths of the mappings we visit. if the offset is greater than the current sum, then the requested data is not within this mapping so continue to next mapping */ cntlen += tmmp->len; if (cntlen < off) { /* we need to maintain a base length so that we know how far we need to move the pointer to reach the offset */ cntbase += tmmp->len; continue; } else { /* our offset is within this mapping, but we still might need to bring the pointer up to it */ aptr = tmmp->addr; /* (off - cntbase) is the distance from the start of * mapping that we need to traverse to get to the * offset. */ /* ilen is the length remaining in the mapping, after * the walk */ ilen = tmmp->len - (off - cntbase); if (ilen > tmmp->len) errx(1, "overflow: ilen > tmmp->len"); for(; cntbase < off; cntbase++) aptr++; trace("torrent_block_read: off: %jd len: %u ilen: %u (off - cntbase): %jd tmmp->len: %u tlen: %u file: %s md5sum: %s", off, len, ilen, off - cntbase, tmmp->len, tlen, tmmp->tfp->path, tmmp->tfp->md5sum); /* this mapping might not contain as many bytes as we requested. in that case, copy as many as possible and continue to next mapping */ if (ilen < tlen) { /* make sure we only malloc once */ if (*hint == 0) { block = xmalloc(len); bptr = block; *hint = 1; } memcpy(bptr, aptr, ilen); bptr += ilen; tlen -= ilen; } else { /* if possible, do not do a buffer copy, * but return the mmaped base address directly */ if (*hint == 0) { return (aptr); } memcpy(bptr, aptr, tlen); return (block); } } } if (*hint == 1) { return (block); } return (NULL); } /* * torrent_piece_find() * * Look up the torrent piece at a given index. */ struct torrent_piece * torrent_piece_find(struct torrent *tp, u_int32_t idx) { struct torrent_piece *tpp; if (idx > tp->num_pieces - 1) errx(1, "torrent_piece_find: index %u out of bounds", idx); tpp = tp->piece_array + idx; return (tpp); } /* * torrent_mmap_create() * * Create the mmap corresponding to a given offset and length of a supplied * torrent file. Also handles zero'ing the file if necessary. */ struct torrent_mmap * torrent_mmap_create(struct torrent *tp, struct torrent_file *tfp, off_t off, u_int32_t len) { struct torrent_mmap *tmmp; struct stat sb; char buf[MAXPATHLEN], *buf2, zero = 0x00, *basedir; int openflags, mmapflags, fd = 0, l; long pagesize; u_int8_t *nearest_page = NULL; off_t page_off = 0; if ((pagesize = sysconf(_SC_PAGESIZE)) == -1) err(1, "torrent_mmap_create: sysconf"); open: if (tfp->fd == 0) { if (tp->type == SINGLEFILE) l = snprintf(buf, sizeof(buf), "%s", tfp->path); else { l = snprintf(buf, sizeof(buf), "%s/%s", tp->body.multifile.name, tfp->path); if (l == -1 || l >= (int)sizeof(buf)) errx(1, "torrent_mmap_create: path too long"); /* Linux dirname() modifies the buffer, so make a copy */ buf2 = xstrdup(buf); if ((basedir = dirname(buf2)) == NULL) err(1, "torrent_mmap_create: basename"); if (mkpath(basedir, 0755) == -1) if (errno != EEXIST) err(1, "torrent_mmap_create \"%s\": mkdir", basedir); xfree(buf2); basedir = NULL; } openflags = (tp->good_pieces == tp->num_pieces ? O_RDONLY : O_RDWR|O_CREAT); if ((fd = open(buf, openflags, 0600)) == -1) err(1, "torrent_mmap_create: open `%s'", buf); if (flock(fd, LOCK_EX | LOCK_NB) == -1) err(1, "torrent_mmap_create: flock()"); tfp->fd = fd; } if (fstat(tfp->fd, &sb) == -1) err(1, "torrent_mmap_create: fstat `%d'", tfp->fd); /* trace("size: %u vs. len+off: %u", sb.st_size, (off_t)len + off); */ if (sb.st_size < ((off_t)len + off)) { /* trace("%llu offset zeroed, len %u, size: %u", off, len, sb.st_size); */ tp->isnew = 1; /* seek to the expected size of file ... */ if (lseek(tfp->fd, (off_t)len + off - 1, SEEK_SET) == -1) err(1, "torrent_mmap_create: lseek() failure"); /* and write a byte there */ if (write(tfp->fd, &zero, 1) < 1) err(1, "torrent_mmap_create: write() failure"); if (flock(tfp->fd, LOCK_UN) == -1) err(1, "torrent_mmap_create: flock()"); close(tfp->fd); tfp->fd = 0; goto open; } /* OpenBSD does not require us to align our mmap to page-size boundaries, * but Linux and no doubt other platforms do. */ tmmp = xmalloc(sizeof(*tmmp)); memset(tmmp, 0, sizeof(*tmmp)); tmmp->len = len; if (off > 0) { page_off = ((off / pagesize) * pagesize); len += off - page_off; } /* trace("mmap: len: %u off: %u sbsiz: %u fd: %d aligned off: %u pagesize: %d", len, off, sb.st_size, tfp->fd, page_off, pagesize); */ tmmp->tfp = tfp; mmapflags = (tp->good_pieces == tp->num_pieces ? PROT_READ : PROT_READ|PROT_WRITE); tmmp->aligned_addr = mmap(0, len, mmapflags, MAP_SHARED, tfp->fd, page_off); if (tmmp->aligned_addr == MAP_FAILED) err(1, "torrent_mmap_create: mmap"); /* cygwin doesn't provide madvise() */ #if !defined(__CYGWIN__) if (madvise(tmmp->aligned_addr, len, MADV_SEQUENTIAL|MADV_WILLNEED) == -1) err(1, "torrent_mmap_create: madvise"); #endif nearest_page = tmmp->aligned_addr + (off - page_off); tmmp->addr = nearest_page; tfp->refs++; return (tmmp); } /* * torrent_pieces_create() * * Initialize the flat array of piece descriptors, one for each * piece in the torrent. This facilitates extremely fast * random access for piece metadata. */ struct torrent_piece * torrent_pieces_create(struct torrent *tp) { struct torrent_piece *tpp; u_int32_t len, i; off_t off; tpp = xcalloc(tp->num_pieces, sizeof(*tpp)); for (i = 0; i < tp->num_pieces; i++) { tpp[i].tp = tp; tpp[i].index = i; TAILQ_INIT(&tpp[i].mmaps); off = tp->piece_length * (off_t)i; /* nice and simple */ if (tp->type == SINGLEFILE) { /* last piece is irregular */ if (i == tp->num_pieces - 1) { len = tp->body.singlefile.tfp.file_length - off; } else { len = tp->piece_length; } tpp[i].len = len; } else { /* last piece is irregular */ if (i == tp->num_pieces - 1) { len = tp->body.multifile.total_length - ((tp->num_pieces - 1) * tp->piece_length); } else { len = tp->piece_length; } tpp[i].len = len; } } tp->piece_array = tpp; return (tpp); } /* * torrent_piece_map() * * Creates the mmap region(s) corresponding to this piece. For multi-file * torrents, this can be quite complicated. * * Returns 1 on success, 0 on failure. */ int torrent_piece_map(struct torrent_piece *tpp) { struct torrent_file *tfp, *nxttfp; struct torrent_mmap *tmmp; u_int32_t len; off_t off; off = tpp->tp->piece_length * (off_t)tpp->index; /* nice and simple */ if (tpp->tp->type == SINGLEFILE) { tmmp = torrent_mmap_create(tpp->tp, &tpp->tp->body.singlefile.tfp, off, tpp->len); TAILQ_INSERT_TAIL(&tpp->mmaps, tmmp, mmaps); tpp->flags |= TORRENT_PIECE_MAPPED; return (0); } else { /* * From: http://wiki.theory.org/BitTorrentSpecification * * "For the purposes of piece boundaries in the multi-file case, * consider the file data as one long continuous stream, * composed of the concatenation of each file in the order * listed in the files list. The number of pieces and their * boundaries are then determined in the same manner as the * case of a single file. Pieces may overlap file boundaries." * * So, find which file(s) this piece should be mapped within. * Or, if files are too small to make up a whole piece, which * files should be mapped within the piece. * This is kind of complicated. */ len = tpp->len; tpp->len = 0; TAILQ_FOREACH(tfp, &tpp->tp->body.multifile.files, files) { /* piece offset puts it outside the current file, may be mapped to next file */ if (off > tfp->file_length) { off -= tfp->file_length; continue; } /* file is too small for one piece and this piece is not yet full */ if (tfp->file_length < (off_t)len && tpp->len < len) { if (tfp->file_length - off == 0) continue; tmmp = torrent_mmap_create(tpp->tp, tfp, off, tfp->file_length - off); TAILQ_INSERT_TAIL(&tpp->mmaps, tmmp, mmaps); tpp->len += tfp->file_length - off; len -= tfp->file_length - off; off = 0; continue; } if (off + (off_t)len > tfp->file_length) { if (tfp->file_length == off) { off = 0; continue; } if (tfp->file_length - off == 0) continue; tmmp = torrent_mmap_create(tpp->tp, tfp, off, tfp->file_length - off); tpp->len += tmmp->len; TAILQ_INSERT_TAIL(&tpp->mmaps, tmmp, mmaps); nxttfp = TAILQ_NEXT(tfp, files); len -= tmmp->len; off++; if (nxttfp->file_length < (off_t)len) { if (nxttfp->file_length == 0) continue; tmmp = torrent_mmap_create(tpp->tp, nxttfp, 0, nxttfp->file_length); tpp->len += tmmp->len; TAILQ_INSERT_TAIL(&tpp->mmaps, tmmp, mmaps); off++; len -= tmmp->len; tfp = nxttfp; off = 0; continue; } else { if (len == 0) continue; tmmp = torrent_mmap_create(tpp->tp, nxttfp, 0, len); } tpp->len += tmmp->len; TAILQ_INSERT_TAIL(&tpp->mmaps, tmmp, mmaps); off++; break; } else if (off < tfp->file_length) { /* piece lies within this file */ if (len == 0) continue; tmmp = torrent_mmap_create(tpp->tp, tfp, off, len); off++; TAILQ_INSERT_TAIL(&tpp->mmaps, tmmp, mmaps); tpp->len += len; break; } } tpp->flags |= TORRENT_PIECE_MAPPED; return (0); } return (1); } /* * torrent_piece_checkhash() * * Checksum the supplied piece. Set the piece's checksum bit to true * if it is good, and also return 1. Returns 0 if the hash check fails. */ int torrent_piece_checkhash(struct torrent *tp, struct torrent_piece *tpp) { SHA1_CTX sha; u_int8_t *d, *s, results[SHA1_DIGEST_LENGTH]; int hint, res; if (!(tpp->flags & TORRENT_PIECE_MAPPED)) errx(1, "torrent_piece_checkhash: unmapped piece: %u", tpp->index); d = torrent_block_read(tpp, 0, tpp->len, &hint); if (d == NULL) return (-1); SHA1Init(&sha); SHA1Update(&sha, d, tpp->len); SHA1Final(results, &sha); if (tp->type == MULTIFILE) { s = tp->body.multifile.pieces + (SHA1_DIGEST_LENGTH * tpp->index); } else { s = tp->body.singlefile.pieces + (SHA1_DIGEST_LENGTH * tpp->index); } res = memcmp(results, s, SHA1_DIGEST_LENGTH); if (res == 0) { tpp->flags |= TORRENT_PIECE_CKSUMOK; } if (hint == 1) xfree(d); return (res); } /* * torrent_piece_sync() * * Sync given piece to disk. */ void torrent_piece_sync(struct torrent *tp, u_int32_t idx) { struct torrent_piece *tpp; struct torrent_mmap *tmmp; tpp = torrent_piece_find(tp, idx); if (tpp == NULL) errx(1, "torrent_piece_sync: NULL piece"); TAILQ_FOREACH(tmmp, &tpp->mmaps, mmaps) if (msync(tmmp->aligned_addr, tmmp->len, MS_SYNC) == -1) err(1, "torrent_piece_sync: msync"); } /* * torrent_piece_unmap() * * Unmap the supplied piece, flushing it to disk and * closing the file descriptor if necessary. */ void torrent_piece_unmap(struct torrent_piece *tpp) { struct torrent_mmap *tmmp; TAILQ_FOREACH(tmmp, &tpp->mmaps, mmaps) { tmmp->tfp->refs--; if (tmmp->tfp->refs == 0) { flock(tmmp->tfp->fd, LOCK_UN); (void) close(tmmp->tfp->fd); tmmp->tfp->fd = 0; } if (msync(tmmp->aligned_addr, tmmp->len, MS_SYNC) == -1) err(1, "torrent_piece_unmap: msync"); if (munmap(tmmp->aligned_addr, tmmp->len) == -1) err(1, "torrent_piece_unmap: munmap"); } while ((tmmp = TAILQ_FIRST(&tpp->mmaps))) { TAILQ_REMOVE(&tpp->mmaps, tmmp, mmaps); xfree(tmmp); } tpp->flags &= ~TORRENT_PIECE_MAPPED; } /* * torrent_bitfield_get() * * Allocate and return a byte-array corresponding to our bitmap representation * of the data we have in the torrent. Caller is responsible for freeing * the memory, and must figure out itself how long the buffer is. */ u_int8_t * torrent_bitfield_get(struct torrent *tp) { struct torrent_piece *tpp; u_int32_t i, len; u_int8_t *bitfield; len = (tp->num_pieces + 7u) / 8u; bitfield = xmalloc(len); memset(bitfield, 0, len); for (i = 0; i < tp->num_pieces; i++) { tpp = torrent_piece_find(tp, i); if (tpp->flags & TORRENT_PIECE_CKSUMOK) util_setbit(bitfield, i); } return (bitfield); } /* * torrent_empty() * * Returns 1 if the torrent is empty, that is, contains no checksummed * pieces. Returns 0 if there is at least 1 good piece. */ int torrent_empty(struct torrent *tp) { struct torrent_piece *tpp; u_int32_t i; for (i = 0; i < tp->num_pieces; i++) { tpp = torrent_piece_find(tp, i); if (tpp->flags & TORRENT_PIECE_CKSUMOK) return (0); } return (1); } /* * torrent_fastresume_dump() * * Write out the bitfield and some other metadata to a file in the same * dir as the torrent file, named .funresume. */ void torrent_fastresume_dump(struct torrent *tp) { char resumename[MAXPATHLEN]; FILE *fp; u_int32_t bitfieldlen; u_int8_t *bitfield; size_t bytes; int l; bitfieldlen = (tp->num_pieces + 7u) / 8u; bitfield = torrent_bitfield_get(tp); l = snprintf(resumename, sizeof(resumename), "%s.funresume", tp->name); if (l == -1 || l >= (int)sizeof(resumename)) errx(1, "torrent_fastresume_dump: snprintf truncation"); if ((fp = fopen(resumename, "w")) == NULL) err(1, "torrent_fastresume_dump: fopen"); bytes = fwrite(bitfield, bitfieldlen, 1, fp); if (bytes != bitfieldlen && ferror(fp)) errx(1, "torrent_fastresume_dump: fwrite failure"); fclose(fp); xfree(bitfield); } /* * torrent_fastresume_load() * * Load the fast resume data produced by torrent_fastresume_dump(). * Returns -1 if it could not load the fast resume data, 0 otherwise. */ int torrent_fastresume_load(struct torrent *tp) { struct torrent_piece *tpp; char resumename[MAXPATHLEN]; FILE *fp; u_int32_t i, bitfieldlen; u_int8_t *bitfield; size_t bytes; int l; bitfieldlen = (tp->num_pieces + 7u) / 8u; l = snprintf(resumename, sizeof(resumename), "%s.funresume", tp->name); if (l == -1 || l >= (int)sizeof(resumename)) errx(1, "torrent_fastresume_load: snprintf truncation"); if ((fp = fopen(resumename, "r")) == NULL) return (-1); bitfield = xmalloc(bitfieldlen); memset(bitfield, 0, bitfieldlen); bytes = fread(bitfield, bitfieldlen, 1, fp); if (bytes != bitfieldlen && ferror(fp)) errx(1, "torrent_fastresume_load: fread failure"); fclose(fp); /* there are faster ways to do this, but this is pretty readable */ for (i = 0; i < tp->num_pieces; i++) { tpp = torrent_piece_find(tp, i); if (util_getbit(bitfield, i) == 1) { tpp->flags |= TORRENT_PIECE_CKSUMOK; tp->good_pieces++; tp->downloaded += tpp->len; } } xfree(bitfield); return (0); } unworkable-0.53/TODO0000644000014500017510000000075711071325315013576 0ustar michaelstaff- Better progress guage handling of the situation where we have received more bytes than are in the torrent, but hash check failures mean we aren't actually as far along as we wish. In other words, raw data rx vs. checksummed pieces. - UDP tracker support (BEP 15). - Multi-tracker support. - Multiple torrents per unworkable process support, along with hooks to load new torrents etc in the control server. - Encryption. - DHT/Kadamalia overlay network support. - Rate limiting. unworkable-0.53/unworkable.py0000644000014500017510000004647511000573466015644 0ustar michaelstaff# Copyright (c) 2008 Harry Tormey # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #------------------------------------------------------------------------ # Demo allows you to add torrents to a custom wxpython grid. For every # torrent selected a listener thread and a simulator thread are initialized # and grid is updated. The simulator threads (class torrent) are for debug/testing # purposes only, they simulate the messages that would be sent by an instance of # unworkable to the gui. Included with this file is code to run an instance of unworkable.exe # as a seperate thread. This is currently disabled. #------------------------------------------------------------------------ # April 13 # +unworkable.exe to listener communication inconsistant and barely usable. # March 31 TODO (ordered by priority): # +Delete torrent button: # *Add a check box to grid. # *clicking delete, removes all running torrents from grid # *Need a way to terminate instances of unworkable, stop listener threads # +Log # *Debug information (what function called when, exceptions, etc) to logfile # +Status bar # *figure out how to draw and update coloured rectangles in grid # +Unit tests # *Read up on writing Unit tests for gui/network apps. # *integrate emulator.py into unworkable.pyu code # # import wx.grid as gridlib import wx import wx.lib.newevent import os import subprocess import getopt import socket import thread import threading import Queue import time import random #--------------------------------------------------------------------------- # This is how you pre-establish a file filter so that the dialog # only shows the extension(s) you want it to. wildcard = "Torrent files (*.torrent)|*.torrent|" \ "All files (*.*)|*.*" #--------------------------------------------------------------------------- # Same as above but just show exe's (windows, fix this for linux/osx) exeWildcard = "exe files (*.exe)|*.exe|" \ "All files (*.*)|*.*" #---------------------------------------------------------------------- # This creates a new Event class and a EVT binder function # Usage: An unworkable listener thread sends out an EVT_UPDATE_GRID # message every time new information is received from the unworkable # client processing a given torrent. (UpdateGridEvent, EVT_UPDATE_GRID) = wx.lib.newevent.NewEvent() #This emulates an instance of unworkable, it waits for connections on a given port class torrent(threading.Thread): '''Send message to conn.py simulating unworkable''' def __init__(self, host, port): self.host = host self.port = port self.num_peers = 0 self.num_pieces = 0 self.torrent_size = 0 self.torrent_bytes = 0 self.pieces = [] self.peers = [] self.bytes = 0 self._done = False self.quit = False self._f = None self.counter = 0 self.running = False #setup socket to listen for tcp/ip on host/port self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self._socket.bind((host, port)) self._socket.listen(5) self.keepGoing = False threading.Thread.__init__(self) def Start(self): self.keepGoing = self.running = True thread.start_new_thread(self.run, ()) def IsRunning(self): return self.running def Stop(self): #self._f.close() self._done = True self._socket.close() self.keepGoing = False self.quit = True def run(self): try: # program will wait for connections # to terminate, hit ctrl c/ctrl break print "waiting for conntections \n" newSocket, address = self._socket.accept( ) print "Connected from", address self._f = newSocket.makefile() numbytes = 0 while self.keepGoing: #while numbytes < 100: message = 'num_peers:%d\r\nnum_pieces:%d\r\ntorrent_size:%d\ntorrent_bytes:%d\r\n' % (10,0,2,numbytes) self._f.write(message) self._f.flush() print message time.sleep(1) numbytes += 1 self._f.close() self._socket.close() self.running = False except socket.error, e: print e #print and ignore all socket errors pass except KeyboardInterrupt, e: #print and stop on keyboard interrupt print e self.stop() #This creates an instance of unworkable.exe class unworkable(): ''''Run an instance of unworkable.exe as a sepetate thread''' def __init__(self, host, port, torrent, path, log, handler=None): #Adress of host (currently not used) self.host = host #Tell unworkable.exe to speak to unworkablelistener class on this port self.port = port #Name of the torrent we are downloading self.torrent = torrent #Path to unworkable.exe self.path = path #Trace file where log output from unworkable.exe gets dumped. self.log = log #Variable which will eventually be used to kill the subprocess (currently not used) self.keepGoing = False #subprocess self.process = None #Set up function to be run in background thread self.t = threading.Thread(target=self.Run) #This setting might not do anything self.t.setDaemon(True) #The actual command that gets executed self.cmd = ('%s -t %s -g %i %s' % (self.path, self.log, self.port, self.torrent)) def Stop(self): #Change this to kill a process. self.keepGoing = False def Start(self): #start the thread self.keepGoing = True self.t.start() def Run(self): self.process = subprocess.Popen(self.cmd) #---------------------------------------------------------------------- # UnworkableListener listens for information from instances of unworkable.exe # and passes output back to mainwindow. #---------------------------------------------------------------------- class UnworkableListener: def __init__(self, win, barNum, val, host, port): self.win = win self.barNum = barNum self.val = val self.host = host self.port = port self.num_peers = 0 self.num_pieces = 0 self.torrent_size = 0 self.torrent_bytes = 0 self.pieces = [] self.peers = [] self.bytes = 0 self._socket = None self._f = None self.keepGoing = False self.running = False def Start(self): print "listener started \n" self.keepGoing = self.running = True thread.start_new_thread(self.Run, ()) def Stop(self): self.keepGoing = False #self._f.close() def IsRunning(self): return self.running def Run(self): try: self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self._socket.connect_ex((self.host, self.port)) self._f = self._socket.makefile() except socket.error, e: #ignore all socket errors pass try: #keep polling for information from uworkable.exe while self.keepGoing: print "listener still looping \n" for l in self._f: try: d = l.strip().split(':', 1) except: # ignore malformed line continue if d[0] == 'num_peers': if not isinstance(d[1], int): continue self.num_peers = int(d[1]) elif d[0] == 'num_pieces': self.num_pieces = int(d[1]) elif d[0] == 'torrent_size': self.torrent_size = int(d[1]) elif d[0] == 'torrent_bytes': self.torrent_bytes = int(d[1]) elif d[0] == 'pieces': try: new_pieces = d[1].split(',') new_pieces.sort() self.pieces = new_pieces except: # no pieces yet continue elif d[0] == 'bytes': self.bytes = int(d[1]) elif d[0] == 'peers': try: new_peers = d[1].split(',') new_peers.sort() self.peers = new_peers except: # no peers yet continue else: print "unkown message: %s" %(l) #Create an update grid event with all messages received from unworkable.exe evt = UpdateGridEvent(self.barNum) #self.num_peers) #self.num_pieces, #self.torrent_size, #self.torrent_bytes) #self.pieces, #self.bytes, #self.peers) #post event for mainwindow to process wx.PostEvent(self.win, evt) except socket.error, e: #ignore all socket errors sleep for 5 seconds and run again time.sleep(1) self.run() self.running = False #---------------------------------------------------------------------- # This is where all the information from unworkablelistener threads gets # stored/updated. #---------------------------------------------------------------------- class CustomDataTable(gridlib.PyGridTableBase): def __init__(self): gridlib.PyGridTableBase.__init__(self) self.numCols = 13 self.colLabels = ['#', 'Name', 'Size', 'Done', 'Seeds', 'Peers', 'Down Speed', 'Up Speed', 'ETA', 'Uploaded', 'Ratio','Port', 'Avail'] self.dataTypes = [gridlib.GRID_VALUE_NUMBER, gridlib.GRID_VALUE_STRING, gridlib.GRID_VALUE_NUMBER, gridlib.GRID_VALUE_NUMBER , gridlib.GRID_VALUE_NUMBER, gridlib.GRID_VALUE_NUMBER, gridlib.GRID_VALUE_NUMBER, gridlib.GRID_VALUE_NUMBER, gridlib.GRID_VALUE_NUMBER, gridlib.GRID_VALUE_FLOAT, gridlib.GRID_VALUE_NUMBER, gridlib.GRID_VALUE_NUMBER, gridlib.GRID_VALUE_NUMBER] #Blank entry self.data = [] for i in range(1,2): self.data.append([0, "", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) def GetNumberRows(self): return len(self.data) + 1 def GetNumberCols(self): return len(self.data[0]) def IsEmptyCell(self, row, col): try: return not self.data[row][col] except IndexError: return True #Get value from row/col def GetValue(self, row, col): try: return self.data[row][col] except IndexError: return '' #Update row from passed entry def UpdateEntry(self, row, entry): #for col in range(1, self.numCols): col = 0 for value in entry: self.SetValue(row, col, value) col = col + 1 #self.GetView().ProcessTableMessage(msg) #self.ForceRefresh() #Add another entry to the grid. def appendEntry(self, entry): self.data.append(entry) msg = gridlib.GridTableMessage(self, # The table gridlib.GRIDTABLE_NOTIFY_ROWS_APPENDED, # what we did to it 1 # how many ) self.GetView().ProcessTableMessage(msg) def SetValue(self, row, col, value): try: self.data[row][col] = value except IndexError, e: print e def GetColLabelValue(self, col): return self.colLabels[col] def GetTypeName(self, row, col): return self.dataTypes[col] def CanGetValueAs(self, row, col, typeName): colType = self.dataTypes[col].split(':')[0] if typeName == colType: return True else: return False def CanSetValueAs(self, row, col, typeName): return self.CanGetValueAs(row, col, typeName) #--------------------------------------------------------------------------- # This handles how custom data table is displayed/controlled in the frame # used by main window. #--------------------------------------------------------------------------- class CustTableGrid(gridlib.Grid): def __init__(self, parent): gridlib.Grid.__init__(self, parent, -1) table = CustomDataTable() self.table = table self.SetTable(table, True) self.SetRowLabelSize(0) self.SetMargins(0,0) self.AutoSizeColumns(False) # Maybe set this to true gridlib.EVT_GRID_CELL_LEFT_DCLICK(self, self.OnLeftDClick) def OnLeftDClick(self, evt): if self.CanEnableCellControl(): self.EnableCellEditControl() def appendEntry(self): self.table.appendEntry() #Main window/frame class mainwindow(wx.Frame): def __init__(self, parent): #Path to unworkable.exe self.unworkable = './unworkable.exe' #currently just holds option to specify port for unworkable self.unworkableOptionPort = '-g' #Set the inital value of process to none self.process = None #Counts the number of listener threads running. Displayed under # in gui self.NumThreads = 0 #map a port to each listener thread. Displayed under port in gui self.ThreadPort = 5000 #Initialize the frame wx.Frame.__init__(self, parent, -1, "unworkable", size=(590,490)) #setup the panel where all widgets will live self.p = wx.Panel(self, -1, style=0) #create instance of custom grid grid = CustTableGrid(self.p) self.grid = grid #Number of rows, cols in customdatatable self.numRow = self.grid.table.GetNumberRows() self.numCols = 13 #A simple sizer to control the layout of the widgets #Within the frame self.bs = wx.BoxSizer(wx.VERTICAL) self.bs.Add(grid, 1, wx.GROW|wx.ALL, 5) #Add Delete, path and open buttons + bind them to functions #+ add them to the sizer bs. self.createButtons(self.p) self.p.SetSizer(self.bs) #This event checks for a update event posted by a thread self.Bind(EVT_UPDATE_GRID, self.OnUpdate) #This event checks for a close screen event, #i.e to kill all active threads self.Bind(wx.EVT_CLOSE, self.OnCloseWindow) #Set up a list of unworkable torrent listener/unworkable.exe threads self.threads = [] #Setup unworkable.exe thread to feed data to listener with host,port,torrent,path, log handler (default=None) #self.threads.append(unworkable("localhost", self.ThreadPort, "fiore_thesis_final.pdf.torrent", self.unworkable, "5000.log")) #Setup listener #self.threads.append(UnworkableListener(self, self.NumThreads, self.NumThreads, "localhost", self.ThreadPort)) #Example of how to append data to grid #entry = [self.NumThreads, 'Testtorrent', "critical", 0, 'all', 0, 0, 0, self.ThreadPort, 0.0] #self.grid.table.SetValue(self.NumThreads, 0, self.NumThreads) #self.grid.table.SetValue( self.NumThreads, 1, "testtorrent") #self.grid.table.SetValue( self.NumThreads, 11, self.ThreadPort) #self.Refresh(False) #self.grid.table.appendEntry(entry) #self.NumThreads += 1 #self.ThreadPort += 1 #start all threads #for t in self.threads: # t.Start() #Add all buttons to GUI def buttonData(self): return (("Delete", self.DelButton), ("Path", self.PathButton), ("Open", self.OpenButton)) #Iterate through the entries returned by buttonData and populate the #screen def createButtons(self, panel): for eachLabel, eachHandler in self.buttonData(): button = wx.Button(self.p, -1, eachLabel) button.SetDefault() self.Bind(wx.EVT_BUTTON, eachHandler, button) self.bs.Add(button) #----End of process stuff #def buttonData(self): # return def AddButton(self, evt): print "ADD button selected" self.grid.table.TestUpdateGrid() def DelButton(self, evt): print "Delete button selected" def OnButtonFocus(self, evt): print "button focus" #When update message received post value and refresh the screen def OnUpdate(self, evt): #self.grid.table.SetValue(evt.barNum, 2, evt.torrent_size) #formula for how much is done #percent = ((float)*good_pieces / num_pieces) * 100; self.grid.table.SetValue(evt.barNum, 3, evt.num_pieces) print "Bar number" print evt.barNum print evt.num_pieces self.grid.table.SetValue(evt.barNum, 5, evt.num_peers) print evt.num_peers self.grid.table.SetValue(evt.barNum, 6, evt.torrent_bytes) print evt.torrent_bytes #Tell the gui to refresh screen self.Refresh(False) # 1)Open torrent button: # *Button should produce a dialog to select a torrent. # *Upon Selection torrent name should be added to dialog, # *a new instance of unworkable listener should be created # *a new instance of unworkable should be run with an incremented port number def OpenButton(self, evt): dlg = wx.FileDialog( self, message="Choose a file", defaultDir=os.getcwd(), defaultFile="", wildcard=wildcard, style=wx.OPEN | wx.MULTIPLE | wx.CHANGE_DIR ) if dlg.ShowModal() == wx.ID_OK: # This returns a Python list of files that were selected. paths = dlg.GetPaths() # Be carefull of GetPath, this just gets path to one file for path in paths: #Start listening on barnum? #Split the path into filename, directories folders, fileName = os.path.split(path) #Split the file extension from the filename (fileBaseName, fileExtension)=os.path.splitext(fileName) #Make sure that each entry in the custom table, corresponds with a thread entryNumber = len(self.threads) entry = [self.NumThreads, fileBaseName, 0, 0, 0, 0, 0, 0, 0, 0, 0, self.ThreadPort, 0.0] #Setup unworkable.exe thread to feed data to listener with host,port,torrent,path, #log handler (default=None) unworkableBinary = unworkable("localhost", self.ThreadPort, fileName, self.unworkable, "5000.log") #Setup listener listener = UnworkableListener(self, self.NumThreads, self.NumThreads, "localhost", self.ThreadPort) if(entryNumber < self.numRow): print entryNumber #Update entry corresponding to new thread added self.grid.table.UpdateEntry(entryNumber, entry) #place holder till I get the threads consolidated self.threads.append([entryNumber, unworkableBinary, listener]) #Tell the gui to refresh screen self.Refresh(False) else: self.grid.table.appendEntry(entry) self.threads.append([entryNumber, unworkableBinary, listener]) #unworkable.Start() #listener.Start() listener.Start() unworkableBinary.Start() #Start torrent simulator #self.threads.append(torrent("localhost", self.ThreadPort)) #self.threads[(len(self.threads) -1)].Start() #Start unworkablelistener thread #self.threads.append(UnworkableListener(self, self.NumThreads, self.NumThreads, "localhost", self.ThreadPort)) #self.threads[(len(self.threads) -1)].Start() #Increment counters #self.NumThreads += 1 self.ThreadPort += 1 dlg.Destroy() def PathButton(self, evt): dlg = wx.FileDialog( self, message="Set unworkable path", defaultDir=os.getcwd(), defaultFile="", wildcard=exeWildcard, style=wx.OPEN | wx.MULTIPLE | wx.CHANGE_DIR ) if dlg.ShowModal() == wx.ID_OK: # This returns the path (just one) of the .exe selected. self.unworkable = dlg.GetPath() # Be carefull of GetPath, this just gets path to one file dlg.Destroy() def OnCloseWindow(self, evt): #Display a busy message dialog while all threads are being killed off busy = wx.BusyInfo("One moment please, waiting for threads to die...") wx.Yield() #stop all active threads for t in self.threads: t.Stop() # keep looping till all threads stop running running = 1 while running: running = 0 for t in self.threads: running = running + t.IsRunning() time.sleep(0.1) #destroy window self.Destroy() #--------------------------------------------------------------------------- if __name__ == '__main__': import sys app = wx.PySimpleApp() frame = mainwindow(None) frame.Show(True) app.MainLoop() unworkable-0.53/main.c0000644000014500017510000001022011310320724014153 0ustar michaelstaff/* $Id: main.c,v 1.58 2008-09-27 20:35:43 niallo Exp $ */ /* * Copyright (c) 2006, 2007, 2008 Niall O'Higgins * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined(USE_BOEHM_GC) #include #endif #include "includes.h" #define DEFAULT_WINSIZE 80 #define MAX_WINSIZE 512 #define MESSAGE "hash check" #define METER "|/-\\" static void sighandler(int, short, void *); void usage(void); extern char *optarg; extern int optind; void usage(void) { fprintf(stderr, "usage: unworkable [-s] [-g port] [-p port] [-t tracefile] torrent\n"); exit(1); } void sighandler(int sig, short event, void *arg) { switch (sig) { case SIGTERM: case SIGINT: (void)event_loopexit(NULL); terminate_handler(); } } int main(int argc, char **argv) { struct rlimit rlp; struct torrent *torrent; struct torrent_piece *tpp; struct winsize winsize; struct event ev_sigint; struct event ev_sigterm; u_int32_t i; int ch, j, win_size, percent; char blurb[MAX_WINSIZE+1]; #if defined(USE_BOEHM_GC) GC_INIT(); #endif network_init(); signal_set(&ev_sigint, SIGINT, sighandler, NULL); signal_set(&ev_sigterm, SIGTERM, sighandler, NULL); signal_add(&ev_sigint, NULL); signal_add(&ev_sigterm, NULL); /* don't die on sigpipe */ signal(SIGPIPE, SIG_IGN); #if defined(__SVR4) && defined(__sun) __progname = argv[0]; #endif while ((ch = getopt(argc, argv, "sg:t:p:")) != -1) { switch (ch) { case 't': unworkable_trace = xstrdup(optarg); break; case 'g': gui_port = xstrdup(optarg); break; case 'p': user_port = xstrdup(optarg); break; case 's': seed = 1; break; default: usage(); } } argc -= optind; argv += optind; if (argc == 0) usage(); if (getrlimit(RLIMIT_NOFILE, &rlp) == -1) err(1, "getrlimit"); if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &winsize) != -1 && winsize.ws_col != 0) { if (winsize.ws_col > MAX_WINSIZE) win_size = MAX_WINSIZE; else win_size = winsize.ws_col; } else win_size = DEFAULT_WINSIZE; win_size += 1; /* trailing \0 */ torrent = torrent_parse_file(argv[0]); mytorrent = torrent; torrent_pieces_create(torrent); /* a little extra info? torrent_print(torrent); */ memset(&blurb, '\0', sizeof(blurb)); snprintf(blurb, sizeof(blurb), "%s ", MESSAGE); atomicio(vwrite, STDOUT_FILENO, blurb, win_size - 1); if (torrent_fastresume_load(torrent) == -1) { for (i = 0; i < torrent->num_pieces; i++) { tpp = torrent_piece_find(torrent, i); if (tpp->index != i) errx(1, "main: something went wrong, index is %u, should be %u", tpp->index, i); torrent_piece_map(tpp); if (!torrent->isnew) { j = torrent_piece_checkhash(torrent, tpp); if (j == 0) { torrent->good_pieces++; torrent->downloaded += tpp->len; } } torrent_piece_unmap(tpp); percent = (float)i / torrent->num_pieces * 100; snprintf(blurb, sizeof(blurb), "\r%s [%3d%%] %c", MESSAGE, percent, METER[i % 3]); atomicio(vwrite, STDOUT_FILENO, blurb, win_size - 1); } } /* do we already have everything? */ if (!seed && torrent->good_pieces == torrent->num_pieces) { printf("\rdownload already complete!\n"); exit(0); } srandom(time(NULL)); network_start_torrent(torrent, rlp.rlim_cur); exit(0); } unworkable-0.53/network.c0000644000014500017510000014247411072442422014746 0ustar michaelstaff/* $Id: network.c,v 1.225 2008-10-06 17:04:18 niallo Exp $ */ /* * Copyright (c) 2006, 2007, 2008 Niall O'Higgins * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include /* cygwin */ #if defined(NO_GETADDRINFO) #include "openbsd-compat/getaddrinfo.h" #endif #include #include #include #include #include #include #include #include #include #include #include #include "includes.h" char *user_port = NULL; int seed = 0; static void network_peer_write(struct peer *, u_int8_t *, u_int32_t); static void network_peerlist_update_dict(struct session *, struct benc_node *); static void network_peerlist_update_string(struct session *, struct benc_node *); static char *network_peer_id_create(void); static int network_connect(int, int, int, const struct sockaddr *, socklen_t); static int network_connect_peer(struct peer *); static void network_handle_peer_response(struct bufferevent *, void *); static void network_peer_process_message(u_int8_t, struct peer *); static void network_peer_handshake(struct session *, struct peer *); static void network_peer_keepalive(int, short, void *); /* index of piece dls by block index and offset */ RB_PROTOTYPE(piece_dl_by_idxoff, piece_dl_idxnode, entry, piece_dl_idxnode_cmp) RB_GENERATE(piece_dl_by_idxoff, piece_dl_idxnode, entry, piece_dl_idxnode_cmp) int piece_dl_idxnode_cmp(struct piece_dl_idxnode *p1, struct piece_dl_idxnode *p2) { int64_t idxdiff; idxdiff = p1->idx - p2->idx; if (idxdiff == 0) { return (p1->off - p2->off); } else { return (idxdiff); } } /* * network_peer_id_create() * * Generate a random peer id string for us to use */ static char * network_peer_id_create() { long r; char *id; r = random(); id = xmalloc(PEER_ID_LEN+1); memset(id, 1, PEER_ID_LEN+1); /* we don't care about truncation here */ (void) snprintf(id, PEER_ID_LEN+1, "-UL-0001-0%010ld", r); return (id); } /* * network_peer_write() * * Write data to a peer. */ static void network_peer_write(struct peer *p, u_int8_t *msg, u_int32_t len) { if (bufferevent_write(p->bufev, msg, len) != 0) errx(1, "network_peer_write() failure"); xfree(msg); p->lastsend = time(NULL); } /* * network_connect() * * Generic TCP/IP socket connection routine, used by other functions. */ static int network_connect(int domain, int type, int protocol, const struct sockaddr *name, socklen_t namelen) { int sockfd; trace("network_connect() making socket"); sockfd = socket(domain, type, protocol); if (sockfd == -1) { trace("network_connect(): socket"); return (-1); } trace("network_connect() setting socket non-blocking"); if (fcntl(sockfd, F_SETFL, O_NONBLOCK) == -1) err(1, "network_connect"); trace("network_connect() calling connect() on fd"); if (connect(sockfd, name, namelen) == -1) { if (errno != EINPROGRESS) { trace("network_connect() connect(): %s", strerror(errno)); return (-1); } } trace("network_connect() connect() returned"); return (sockfd); } /* * network_connect_peer() * * Connects socket to a peer. */ static int network_connect_peer(struct peer *p) { p->state |= PEER_STATE_HANDSHAKE1; return (network_connect(PF_INET, SOCK_STREAM, 0, (const struct sockaddr *) &p->sa, sizeof(p->sa))); } /* * network_peerlist_connect() * * Connect any new peers in our peer list. */ void network_peerlist_connect(struct session *sc) { struct peer *ep, *nxt; struct timeval tv; for (ep = TAILQ_FIRST(&sc->peers); ep != TAILQ_END(&sc->peers) ; ep = nxt) { nxt = TAILQ_NEXT(ep, peer_list); /* stay within our limits */ if (sc->num_peers >= sc->maxfds - 5) { network_peer_free(ep); sc->num_peers--; TAILQ_REMOVE(&sc->peers, ep, peer_list); continue; } trace("network_peerlist_update() we have a peer: %s:%d", inet_ntoa(ep->sa.sin_addr), ntohs(ep->sa.sin_port)); if (ep->connfd != 0) { /* XXX */ } else { trace("network_peerlist_update() connecting to peer: %s:%d", inet_ntoa(ep->sa.sin_addr), ntohs(ep->sa.sin_port)); /* XXX does this failure case do anything worthwhile? */ if ((ep->connfd = network_connect_peer(ep)) == -1) { trace("network_peerlist_update() failure connecting to peer: %s:%d - removing", inet_ntoa(ep->sa.sin_addr), ntohs(ep->sa.sin_port)); TAILQ_REMOVE(&sc->peers, ep, peer_list); network_peer_free(ep); sc->num_peers--; continue; } trace("network_peerlist_update() connected fd %d to peer: %s:%d", ep->connfd, inet_ntoa(ep->sa.sin_addr), ntohs(ep->sa.sin_port)); ep->bufev = bufferevent_new(ep->connfd, network_handle_peer_response, network_handle_peer_write, network_handle_peer_error, ep); if (ep->bufev == NULL) errx(1, "network_peerlist_update: bufferevent_new failure"); bufferevent_enable(ep->bufev, EV_READ|EV_WRITE); /* set up keep-alive timer */ timerclear(&tv); tv.tv_sec = 1; evtimer_set(&ep->keepalive_event, network_peer_keepalive, ep); evtimer_add(&ep->keepalive_event, &tv); trace("network_peerlist_update() initiating handshake"); network_peer_handshake(sc, ep); } } } /* * Adds a prepared struct peer to the peer list if it isn't already there * */ void network_peerlist_add_peer(struct session *sc, struct peer *p) { struct peer *ep; /* Is this peer already in the list? */ int found = 0; TAILQ_FOREACH(ep, &sc->peers, peer_list) { if (memcmp(&ep->sa.sin_addr, &p->sa.sin_addr, sizeof(ep->sa.sin_addr)) == 0 && memcmp(&ep->sa.sin_port, &p->sa.sin_port, sizeof(ep->sa.sin_port)) == 0) { found = 1; break; } } if (found == 0) { trace("network_peerlist_add_peer() adding peer to list: %s:%d", inet_ntoa(p->sa.sin_addr), ntohs(p->sa.sin_port)); TAILQ_INSERT_TAIL(&sc->peers, p, peer_list); sc->num_peers++; } else { network_peer_free(p); } } /* * network_peerlist_update_string() * * Handle string format peerlist parsing. */ static void network_peerlist_update_string(struct session *sc, struct benc_node *peers) { char *peerlist; size_t len, i; struct peer *p; len = peers->body.string.len; peerlist = peers->body.string.value; p = NULL; if (len == 0) trace("network_peerlist_update() peer list is zero in length"); /* check for peers to add */ for (i = 0; i < len; i++) { if (i % 6 == 0) { p = network_peer_create(); p->sc = sc; p->sa.sin_family = AF_INET; memcpy(&p->sa.sin_addr, peerlist + i, 4); memcpy(&p->sa.sin_port, peerlist + i + 4, 2); network_peerlist_add_peer(sc, p); continue; } } network_peerlist_connect(sc); } /* * network_peerlist_update_dict() * * Handle dictionary format peerlist parsing. */ static void network_peerlist_update_dict(struct session *sc, struct benc_node *peers) { struct benc_node *dict, *n; struct peer *p = NULL; struct addrinfo hints, *res; struct sockaddr_in sa; int port, error, l; char *ip, portstr[6]; if (!(peers->flags & BLIST)) errx(1, "peers object is not a list"); /* iterate over a blist of bdicts each with three keys */ TAILQ_FOREACH(dict, &peers->children, benc_nodes) { p = network_peer_create(); p->sc = sc; n = benc_node_find(dict, "ip"); if (!(n->flags & BSTRING)) errx(1, "node is not a string"); ip = n->body.string.value; n = benc_node_find(dict, "port"); if (!(n->flags & BINT)) errx(1, "node is not an integer"); port = n->body.number; l = snprintf(portstr, sizeof(portstr), "%d", port); if (l == -1 || l >= (int)sizeof(portstr)) errx(1, "network_peerlist_update_dict() string truncations"); if ((n = benc_node_find(dict, "peer id")) == NULL) errx(1, "couldn't find peer id field"); if (!(n->flags & BSTRING)) errx(1, "node is not a string"); memcpy(&p->id, n->body.string.value, sizeof(p->id)); memset(&hints, 0, sizeof(hints)); hints.ai_family = PF_INET; hints.ai_socktype = SOCK_STREAM; trace("network_peerlist_update_dict() calling getaddrinfo()"); error = getaddrinfo(ip, portstr, &hints, &res); if (error != 0) errx(1, "\"%s\" - %s", ip, gai_strerror(error)); p->sa.sin_family = AF_INET; sa.sin_addr = ((struct sockaddr_in *)res->ai_addr)->sin_addr; sa.sin_port = ((struct sockaddr_in *)res->ai_addr)->sin_port; memcpy(&p->sa.sin_addr, &sa.sin_addr, 4); memcpy(&p->sa.sin_port, &sa.sin_port, 2); freeaddrinfo(res); network_peerlist_add_peer(sc, p); } network_peerlist_connect(sc); } /* * network_peer_handshake() * * Build and write a handshake message to remote peer. */ static void network_peer_handshake(struct session *sc, struct peer *p) { u_int8_t *msg; /* * handshake: * pstrlen: string length of , as a single raw byte * pstr: string identifier of the protocol * reserved: eight (8) reserved bytes. All current implementations use all zeroes. Each bit in * these bytes can be used to change the behavior of the protocol. * An email from Bram suggests that trailing bits should be used first, so that leading bits * may be used to change the meaning of trailing bits. * info_hash: 20-byte SHA1 hash of the info key in the metainfo file. This is the same * info_hash that is transmitted in tracker requests. * peer_id: 20-byte string used as a unique ID for the client. This is the same peer_id that is * transmitted in tracker requests. * * In version 1.0 of the BitTorrent protocol, pstrlen = 19, and pstr = "BitTorrent protocol". */ p->connected = time(NULL); #define HANDSHAKELEN (1 + 19 + 8 + 20 + 20) msg = xmalloc(HANDSHAKELEN); memset(msg, 0, HANDSHAKELEN); msg[0] = 19; memcpy(msg + 1, "BitTorrent protocol", 19); /* set reserved bit to indicate we support the fast extension */ msg[27] |= 0x04; memcpy(msg + 28, sc->tp->info_hash, 20); memcpy(msg + 48, sc->peerid, 20); network_peer_write(p, msg, HANDSHAKELEN); } /* * network_handle_peer_response() * * Handle any input from peer, managing handshakes, * encryption requests and so on. Also handle ensuring the message is * complete before passing it to the message processor. */ static void network_handle_peer_response(struct bufferevent *bufev, void *data) { struct peer *p = data; size_t len; u_int32_t msglen; u_int8_t *base, id = 0; /* the complicated thing here is the non-blocking IO, which * means we have to be prepared to come back later and add more * data */ if (p->state & PEER_STATE_HANDSHAKE1 && p->rxpending == 0) { p->lastrecv = time(NULL); p->rxmsg = xmalloc(BT_INITIAL_LEN); p->rxmsglen = BT_INITIAL_LEN; p->rxpending = p->rxmsglen; goto read; } else { if (p->rxpending == 0) { /* this is a new message */ p->state &= ~PEER_STATE_GOTLEN; p->rxmsg = xmalloc(LENGTH_FIELD); p->rxmsglen = LENGTH_FIELD; p->rxpending = p->rxmsglen; goto read; } else { read: base = p->rxmsg + (p->rxmsglen - p->rxpending); len = bufferevent_read(bufev, base, p->rxpending); p->totalrx += len; p->rxpending -= len; /* more rx data pending, come back later */ if (p->rxpending > 0) goto out; if (p->state & PEER_STATE_HANDSHAKE1) { memcpy(&p->pstrlen, p->rxmsg, sizeof(p->pstrlen)); /* test for plain handshake */ if (p->pstrlen == BT_PSTRLEN && memcmp(p->rxmsg+1, BT_PROTOCOL, BT_PSTRLEN) == 0) { xfree(p->rxmsg); p->rxmsg = NULL; /* see comment above network_peer_handshake() for explanation of these numbers */ p->rxpending = 8 + 20 + 20; p->rxmsglen = p->rxpending; p->rxmsg = xmalloc(p->rxmsglen); p->state &= ~PEER_STATE_HANDSHAKE1; p->state |= PEER_STATE_HANDSHAKE2; goto out; } else { /* XXX: try D-H key exchange */ trace("network_handle_peer_response: crypto, killing peer for now"); p->state = 0; p->state |= PEER_STATE_DEAD; goto out; } } if (p->state & PEER_STATE_HANDSHAKE2) { /* see comment above network_peer_handshake() for explanation of these numbers */ memcpy(&p->info_hash, p->rxmsg + 8, 20); memcpy(&p->id, p->rxmsg + 8 + 20, 20); /* does this peer support fast extension? */ if (p->rxmsg[27] & 0x04) { p->state |= PEER_STATE_FAST; trace("network_handle_peer_response() fast peer %s:%d", inet_ntoa(p->sa.sin_addr), ntohs(p->sa.sin_port)); } else { trace("network_handle_peer_response() slow peer %s:%d", inet_ntoa(p->sa.sin_addr), ntohs(p->sa.sin_port)); } if (memcmp(p->info_hash, p->sc->tp->info_hash, 20) != 0) { trace("network_handle_peer_response() info hash mismatch for peer %s:%d", inet_ntoa(p->sa.sin_addr), ntohs(p->sa.sin_port)); p->state = 0; p->state |= PEER_STATE_DEAD; goto out; } xfree(p->rxmsg); p->rxmsg = NULL; p->state |= PEER_STATE_BITFIELD; p->state |= PEER_STATE_SENDBITFIELD; p->state &= ~PEER_STATE_HANDSHAKE2; p->rxpending = 0; goto out; } if (!(p->state & PEER_STATE_GOTLEN)) { /* got the length field */ memcpy(&msglen, p->rxmsg, sizeof(msglen)); p->rxmsglen = ntohl(msglen); if (p->rxmsglen > MAX_MESSAGE_LEN) { trace("network_handle_peer_response() got a message %u bytes long, longer than %u bytes, assuming its malicious and killing peer %s:%d", p->rxmsglen, MAX_MESSAGE_LEN, inet_ntoa(p->sa.sin_addr), ntohs(p->sa.sin_port)); p->state = 0; p->state |= PEER_STATE_DEAD; goto out; } if (p->rxmsg != NULL) { xfree(p->rxmsg); p->rxmsg = NULL; } p->state |= PEER_STATE_GOTLEN; /* keep-alive: do nothing */ if (p->rxmsglen == 0) goto out; p->rxmsg = xmalloc(p->rxmsglen); memset(p->rxmsg, 0, p->rxmsglen); p->rxpending = p->rxmsglen; goto out; } } /* if we get this far, means we have the entire message */ memcpy(&id, p->rxmsg, 1); network_peer_process_message(id, p); if (p->rxmsg != NULL) { xfree(p->rxmsg); p->rxmsg = NULL; } } out: /* * if its time to send the bitfield, and we actually have some pieces, * send the bitfield. */ if (p->state & PEER_STATE_SENDBITFIELD) { /* fast extension gives us a couple more options */ if (p->state & PEER_STATE_FAST) { if (torrent_empty(p->sc->tp)) { network_peer_write_havenone(p); } else if (p->sc->tp->good_pieces == p->sc->tp->num_pieces) { network_peer_write_haveall(p); } else { network_peer_write_bitfield(p); } } else if (!torrent_empty(p->sc->tp)) { network_peer_write_bitfield(p); } p->state &= ~PEER_STATE_SENDBITFIELD; } if (EVBUFFER_LENGTH(EVBUFFER_INPUT(bufev))) bufev->readcb(bufev, data); } /* * network_peer_process_message() * * Now that we actually have the full message in our * buffers, process it. */ static void network_peer_process_message(u_int8_t id, struct peer *p) { struct torrent_piece *tpp; struct peer *tp; struct piece_dl *pd, *nxtpd; struct piece_ul *pu, *nxtpu; int res = 0; int found = 0; u_int32_t bitfieldlen, idx, blocklen, off; /* XXX: safety-check for correct message lengths */ switch (id) { case PEER_MSG_ID_CHOKE: trace("CHOKE message from peer %s:%d", inet_ntoa(p->sa.sin_addr), ntohs(p->sa.sin_port)); p->state |= PEER_STATE_CHOKED; if (!(p->state & PEER_STATE_FAST)) { for (pd = TAILQ_FIRST(&p->peer_piece_dls); pd; pd = nxtpd) { nxtpd = TAILQ_NEXT(pd, peer_piece_dl_list); pd->pc = NULL; TAILQ_REMOVE(&p->peer_piece_dls, pd, peer_piece_dl_list); p->dl_queue_len--; } } break; case PEER_MSG_ID_UNCHOKE: trace("UNCHOKE message from peer %s:%d", inet_ntoa(p->sa.sin_addr), ntohs(p->sa.sin_port)); p->state &= ~PEER_STATE_CHOKED; break; case PEER_MSG_ID_INTERESTED: trace("INTERESTED message from peer %s:%d", inet_ntoa(p->sa.sin_addr), ntohs(p->sa.sin_port)); p->state |= PEER_STATE_INTERESTED; break; case PEER_MSG_ID_NOTINTERESTED: trace("NOTINTERESTED message from peer %s:%d", inet_ntoa(p->sa.sin_addr), ntohs(p->sa.sin_port)); p->state &= ~PEER_STATE_INTERESTED; break; case PEER_MSG_ID_HAVE: memcpy(&idx, p->rxmsg+sizeof(id), sizeof(idx)); idx = ntohl(idx); trace("HAVE message from peer %s:%d (idx=%u)", inet_ntoa(p->sa.sin_addr), ntohs(p->sa.sin_port), idx); if (idx > p->sc->tp->num_pieces - 1) { trace("have index overflow, ignoring"); break; } if (p->bitfield == NULL) { bitfieldlen = (p->sc->tp->num_pieces + 7) / 8; p->bitfield = xmalloc(bitfieldlen); memset(p->bitfield, 0, bitfieldlen); p->state &= ~PEER_STATE_BITFIELD; p->state |= PEER_STATE_ESTABLISHED; } util_setbit(p->bitfield, idx); /* does this peer have anything we want? */ scheduler_piece_gimme(p, PIECE_GIMME_NOCREATE, &res); if (res && !(p->state & PEER_STATE_AMINTERESTED)) network_peer_write_interested(p); break; case PEER_MSG_ID_BITFIELD: trace("BITFIELD message from peer %s:%d", inet_ntoa(p->sa.sin_addr), ntohs(p->sa.sin_port)); if (!(p->state & PEER_STATE_BITFIELD)) { trace("not expecting bitfield!"); break; } bitfieldlen = p->rxmsglen - sizeof(id); if (bitfieldlen != (p->sc->tp->num_pieces + 7) / 8) { trace("bitfield is wrong size! killing peer connection (is: %u should be: %u)", bitfieldlen*8, p->sc->tp->num_pieces + 7); p->state = 0; p->state |= PEER_STATE_DEAD; break; } p->bitfield = xmalloc(bitfieldlen); memset(p->bitfield, 0, bitfieldlen); memcpy(p->bitfield, p->rxmsg+sizeof(id), bitfieldlen); p->state &= ~PEER_STATE_BITFIELD; p->state |= PEER_STATE_ESTABLISHED; /* does this peer have anything we want? */ scheduler_piece_gimme(p, PIECE_GIMME_NOCREATE, &res); if (res && !(p->state & PEER_STATE_AMINTERESTED)) network_peer_write_interested(p); break; case PEER_MSG_ID_REQUEST: memcpy(&idx, p->rxmsg+sizeof(id), sizeof(idx)); idx = ntohl(idx); if (idx > p->sc->tp->num_pieces - 1) { trace("REQUEST index out of bounds (%u)", idx); break; } memcpy(&off, p->rxmsg+sizeof(id)+sizeof(idx), sizeof(off)); off = ntohl(off); tpp = torrent_piece_find(p->sc->tp, idx); if (off > tpp->len) { trace("REQUEST offset out of bounds (%u)"), off; break; } memcpy(&blocklen, p->rxmsg+sizeof(id)+sizeof(idx)+sizeof(off), sizeof(blocklen)); blocklen = ntohl(blocklen); if (!(tpp->flags & TORRENT_PIECE_CKSUMOK)) { trace("REQUEST for data we don't have from peer %s:%d idx=%u off=%u len=%u", inet_ntoa(p->sa.sin_addr), ntohs(p->sa.sin_port), idx, off, blocklen); if (p->state & PEER_STATE_FAST) network_peer_reject_block(p, idx, off, blocklen); break; } trace("REQUEST message from peer %s:%d idx=%u off=%u len=%u", inet_ntoa(p->sa.sin_addr), ntohs(p->sa.sin_port), idx, off, blocklen); /* network_peer_write_piece(p, idx, off, blocklen); */ network_piece_ul_enqueue(p, idx, off, blocklen); break; case PEER_MSG_ID_PIECE: memcpy(&idx, p->rxmsg+sizeof(id), sizeof(idx)); idx = ntohl(idx); memcpy(&off, p->rxmsg+sizeof(id)+sizeof(idx), sizeof(off)); off = ntohl(off); trace("PIECE message (idx=%u off=%u len=%u) from peer %s:%d", idx, off, p->rxmsglen - (sizeof(id)+sizeof(off)+sizeof(idx)), inet_ntoa(p->sa.sin_addr), ntohs(p->sa.sin_port)); if (idx > p->sc->tp->num_pieces - 1) { trace("PIECE index out of bounds"); break; } tpp = torrent_piece_find(p->sc->tp, idx); if (off > tpp->len) { trace("PIECE offset out of bounds"); break; } pd = network_piece_dl_find(p->sc, p, idx, off); if (pd != NULL && p->rxmsglen-(sizeof(id)+sizeof(off)+sizeof(idx)) != pd->len) { trace("PIECE len incorrect, should be %u", pd->len); break; } if (pd == NULL) { trace("PIECE message for data we didn't request - killing peer"); p->state = 0; p->state |= PEER_STATE_DEAD; break; } /* Only read if we don't already have it */ if (!(tpp->flags & TORRENT_PIECE_CKSUMOK)) { p->dl_queue_len--; if (!(tpp->flags & TORRENT_PIECE_MAPPED)) torrent_piece_map(tpp); network_peer_read_piece(p, idx, off, p->rxmsglen-(sizeof(id)+sizeof(off)+sizeof(idx)), p->rxmsg+sizeof(id)+sizeof(off)+sizeof(idx)); /* only checksum if we think we have every block of this piece */ found = 1; for (off = 0; off < tpp->len; off += BLOCK_SIZE) { if ((pd = network_piece_dl_find(p->sc, p, idx, off)) == NULL) { found = 0; break; } if (pd->len != pd->bytes) { found = 0; break; } } if (found) { res = torrent_piece_checkhash(p->sc->tp, tpp); torrent_piece_unmap(tpp); if (res == 0) { trace("hash check success for piece %d", idx); /* dump fastresume data */ torrent_fastresume_dump(p->sc->tp); p->sc->tp->good_pieces++; p->sc->tp->left -= tpp->len; if (p->sc->tp->good_pieces == p->sc->tp->num_pieces) { if (!seed) { refresh_progress_meter(); exit(0); } else if (!p->sc->announce_underway) { /* tell tracker we're done */ announce(p->sc, "completed"); } } /* send HAVE messages to all peers */ TAILQ_FOREACH(tp, &p->sc->peers, peer_list) network_peer_write_have(tp, idx); /* notify control server */ ctl_server_notify_pieces(p->sc); /* clean up all the piece dls for this now that its done */ for (off = 0; off < tpp->len; off += BLOCK_SIZE) { if ((pd = network_piece_dl_find(p->sc, NULL, idx, off)) != NULL) { network_piece_dl_free(p->sc, pd); } } } else { trace("hash check failure for piece %d", idx); for (off = 0; off < tpp->len; off += BLOCK_SIZE) { if ((pd = network_piece_dl_find(p->sc, NULL, idx, off)) != NULL) { network_piece_dl_free(p->sc, pd); } } } } } else { /* this code is wrong */ #if 0 /* XXX hash check failed, try re-downloading this piece? */ /* clean up this piece dl, although its not fully the correct thing to do */ if ((pd = network_piece_dl_find(p->sc, idx, off)) != NULL) { pd->pc = NULL; p->dl_queue_len--; } #endif } p->lastrecv = time(NULL); break; case PEER_MSG_ID_CANCEL: memcpy(&idx, p->rxmsg+sizeof(id), sizeof(idx)); idx = ntohl(idx); if (idx > p->sc->tp->num_pieces - 1) { trace("CANCEL index out of bounds (%u)", idx); break; } memcpy(&off, p->rxmsg+sizeof(id)+sizeof(idx), sizeof(off)); off = ntohl(off); memcpy(&blocklen, p->rxmsg+sizeof(id)+sizeof(idx)+sizeof(off), sizeof(blocklen)); blocklen = ntohl(blocklen); trace("CANCEL message idx=%u off=%u len=%u from peer %s:%d", idx, off, blocklen, inet_ntoa(p->sa.sin_addr), ntohs(p->sa.sin_port)); for (pu = TAILQ_FIRST(&p->peer_piece_uls); pu; pu = nxtpu) { nxtpu = TAILQ_NEXT(pu, peer_piece_ul_list); if (pu->idx == idx && pu->off == off && pu->len == blocklen) { TAILQ_REMOVE(&p->peer_piece_uls, pu, peer_piece_ul_list); xfree(pu); } } break; case PEER_MSG_ID_REJECT: trace("REJECT message from peer %s:%d", inet_ntoa(p->sa.sin_addr), ntohs(p->sa.sin_port)); if (!(p->state & PEER_STATE_FAST)) { trace("peer %s:%d does not support fast extension, closing", inet_ntoa(p->sa.sin_addr), ntohs(p->sa.sin_port)); p->state = 0; p->state |= PEER_STATE_DEAD; break; } memcpy(&idx, p->rxmsg+sizeof(id), sizeof(idx)); idx = ntohl(idx); if (idx > p->sc->tp->num_pieces - 1) { trace("REJECT index out of bounds (%u)", idx); break; } memcpy(&off, p->rxmsg+sizeof(id)+sizeof(idx), sizeof(off)); off = ntohl(off); memcpy(&blocklen, p->rxmsg+sizeof(id)+sizeof(idx)+sizeof(off), sizeof(blocklen)); blocklen = ntohl(blocklen); trace("REJECT message from peer %s:%d idx=%u off=%u len=%u", inet_ntoa(p->sa.sin_addr), ntohs(p->sa.sin_port), idx, off, blocklen); if ((pd = network_piece_dl_find(p->sc, p, idx, off)) == NULL) { trace("could not find piece dl for reject from peer %s:%d idx=%u off=%u len=%u", inet_ntoa(p->sa.sin_addr), ntohs(p->sa.sin_port), idx, off, blocklen); break; } network_piece_dl_free(p->sc, pd); p->dl_queue_len--; break; case PEER_MSG_ID_HAVENONE: trace("HAVENONE message from peer %s:%d", inet_ntoa(p->sa.sin_addr), ntohs(p->sa.sin_port)); if (!(p->state & PEER_STATE_BITFIELD)) { trace("not expecting HAVENONE!"); break; } bitfieldlen = (p->sc->tp->num_pieces + 7) / 8; p->bitfield = xmalloc(bitfieldlen); memset(p->bitfield, 0, bitfieldlen); p->state &= ~PEER_STATE_BITFIELD; p->state |= PEER_STATE_ESTABLISHED; /* does this peer have anything we want? */ scheduler_piece_gimme(p, PIECE_GIMME_NOCREATE, &res); if (res && !(p->state & PEER_STATE_AMINTERESTED)) network_peer_write_interested(p); break; case PEER_MSG_ID_HAVEALL: trace("HAVEALL message from peer %s:%d", inet_ntoa(p->sa.sin_addr), ntohs(p->sa.sin_port)); if (!(p->state & PEER_STATE_BITFIELD)) { trace("not expecting HAVEALL"); break; } bitfieldlen = (p->sc->tp->num_pieces + 7) / 8; p->bitfield = xmalloc(bitfieldlen); memset(p->bitfield, 0xFF, bitfieldlen); p->state &= ~PEER_STATE_BITFIELD; p->state |= PEER_STATE_ESTABLISHED; /* does this peer have anything we want? */ scheduler_piece_gimme(p, PIECE_GIMME_NOCREATE, &res); if (res && !(p->state & PEER_STATE_AMINTERESTED)) network_peer_write_interested(p); break; case PEER_MSG_ID_ALLOWEDFAST: memcpy(&idx, p->rxmsg+sizeof(id), sizeof(idx)); idx = ntohl(idx); trace("ALLOWEDFAST message (idx=%u) from peer %s:%d", idx, inet_ntoa(p->sa.sin_addr), ntohs(p->sa.sin_port)); if (idx > p->sc->tp->num_pieces - 1) { trace("ALLOWEDFAST index out of bounds"); break; } /* ignore these for now */ case PEER_MSG_ID_SUGGEST: memcpy(&idx, p->rxmsg+sizeof(id), sizeof(idx)); idx = ntohl(idx); trace("SUGGEST message (idx=%u) from peer %s:%d", idx, inet_ntoa(p->sa.sin_addr), ntohs(p->sa.sin_port)); if (idx > p->sc->tp->num_pieces - 1) { trace("SUGGEST index out of bounds"); break; } /* ignore these for now */ default: trace("Unknown message from peer %s:%d", inet_ntoa(p->sa.sin_addr), ntohs(p->sa.sin_port)); break; } } /* * network_handle_peer_error() * * Handle errors on peer sockets. Typically we mark things as dead * and let the scheduler handle cleanup. */ void network_handle_peer_error(struct bufferevent *bufev, short error, void *data) { struct peer *p; p = data; if (error & EVBUFFER_TIMEOUT) { trace("network_handle_peer_error() TIMEOUT for peer %s:%d", inet_ntoa(p->sa.sin_addr), ntohs(p->sa.sin_port)); } if (error & EVBUFFER_EOF) { p->state = 0; p->state |= PEER_STATE_DEAD; trace("network_handle_peer_error() EOF for peer %s:%d", inet_ntoa(p->sa.sin_addr), ntohs(p->sa.sin_port)); } else { trace("network_handle_peer_error() error for peer %s:%d", inet_ntoa(p->sa.sin_addr), ntohs(p->sa.sin_port)); p->state = 0; p->state |= PEER_STATE_DEAD; } } /* * network_peer_keepalive() * * Periodically send keep-alive messages to peers if necessary. */ static void network_peer_keepalive(int fd, short type, void *arg) { struct peer *p; struct timeval tv; p = arg; if (time(NULL) - p->lastsend >= PEER_KEEPALIVE_SECONDS) network_peer_write_keepalive(p); timerclear(&tv); tv.tv_sec = 1; evtimer_set(&p->keepalive_event, network_peer_keepalive, p); evtimer_add(&p->keepalive_event, &tv); } /* * network_handle_peer_write() * * Handle write events. Mostly involves cleanup. */ void network_handle_peer_write(struct bufferevent *bufev, void *data) { /* do nothing */ } /* * network_peer_write_have() * * Send HAVE message to peer. */ void network_peer_write_have(struct peer *p, u_int32_t idx) { u_int32_t msglen, msglen2; u_int8_t *msg, id; msglen = sizeof(msglen) + sizeof(id) + sizeof(idx); msg = xmalloc(msglen); msglen2 = htonl(msglen - sizeof(msglen)); id = PEER_MSG_ID_HAVE; idx = htonl(idx); memcpy(msg, &msglen2, sizeof(msglen2)); memcpy(msg+sizeof(msglen2), &id, sizeof(id)); memcpy(msg+sizeof(msglen2)+sizeof(id), &idx, sizeof(idx)); network_peer_write(p, msg, msglen); } /* * network_peer_write_piece() * * Write a PIECE message to a remote peer, * filling the buffer from our local torrent data store. */ void network_peer_write_piece(struct peer *p, u_int32_t idx, u_int32_t offset, u_int32_t len) { struct torrent_piece *tpp; u_int32_t msglen, msglen2; u_int8_t *data, *msg, id; int hint = 0; trace("network_peer_write_piece() idx=%u off=%u len=%u for peer %s:%d", idx, offset, len, inet_ntoa(p->sa.sin_addr), ntohs(p->sa.sin_port)); if ((tpp = torrent_piece_find(p->sc->tp, idx)) == NULL) { trace("network_peer_write_piece() piece %u - failed at torrent_piece_find(), returning", idx); return; } if (!(tpp->flags & TORRENT_PIECE_MAPPED)) torrent_piece_map(tpp); if ((data = torrent_block_read(tpp, offset, len, &hint)) == NULL) { trace("network_peer_write_piece() piece %u - failed at torrent_block_read(), returning", idx); return; } /* construct PIECE message response */ msglen = sizeof(msglen) + sizeof(id) + sizeof(idx) + sizeof(offset) + len; msglen2 = htonl((msglen - sizeof(msglen))); msg = xmalloc(msglen); memset(msg, 0, msglen); id = PEER_MSG_ID_PIECE; idx = htonl(idx); offset = htonl(offset); memcpy(msg, &msglen2, sizeof(msglen2)); memcpy(msg+sizeof(msglen2), &id, sizeof(id)); memcpy(msg+sizeof(msglen2)+sizeof(id), &idx, sizeof(idx)); memcpy(msg+sizeof(msglen2)+sizeof(id)+sizeof(idx), &offset, sizeof(offset)); memcpy(msg+sizeof(msglen2)+sizeof(id)+sizeof(idx)+sizeof(offset), data, len); network_peer_write(p, msg, msglen); if (hint == 1) xfree(data); p->totaltx += msglen; } /* * network_peer_read_piece() * * Taking a buffer and the pre-parsed parameters, read a PIECE request * into our torrent data store. */ void network_peer_read_piece(struct peer *p, u_int32_t idx, off_t offset, u_int32_t len, void *data) { struct torrent_piece *tpp; struct piece_dl *pd; if ((tpp = torrent_piece_find(p->sc->tp, idx)) == NULL) { trace("network_peer_read_piece: piece %u - failed at torrent_piece_find(), returning", idx); return; } trace("network_peer_read_piece() at index %u offset %u length %u", idx, offset, len); if ((pd = network_piece_dl_find(p->sc, p, idx, offset)) == NULL) return; torrent_block_write(tpp, offset, len, data); pd->bytes += len; /* XXX not really accurate measure of progress since the data could be bad */ p->sc->tp->downloaded += len; p->totalrx += len; ctl_server_notify_bytes(p->sc, p->sc->tp->downloaded); } /* network_peer_request_block() * * Send a REQUEST message to remote peer. */ void network_peer_request_block(struct peer *p, u_int32_t idx, u_int32_t off, u_int32_t len) { u_int32_t msglen, msglen2, blocklen; u_int8_t *msg, id; trace("network_peer_request_block, index: %u offset: %u len: %u to peer %s:%d", idx, off, len, inet_ntoa(p->sa.sin_addr), ntohs(p->sa.sin_port)); msglen = sizeof(msglen) + sizeof(id) + sizeof(idx) + sizeof(off) + sizeof(blocklen); msg = xmalloc(msglen); msglen2 = htonl(msglen - sizeof(msglen)); id = PEER_MSG_ID_REQUEST; idx = htonl(idx); off = htonl(off); blocklen = htonl(len); memcpy(msg, &msglen2, sizeof(msglen2)); memcpy(msg+sizeof(msglen2), &id, sizeof(id)); memcpy(msg+sizeof(msglen2)+sizeof(id), &idx, sizeof(idx)); memcpy(msg+sizeof(msglen2)+sizeof(id)+sizeof(idx), &off, sizeof(off)); memcpy(msg+sizeof(msglen2)+sizeof(id)+sizeof(idx)+sizeof(off), &blocklen, sizeof(blocklen)); network_peer_write(p, msg, msglen); } /* * network_peer_cancel_piece() * * Send a CANCEL message to remote peer. */ void network_peer_cancel_piece(struct piece_dl *pd) { u_int32_t msglen, msglen2, blocklen, off, idx; u_int8_t *msg, id; trace("network_peer_cancel_piece, index: %u offset: %u to peer %s:%d", pd->idx, pd->off, inet_ntoa(pd->pc->sa.sin_addr), ntohs(pd->pc->sa.sin_port)); msglen = sizeof(msglen) + sizeof(id) + sizeof(idx) + sizeof(off) + sizeof(blocklen); msg = xmalloc(msglen); msglen2 = htonl(msglen - sizeof(msglen)); id = PEER_MSG_ID_CANCEL; idx = htonl(pd->idx); off = htonl(pd->off); blocklen = htonl(pd->len); memcpy(msg, &msglen2, sizeof(msglen2)); memcpy(msg+sizeof(msglen2), &id, sizeof(id)); memcpy(msg+sizeof(msglen2)+sizeof(id), &idx, sizeof(idx)); memcpy(msg+sizeof(msglen2)+sizeof(id)+sizeof(idx), &off, sizeof(off)); memcpy(msg+sizeof(msglen2)+sizeof(id)+sizeof(idx)+sizeof(off), &blocklen, sizeof(blocklen)); network_peer_write(pd->pc, msg, msglen); } /* * network_peer_write_interested() * * Send an INTERESTED message to remote peer. */ void network_peer_write_interested(struct peer *p) { u_int32_t len; u_int8_t *msg, id; trace("network_peer_write_interested() to peer %s:%d", inet_ntoa(p->sa.sin_addr), ntohs(p->sa.sin_port)); len = htonl(sizeof(id)); id = PEER_MSG_ID_INTERESTED; msg = xmalloc(sizeof(len) + sizeof(id)); memcpy(msg, &len, sizeof(len)); memcpy(msg+sizeof(len), &id, sizeof(id)); p->state |= PEER_STATE_AMINTERESTED; network_peer_write(p, msg, sizeof(len) + sizeof(id)); } /* * network_peer_write_bitfield() * * Send a BITFIELD message to remote peer. */ void network_peer_write_bitfield(struct peer *p) { u_int32_t bitfieldlen, msglen, msglen2; u_int8_t *bitfield, *msg, id; bitfieldlen = (p->sc->tp->num_pieces + 7) / 8; trace("network_peer_write_bitfield() to peer %s:%d len: %u", inet_ntoa(p->sa.sin_addr), ntohs(p->sa.sin_port), bitfieldlen); id = PEER_MSG_ID_BITFIELD; bitfield = torrent_bitfield_get(p->sc->tp); msglen = sizeof(msglen) + sizeof(id) + bitfieldlen; msg = xmalloc(msglen); msglen2 = htonl(msglen - sizeof(msglen)); memcpy(msg, &msglen2, sizeof(msglen2)); memcpy(msg+sizeof(msglen), &id, sizeof(id)); memcpy(msg+sizeof(msglen)+sizeof(id), bitfield, bitfieldlen); network_peer_write(p, msg, msglen); xfree(bitfield); } /* * network_peer_write_unchoke() * * Send an UNCHOKE message to remote peer. */ void network_peer_write_unchoke(struct peer *p) { u_int32_t len; u_int8_t *msg, id; trace("network_peer_write_unchoke() to peer %s:%d", inet_ntoa(p->sa.sin_addr), ntohs(p->sa.sin_port)); len = htonl(sizeof(id)); id = PEER_MSG_ID_UNCHOKE; msg = xmalloc(sizeof(len) + sizeof(id)); memcpy(msg, &len, sizeof(len)); memcpy(msg+sizeof(len), &id, sizeof(id)); p->state &= ~PEER_STATE_AMCHOKING; network_peer_write(p, msg, sizeof(len) + sizeof(id)); } /* * network_peer_write_choke() * * Send a CHOKE message to remote peer. */ void network_peer_write_choke(struct peer *p) { u_int32_t len; u_int8_t *msg, id; trace("network_peer_write_choke() to peer %s:%d", inet_ntoa(p->sa.sin_addr), ntohs(p->sa.sin_port)); len = htonl(sizeof(id)); id = PEER_MSG_ID_CHOKE; msg = xmalloc(sizeof(len) + sizeof(id)); memcpy(msg, &len, sizeof(len)); memcpy(msg+sizeof(len), &id, sizeof(id)); p->state |= PEER_STATE_AMCHOKING; network_peer_write(p, msg, sizeof(len) + sizeof(id)); } /* * network_peer_write_keepalive() * * Send a keep-alive message to remote peer. */ void network_peer_write_keepalive(struct peer *p) { u_int32_t len; u_int8_t *msg; trace("network_peer_write_keepalive() to peer %s:%d", inet_ntoa(p->sa.sin_addr), ntohs(p->sa.sin_port)); msg = xmalloc(sizeof(len)); memset(msg, 0, sizeof(len)); network_peer_write(p, msg, sizeof(len)); } /* * network_peer_write_haveall() * * Send a HAVEALL message to remote peer. */ void network_peer_write_haveall(struct peer *p) { u_int32_t len; u_int8_t *msg, id; trace("network_peer_write_haveall() to peer %s:%d", inet_ntoa(p->sa.sin_addr), ntohs(p->sa.sin_port)); len = htonl(sizeof(id)); id = PEER_MSG_ID_HAVEALL; msg = xmalloc(sizeof(len) + sizeof(id)); memcpy(msg, &len, sizeof(len)); memcpy(msg+sizeof(len), &id, sizeof(id)); network_peer_write(p, msg, sizeof(len) + sizeof(id)); } /* * network_peer_write_havenone() * * Send a HAVENONE message to remote peer. */ void network_peer_write_havenone(struct peer *p) { u_int32_t len; u_int8_t *msg, id; trace("network_peer_write_havenone() to peer %s:%d", inet_ntoa(p->sa.sin_addr), ntohs(p->sa.sin_port)); len = htonl(sizeof(id)); id = PEER_MSG_ID_HAVENONE; msg = xmalloc(sizeof(len) + sizeof(id)); memcpy(msg, &len, sizeof(len)); memcpy(msg+sizeof(len), &id, sizeof(id)); network_peer_write(p, msg, sizeof(len) + sizeof(id)); } /* network_peer_reject_block() * * Send a REJECT message to remote peer. */ void network_peer_reject_block(struct peer *p, u_int32_t idx, u_int32_t off, u_int32_t len) { u_int32_t msglen, msglen2, blocklen; u_int8_t *msg, id; trace("network_peer_reject_block, index: %u offset: %u len: %u to peer %s:%d", idx, off, len, inet_ntoa(p->sa.sin_addr), ntohs(p->sa.sin_port)); msglen = sizeof(msglen) + sizeof(id) + sizeof(idx) + sizeof(off) + sizeof(blocklen); msg = xmalloc(msglen); msglen2 = htonl(msglen - sizeof(msglen)); id = PEER_MSG_ID_REJECT; idx = htonl(idx); off = htonl(off); blocklen = htonl(len); memcpy(msg, &msglen2, sizeof(msglen2)); memcpy(msg+sizeof(msglen2), &id, sizeof(id)); memcpy(msg+sizeof(msglen2)+sizeof(id), &idx, sizeof(idx)); memcpy(msg+sizeof(msglen2)+sizeof(id)+sizeof(idx), &off, sizeof(off)); memcpy(msg+sizeof(msglen2)+sizeof(id)+sizeof(idx)+sizeof(off), &blocklen, sizeof(blocklen)); network_peer_write(p, msg, msglen); } /* * network_crypto_dh() * * Generate a DH key object. */ DH * network_crypto_dh() { DH *dhp; if ((dhp = DH_new()) == NULL) errx(1, "network_crypto_pubkey: DH_new() failure"); if ((dhp->p = BN_bin2bn(mse_P, CRYPTO_INT_LEN, NULL)) == NULL) errx(1, "network_crypto_pubkey: BN_bin2bn(P) failure"); if ((dhp->g = BN_bin2bn(mse_G, CRYPTO_INT_LEN, NULL)) == NULL) errx(1, "network_crypto_pubkey: BN_bin2bn(G) failure"); if (DH_generate_key(dhp) == 0) errx(1, "network_crypto_pubkey: DH_generate_key() failure"); return (dhp); } /* * network_peer_lastcomms() * * Return how long in seconds since last communication on this peer. */ long network_peer_lastcomms(struct peer *p) { return (time(NULL) - p->lastrecv); } /* * network_peer_rxrate() * * Return the average rx transfer rate of a given peer. */ u_int64_t network_peer_rxrate(struct peer *p) { u_int64_t rate; rate = time(NULL) - p->connected; /* prevent divide by zero */ if (rate == 0) return (0); return (p->totalrx / rate); } /* * network_peer_txrate() * * Return the average tx transfer rate of a given peer. */ u_int64_t network_peer_txrate(struct peer *p) { u_int64_t rate; rate = time(NULL) - p->connected; /* prevent divide by zero */ if (rate == 0) return (0); return (p->totaltx / rate); } /* * network_piece_dl_create() * * Create a piece dl, and also insert into the per-peer list and global * btree index. */ struct piece_dl * network_piece_dl_create(struct peer *p, u_int32_t idx, u_int32_t off, u_int32_t len) { struct piece_dl *pd; struct piece_dl_idxnode find, *res; pd = xmalloc(sizeof(*pd)); memset(pd, 0, sizeof(*pd)); pd->pc = p; pd->idx = idx; pd->off = off; pd->len = len; /* check for an existing piece_dl_idxnode */ find.off = off; find.idx = idx; if ((res = RB_FIND(piece_dl_by_idxoff, &p->sc->piece_dl_by_idxoff, &find)) == NULL) { /* need to create one */ res = xmalloc(sizeof(*res)); memset(res, 0, sizeof(*res)); res->off = off; res->idx = idx; TAILQ_INIT(&res->idxnode_piece_dls); TAILQ_INSERT_TAIL(&res->idxnode_piece_dls, pd, idxnode_piece_dl_list); RB_INSERT(piece_dl_by_idxoff, &p->sc->piece_dl_by_idxoff, res); } else { /* found a pre-existing one, just append this to its list */ TAILQ_INSERT_TAIL(&res->idxnode_piece_dls, pd, idxnode_piece_dl_list); } TAILQ_INSERT_TAIL(&p->peer_piece_dls, pd, peer_piece_dl_list); return (pd); } /* * network_piece_dl_free() * * Free piece dls in a clean manner. */ void network_piece_dl_free(struct session *sc, struct piece_dl *pd) { struct piece_dl_idxnode find, *res; find.off = pd->off; find.idx = pd->idx; /* remove from index/offset btree */ if ((res = RB_FIND(piece_dl_by_idxoff, &sc->piece_dl_by_idxoff, &find)) != NULL) TAILQ_REMOVE(&res->idxnode_piece_dls, pd, idxnode_piece_dl_list); if (pd->pc != NULL) { /* remove from per-peer list */ TAILQ_REMOVE(&pd->pc->peer_piece_dls, pd, peer_piece_dl_list); } if (res != NULL && TAILQ_EMPTY(&res->idxnode_piece_dls)) RB_REMOVE(piece_dl_by_idxoff, &sc->piece_dl_by_idxoff, res); xfree(pd); pd = NULL; } /* public functions */ /* * network_init() * * Network subsystem init, needs to be called before doing anything. */ void network_init() { event_init(); } /* * network_listen() * * Create a listening server socket. */ int network_listen(char *host, char *port) { int error = 0; int fd; int option_value = 1; struct addrinfo hints, *res; trace("network_listen() creating socket"); if ((fd = socket(PF_INET, SOCK_STREAM, 0)) == -1) err(1, "could not create server socket"); trace("network_listen() setting socket non-blocking"); if (fcntl(fd, F_SETFL, O_NONBLOCK) == -1) err(1, "network_listen: fcntl"); memset(&hints, 0, sizeof(hints)); hints.ai_family = PF_INET; hints.ai_socktype = SOCK_STREAM; trace("network_listen() calling getaddrinfo()"); error = getaddrinfo(host, port, &hints, &res); if (error != 0) errx(1, "\"%s\" - %s", host, gai_strerror(error)); trace("network_listen() settings socket options"); error = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &option_value, sizeof(option_value)); if (error == -1) err(1, "could not set socket options"); trace("network_listen() binding socket to address"); if (bind(fd, res->ai_addr, res->ai_addrlen) == -1) err(1, "could not bind to port %s", port); trace("network_listen() listening on socket"); if (listen(fd, MAX_BACKLOG) == -1) err(1, "could not listen on server socket"); freeaddrinfo(res); trace("network_listen() done"); return fd; } /* * network_start_torrent() * * Start handling network stuff for a new torrent. */ int network_start_torrent(struct torrent *tp, rlim_t maxfds) { int ret; struct session *sc; off_t len, started; sc = xmalloc(sizeof(*sc)); memset(sc, 0, sizeof(*sc)); TAILQ_INIT(&sc->peers); sc->tp = tp; sc->maxfds = maxfds; if (tp->good_pieces == tp->num_pieces) tp->left = 0; if (user_port == NULL) { sc->port = xstrdup(DEFAULT_PORT); } else { sc->port = xstrdup(user_port); trace("using port %s instead of default", user_port); } sc->peerid = network_peer_id_create(); trace("my peer id: %s", sc->peerid); /* an ugly way to find out how much data we started with. */ started = tp->downloaded; tp->downloaded = 0; if (gui_port != NULL) ctl_server_start(sc, gui_port, started); if (tp->type == SINGLEFILE) { len = tp->body.singlefile.tfp.file_length; } else { len = tp->body.multifile.total_length; } start_progress_meter(tp->name, len, &tp->downloaded, &tp->good_pieces, tp->num_pieces, started); ret = announce(sc, "started"); event_dispatch(); trace("network_start_torrent() returning name %s good pieces %u", tp->name, tp->good_pieces); return (ret); } /* * network_peerlist_update() * * When given a bencode node, decide which kind of peer list format * its in, and fire off the relevant parsing routine. */ void network_peerlist_update(struct session *sc, struct benc_node *peers) { if (peers->flags & BSTRING) { network_peerlist_update_string(sc, peers); } else { network_peerlist_update_dict(sc, peers); } ctl_server_notify_peers(sc); } /* * network_handle_peer_connect() * * Handle incoming peer connections. */ void network_handle_peer_connect(struct bufferevent *bufev, short error, void *data) { struct session *sc; struct peer *p; struct timeval tv; socklen_t addrlen; trace("network_handle_peer_connect() called"); if (error & EVBUFFER_TIMEOUT) errx(1, "timeout"); if (error & EVBUFFER_EOF) errx(1, "eof"); sc = data; p = network_peer_create(); p->sc = sc; addrlen = sizeof(p->sa); trace("network_handle_peer_connect() accepting connection"); if ((p->connfd = accept(sc->servfd, (struct sockaddr *) &p->sa, &addrlen)) == -1) { trace("network_handle_peer_connect() accept error"); network_peer_free(p); return; } trace("network_handle_peer_connect() accepted peer: %s:%d", inet_ntoa(p->sa.sin_addr), ntohs(p->sa.sin_port)); p->state |= PEER_STATE_HANDSHAKE1; p->bufev = bufferevent_new(p->connfd, network_handle_peer_response, network_handle_peer_write, network_handle_peer_error, p); if (p->bufev == NULL) errx(1, "network_announce: bufferevent_new failure"); bufferevent_enable(p->bufev, EV_READ|EV_WRITE); /* set up keep-alive timer */ timerclear(&tv); tv.tv_sec = 1; evtimer_set(&p->keepalive_event, network_peer_keepalive, p); evtimer_add(&p->keepalive_event, &tv); trace("network_handle_peer_connect() initiating handshake"); TAILQ_INSERT_TAIL(&sc->peers, p, peer_list); sc->num_peers++; network_peer_handshake(sc, p); bufferevent_enable(bufev, EV_READ); } /* * network_connect_tracker() * * Connects socket to a tracker. */ int network_connect_tracker(const char *host, const char *port) { struct addrinfo hints, *res, *res0; int error, sockfd; memset(&hints, 0, sizeof(hints)); /* I think that this is the only place where we should actually * have to resolve host names. The getaddrinfo() calls elsewhere * should be very fast. */ hints.ai_family = PF_INET; hints.ai_socktype = SOCK_STREAM; trace("network_connect_tracker() calling getaddrinfo() for host: %s port: %s", host, port); /* XXX cache thiS, OR PERhaps use evdns */ error = getaddrinfo(host, port, &hints, &res0); if (error) { trace("network_connect_tracker(): %s", gai_strerror(error)); return (-1); } /* assume first address is ok */ res = res0; trace("network_connect_tracker() calling network_connect()"); sockfd = network_connect(res->ai_family, res->ai_socktype, res->ai_protocol, res->ai_addr, res->ai_addrlen); freeaddrinfo(res0); return (sockfd); } /* * network_piece_dl_find() * * Search our binary tree for the correct index and offset of the piece dl, * and return the result. */ struct piece_dl * network_piece_dl_find(struct session *sc, struct peer *p, u_int32_t idx, u_int32_t off) { struct piece_dl *pd; struct piece_dl_idxnode find, *res; /* if a peer has been supplied, we should simply search its list for this piece. */ if (p != NULL) { TAILQ_FOREACH(pd, &p->peer_piece_dls, peer_piece_dl_list) { if (pd->off == off && pd->idx == idx) { return (pd); } } } find.off = off; find.idx = idx; if ((res = RB_FIND(piece_dl_by_idxoff, &sc->piece_dl_by_idxoff, &find)) == NULL) return (NULL); if (TAILQ_EMPTY(&res->idxnode_piece_dls)) return (NULL); /* XXX: for now, return the first piece_dl in the peice_dl_idxnode's list. * later, uniqueness of piece_dl by their index and offset will not be * assumed and we will have to mroe properly handle this */ return (TAILQ_FIRST(&res->idxnode_piece_dls)); } /* * network_peer_create() * * Creates a fresh peer object, initialising state and * data structures. */ struct peer * network_peer_create(void) { struct peer *p; p = xmalloc(sizeof(*p)); memset(p, 0, sizeof(*p)); TAILQ_INIT(&p->peer_piece_dls); TAILQ_INIT(&p->peer_piece_uls); /* peers start in choked state */ p->state |= PEER_STATE_CHOKED; p->state |= PEER_STATE_AMCHOKING; return (p); } /* * network_peer_free() * * Single function to free a peer correctly. Includes walking * its piece dl list and marking any entries as 'orphaned'. */ void network_peer_free(struct peer *p) { struct piece_dl *pd, *nxtpd; struct piece_ul *pu, *nxtpu; /* search the piece dl list for any dls associated with this peer */ for (pd = TAILQ_FIRST(&p->peer_piece_dls); pd; pd = nxtpd) { nxtpd = TAILQ_NEXT(pd, peer_piece_dl_list); pd->pc = NULL; TAILQ_REMOVE(&p->peer_piece_dls, pd, peer_piece_dl_list); /* unless this is completed, remove it from the btree */ if (pd->len != pd->bytes) network_piece_dl_free(p->sc, pd); } /* search the piece ul list for any uls associated with this peer */ for (pu = TAILQ_FIRST(&p->peer_piece_uls); pu; pu = nxtpu) { nxtpu = TAILQ_NEXT(pu, peer_piece_ul_list); pu->pc = NULL; TAILQ_REMOVE(&p->peer_piece_uls, pu, peer_piece_ul_list); xfree(pu); } if (p->bufev != NULL && p->bufev->enabled & EV_WRITE) { bufferevent_disable(p->bufev, EV_WRITE|EV_READ); bufferevent_free(p->bufev); p->bufev = NULL; } if (p->rxmsg != NULL) xfree(p->rxmsg); if (p->bitfield != NULL) xfree(p->bitfield); if (p->connfd != 0) { (void) close(p->connfd); p->connfd = 0; } evtimer_del(&p->keepalive_event); xfree(p); p = NULL; } /* * network_peer_piece_ul_enqueue() * * This function creates an enqueues a piece request for a peer. */ struct piece_ul * network_piece_ul_enqueue(struct peer *p, u_int32_t idx, u_int32_t off, u_int32_t len) { struct piece_ul *pu; pu = xmalloc(sizeof(*pu)); memset(pu, 0, sizeof(*pu)); pu->pc = p; pu->idx = idx; pu->off = off; pu->len = len; TAILQ_INSERT_TAIL(&p->peer_piece_uls, pu, peer_piece_ul_list); return (pu); } /* * network_peer_piece_ul_dequeue() * * This function dequeues and removes a piece request from a peer. */ struct piece_ul * network_piece_ul_dequeue(struct peer *p) { struct piece_ul *pu; if ((pu = TAILQ_FIRST(&p->peer_piece_uls)) != NULL) { TAILQ_REMOVE(&p->peer_piece_uls, pu, peer_piece_ul_list); return (pu); } return (NULL); } unworkable-0.53/SConstruct0000644000014500017510000000767011061406265015144 0ustar michaelstaff# scons (http://www.scons.org) build for non-OpenBSD systems # on OpenBSD, just type 'make'. # $Id: SConstruct,v 1.17 2008-09-09 05:42:13 niallo Exp $ import sys SRCS = ['announce.c', 'bencode.c', 'buf.c', 'ctl_server.c', 'main.c', 'network.c', 'parse.y', 'progressmeter.c', \ 'scheduler.c', 'torrent.c', 'trace.c', 'util.c', 'xmalloc.c'] LIBS = ['event', 'crypto'] LIBPATH = ['/usr/lib', '/usr/local/lib'] CPPPATH = ['/usr/include', '/usr/local/include'] CCFLAGS = ['-Wall', '-Wstrict-prototypes', '-Wmissing-prototypes', '-Wmissing-declarations', '-Wshadow', '-Wpointer-arith', '-Wcast-qual', '-Wsign-compare', '-g', '-ggdb'] # Assume this is Solaris with packages from www.sunfreeware.com if sys.platform.startswith('sunos'): SRCS.append('openbsd-compat/err.c') SRCS.append('openbsd-compat/errx.c') SRCS.append('openbsd-compat/warn.c') SRCS.append('openbsd-compat/warnx.c') SRCS.append('openbsd-compat/verr.c') SRCS.append('openbsd-compat/verrx.c') SRCS.append('openbsd-compat/vwarnx.c') SRCS.append('openbsd-compat/vwarn.c') CPPPATH.append('/usr/local/ssl/include') CPPPATH.append('openbsd-compat/') LIBPATH.append('/usr/local/ssl/lib') LIBPATH.append('/usr/ucblib') LIBS.append('socket') LIBS.append('nsl') LIBS.append('ucb') CCFLAGS.append('-DNO_ERR') # Assume this is Mac OS X with macports, so stuff is under /opt elif sys.platform.startswith('darwin'): LIBPATH.append('/opt/local/lib') CPPPATH.append('/opt/local/include') # Explicitly make off_t 64bit on Linux elif sys.platform.startswith('linux'): CCFLAGS.append('-D_FILE_OFFSET_BITS=64') env = Environment(LIBPATH=LIBPATH, CPPPATH=CPPPATH) conf = Configure(env) if not conf.CheckType('u_int8_t', '#include \n'): CCFLAGS.append('-Du_int8_t=unsigned char') if not conf.CheckType('u_int32_t', '#include \n'): CCFLAGS.append('-Du_int32_t=unsigned int') if not conf.CheckType('u_int64_t', '#include \n'): CCFLAGS.append('-Du_int64_t=unsigned long long') if not conf.CheckType('int64_t', '#include \n'): CCFLAGS.append('-Dint64_t=long long') if not conf.CheckCHeader('openssl/bn.h'): print "No openssl/bn.h found. Do you have the OpenSSL headers correctly installed?" Exit(1) if not conf.CheckCHeader('openssl/dh.h'): print "No openssl/dh.h found. Do you have the OpenSSL headers correctly installed?" Exit(1) if not conf.CheckCHeader('openssl/engine.h'): print "No openssl/engine.h found. Do you have the OpenSSL headers correctly installed?" Exit(1) if not conf.CheckCHeader('sys/queue.h') or not conf.CheckCHeader('sys/tree.h') \ or not conf.CheckCHeader('sha1.h'): print "Missing some headers, using bundled includes" CPPPATH.append('openbsd-compat/') if not conf.CheckFunc('strlcpy'): print "No system strlcpy found. Using bundled version" SRCS.append('openbsd-compat/strlcpy.c') conf.env.Append(CCFLAGS = '-DNO_STRLCPY') CCFLAGS.append('-DNO_STRLCPY') if not conf.CheckFunc('strlcat'): print "No system strlcat found. Using bundled version" SRCS.append('openbsd-compat/strlcat.c') CCFLAGS.append('-DNO_STRLCAT') if not conf.CheckFunc('strtonum'): print "No system strtonum found. Using bundled version" SRCS.append('openbsd-compat/strtonum.c') CCFLAGS.append('-DNO_STRTONUM') if not conf.CheckFunc('SHA1Update'): print "No system SHA1Update found. Using bundled version" SRCS.append('openbsd-compat/sha1.c') if not conf.CheckFunc('getaddrinfo') and not sys.platform.startswith('sunos'): print "No system getaddrinfo() found. Using bundled version" SRCS.append('openbsd-compat/getaddrinfo.c') CCFLAGS.append('-DNO_GETADDRINFO') if not conf.CheckLib('crypto'): print "OpenSSL crypto library not found on your system. You can get it at http://www.openssl.org" Exit(1) if not conf.CheckLib('event'): print "Libevent not found on your system. You can get it at http://monkey.org/~provos/libevent/" Exit(1) env = conf.Finish() env.Program('unworkable', SRCS, LIBS=LIBS, LIBPATH=LIBPATH, CPPPATH=CPPPATH, CCFLAGS=CCFLAGS) unworkable-0.53/xmalloc.c0000644000014500017510000000366711061134670014715 0ustar michaelstaff/* $Id: xmalloc.c,v 1.9 2008-09-08 05:35:52 niallo Exp $ */ /* * Copyright (c) 2006, 2008 Niall O'Higgins * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include "includes.h" #define OOM_MSG "Out of memory" #if !defined (USE_BOEHM_GC) void * xmalloc(size_t size) { void *ptr; if ((ptr = malloc(size)) == NULL) err(1, OOM_MSG); return (ptr); } void * xrealloc(void *ptr, size_t size) { void *nptr; if ((nptr = realloc(ptr, size)) == NULL) err(1, OOM_MSG); return (nptr); } void * xcalloc(size_t nmemb, size_t size) { void *ptr; ptr = calloc(nmemb, size); if (ptr == NULL) err(1, OOM_MSG); return ptr; } void xfree(void *ptr) { if (ptr == NULL) errx(1, "xfree: NULL pointer given as argument"); free(ptr); } char * xstrdup(const char *str) { size_t len; char *cp; len = strlen(str) + 1; cp = xmalloc(len); if (strlcpy(cp, str, len) >= len) errx(1, "xstrdup: string truncated"); return cp; } #else /* Special for compiling with Boehm's GC. See Makefile and xmalloc.h */ char * gc_strdup(const char *x) { char *y = xmalloc(strlen(x) + 1); /* XXX ja ja, should check return value... */ strlcpy(y, x, strlen(x) + 1); return (y); } #endif /* WITH_BOEHM_GC */ unworkable-0.53/announce.c0000644000014500017510000002766111071201274015060 0ustar michaelstaff/* $Id: announce.c,v 1.12 2008-10-02 17:19:56 niallo Exp $ */ /* * Copyright (c) 2006, 2007, 2008 Niall O'Higgins * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include /* solaris 10 */ #if defined(__SVR4) && defined(__sun) #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "includes.h" static void announce_update(int, short, void *); static void handle_announce_response(struct bufferevent *, void *); static void handle_announce_error(struct bufferevent *, short, void *); static void handle_announce_write(struct bufferevent *, void *); /* * announce() * * This sends an announce request to a tracker over HTTP. */ int announce(struct session *sc, const char *event) { int i, l; size_t n; char host[MAXHOSTNAMELEN], port[6], path[MAXPATHLEN], *c; char *params, *tparams, *request; char tbuf[3*SHA1_DIGEST_LENGTH+1]; char pbuf[3*PEER_ID_LEN+1]; struct bufferevent *bufev; trace("announce"); sc->last_announce = time(NULL); params = xmalloc(GETSTRINGLEN); tparams = xmalloc(GETSTRINGLEN); request = xmalloc(GETSTRINGLEN); memset(params, '\0', GETSTRINGLEN); memset(tparams, '\0', GETSTRINGLEN); memset(request, '\0', GETSTRINGLEN); /* convert binary info hash to url encoded format */ for (i = 0; i < SHA1_DIGEST_LENGTH; i++) { l = snprintf(&tbuf[3*i], sizeof(tbuf), "%%%02x", sc->tp->info_hash[i]); if (l == -1 || l >= (int)sizeof(tbuf)) goto trunc; } /* convert peer id to url encoded format */ for (i = 0; i < PEER_ID_LEN; i++) { l = snprintf(&pbuf[3*i], sizeof(pbuf), "%%%02x", sc->peerid[i]); if (l == -1 || l >= (int)sizeof(pbuf)) goto trunc; } /* XXX: need support for announce-list */ /* separate out hostname, port and path */ if ((c = strstr(sc->tp->announce, "http://")) == NULL) errx(1, "unsupported announce protocol: %s", sc->tp->announce); c += HTTPLEN; n = strcspn(c, ":/"); if (n > sizeof(host) - 1) errx(1, "n is greater than sizeof(host) - 1"); memcpy(host, c, n); host[n] = '\0'; c += n; if (*c == ':') { c++; n = strcspn(c, "/"); if (n > sizeof(port)) { errx(1, "n is greater than sizeof(port)"); } memcpy(port, c, n); port[n] = '\0'; } else { if (strlcpy(port, "80", sizeof(port)) >= sizeof(port)) errx(1, "string truncation"); n = 0; } c += n; if (strlcpy(path, c, sizeof(path)) >= sizeof(path)) errx(1, "string truncation"); /* strip trailing slash */ if (path[strlen(path) - 1] == '/') path[strlen(path) - 1] = '\0'; /* build params string */ l = snprintf(params, GETSTRINGLEN, "?info_hash=%s" "&peer_id=%s" "&port=%s" "&uploaded=%jd" "&downloaded=%jd" "&left=%jd" "&compact=1", tbuf, pbuf, sc->port, (intmax_t)sc->tp->uploaded, (intmax_t)sc->tp->downloaded, (intmax_t)sc->tp->left); if (l == -1 || l >= GETSTRINGLEN) goto trunc; /* these parts are optional */ if (event != NULL) { strlcpy(tparams, params, GETSTRINGLEN); l = snprintf(params, GETSTRINGLEN, "%s&event=%s", tparams, event); if (l == -1 || l >= GETSTRINGLEN) goto trunc; } /* While OpenBSD's snprintf doesn't mind snprintf(X, len, "%sblah", X) * others don't like this, so I do the strlcpy and use the temporary * buffer tparams. */ if (sc->ip != NULL) { strlcpy(tparams, params, GETSTRINGLEN); l = snprintf(params, GETSTRINGLEN, "%s&ip=%s", tparams, sc->ip); if (l == -1 || l >= GETSTRINGLEN) goto trunc; } if (sc->numwant != NULL) { strlcpy(tparams, params, GETSTRINGLEN); l = snprintf(params, GETSTRINGLEN, "%s&numwant=%s", params, sc->numwant); if (l == -1 || l >= GETSTRINGLEN) goto trunc; } if (sc->key != NULL) { strlcpy(tparams, params, GETSTRINGLEN); l = snprintf(params, GETSTRINGLEN, "%s&key=%s", params, sc->key); if (l == -1 || l >= GETSTRINGLEN) goto trunc; } if (sc->trackerid != NULL) { strlcpy(tparams, params, GETSTRINGLEN); l = snprintf(params, GETSTRINGLEN, "%s&trackerid=%s", params, sc->trackerid); if (l == -1 || l >= GETSTRINGLEN) goto trunc; } l = snprintf(request, GETSTRINGLEN, "GET %s%s HTTP/1.0\r\nHost: %s\r\nUser-agent: Unworkable/%s\r\n\r\n", path, params, host, UNWORKABLE_VERSION); if (l == -1 || l >= GETSTRINGLEN) goto trunc; trace("announce() to host: %s on port: %s", host, port); trace("announce() request: %s", request); /* non blocking connect ? */ if ((sc->connfd = network_connect_tracker(host, port)) == -1) err(1, "tracker announce"); sc->request = request; sc->res = xmalloc(sizeof *sc->res); memset(sc->res, 0, sizeof *sc->res); sc->res->rxmsg = xmalloc(RESBUFLEN); sc->res->rxmsglen = RESBUFLEN; sc->announce_underway = 1; bufev = bufferevent_new(sc->connfd, handle_announce_response, handle_announce_write, handle_announce_error, sc); if (bufev == NULL) errx(1, "announce: bufferevent_new failure"); bufferevent_enable(bufev, EV_READ); trace("announce() writing to socket"); if (bufferevent_write(bufev, request, strlen(request) + 1) != 0) errx(1, "announce: bufferevent_write failure"); xfree(params); xfree(request); trace("announce() done"); return (0); trunc: trace("announce: string truncation detected"); xfree(params); xfree(request); xfree(tparams); return (-1); } /* * handle_announce_response() * * When data is ready on the announce socket, this is called to buffer it up. */ static void handle_announce_response(struct bufferevent *bufev, void *arg) { size_t len; struct session *sc; sc = arg; trace("handle_announce_response() reading buffer"); /* within 256 bytes of filling up our buffer - grow it */ if (sc->res->rxmsglen <= sc->res->rxread + 256) { sc->res->rxmsglen += RESBUFLEN; sc->res->rxmsg = xrealloc(sc->res->rxmsg, sc->res->rxmsglen); } len = bufferevent_read(bufev, sc->res->rxmsg + sc->res->rxread, 256); sc->res->rxread += len; trace("handle_announce_response() read %u", len); } /* * handle_announce_error() * * Called when the announce request socket is closed by the other * side - ie when the HTTP request has completed. Handles all the announce * response parsing. */ static void handle_announce_error(struct bufferevent *bufev, short error, void *data) { struct session *sc = data; struct benc_node *node, *troot; struct torrent *tp; struct bufferevent *bev; struct timeval tv; BUF *buf = NULL; u_int32_t l; size_t len; u_int8_t *c, *dump; trace("handle_announce_error() called"); /* shouldn't have to worry about this case */ if (sc->res == NULL) { sc->announce_underway = 0; return; } /* still could be data left for reading */ do { l = sc->res->rxread; handle_announce_response(bufev, sc); } while (sc->res->rxread - l > 0); /* XXX: this shouldn't happen - need to look into why it does */ if (sc->res->rxread == 0) { sc->announce_underway = 0; return; } tp = sc->tp; if (error & EVBUFFER_TIMEOUT) errx(1, "handle_announce_error() TIMOUT (unexpected)"); c = sc->res->rxmsg; /* XXX: need HTTP/1.1 support - tricky part is chunked encoding I think */ if (strncmp(c, HTTP_1_0, strlen(HTTP_1_0)) != 0 && strncmp(c, HTTP_1_1, strlen(HTTP_1_1))) { warnx("handle_announce_error: server did not send a valid HTTP/1.0 response"); goto err; } c += strlen(HTTP_1_0) + 1; if (strncmp(c, HTTP_OK, strlen(HTTP_OK)) != 0) { *(c + strlen(HTTP_OK)) = '\0'; warnx("handle_announce_error: HTTP response indicates error (code: %s)", c); goto err; } c = strstr(c, HTTP_END); if (c == NULL) { warnx("handle_announce_error: HTTP response had no content"); goto err; } c += strlen(HTTP_END); if ((buf = buf_alloc(128, BUF_AUTOEXT)) == NULL) errx(1,"handle_announce_error: could not allocate buffer"); len = sc->res->rxread - (c - sc->res->rxmsg); buf_set(buf, c, len, 0); dump = xmalloc(len + 1); memcpy(dump, c, len); dump[len] = '\0'; trace("announce response: %s", dump); xfree(dump); trace("handle_announce_error() bencode parsing buffer"); troot = benc_root_create(); if ((troot = benc_parse_buf(buf, troot)) == NULL) errx(1,"handle_announce_error: HTTP response parsing failed (no peers?)"); /* check for a b-encoded failure response */ if ((node = benc_node_find(troot, "failure reason")) != NULL) { if (!(node->flags & BSTRING)) trace("unspecified tracker failure"); trace("tracker failure: %s", node->body.string.value); goto err; } if ((node = benc_node_find(troot, "interval")) == NULL) { tp->interval = DEFAULT_ANNOUNCE_INTERVAL; } else { if (!(node->flags & BINT)) errx(1, "interval is not a number"); tp->interval = node->body.number; } if ((node = benc_node_find(troot, "complete")) != NULL) { if (!(node->flags & BINT)) errx(1, "complete is not a number"); tp->complete = node->body.number; } if ((node = benc_node_find(troot, "incomplete")) != NULL) { if (!(node->flags & BINT)) errx(1, "incomplete is not a number"); tp->incomplete = node->body.number; } if ((node = benc_node_find(troot, "peers")) == NULL) { trace("no peers field"); goto err; } trace("handle_announce_error() updating peerlist"); network_peerlist_update(sc, node); benc_node_freeall(troot); troot = NULL; trace("handle_announce_error() setting announce timer"); timerclear(&tv); tv.tv_sec = tp->interval; evtimer_del(&sc->announce_event); evtimer_set(&sc->announce_event, announce_update, sc); evtimer_add(&sc->announce_event, &tv); if (sc->servfd == 0) { trace("handle_announce_error() setting up server socket"); /* time to set up the server socket */ if (sc->port != NULL) { sc->servfd = network_listen("0.0.0.0", sc->port); bev = bufferevent_new(sc->servfd, NULL, NULL, network_handle_peer_connect, sc); if (bufev == NULL) errx(1, "handle_announce_error: bufferevent_new failure"); bufferevent_enable(bev, EV_PERSIST|EV_READ); } /* now that we've announced, kick off the scheduler */ trace("handle_announce_error() setting up scheduler"); timerclear(&tv); tv.tv_sec = 1; evtimer_set(&sc->scheduler_event, scheduler, sc); evtimer_add(&sc->scheduler_event, &tv); } err: bufferevent_free(bufev); bufev = NULL; if (buf != NULL) buf_free(buf); if (sc->res != NULL) { xfree(sc->res->rxmsg); xfree(sc->res); sc->res = NULL; } (void) close(sc->connfd); trace("handle_announce_error() done"); sc->announce_underway = 0; } /* * announce_update() * * Called at announce interval to send a fresh announce * to tracker if necessary. */ static void announce_update(int fd, short type, void *arg) { struct session *sc = arg; struct timeval tv; trace("announce_update() called"); if (!sc->announce_underway) announce(sc, NULL); else trace("announce_update() announce already underway"); timerclear(&tv); tv.tv_sec = sc->tp->interval; evtimer_set(&sc->announce_event, announce_update, sc); evtimer_add(&sc->announce_event, &tv); } /* * announce_handle_write() * * Write handler for announce http request socket. */ static void handle_announce_write(struct bufferevent *bufev, void *data) { trace("handle_announce_write() called"); } unworkable-0.53/bencode.c0000644000014500017510000001004411077423373014650 0ustar michaelstaff/* $Id: bencode.c,v 1.41 2008-10-21 19:01:15 niallo Exp $ */ /* * Copyright (c) 2006, 2007, 2008 Niall O'Higgins * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include "includes.h" #define IS_CONTAINER_TYPE(x) \ (x->flags & BDICT || x->flags & BLIST) struct benc_node *root; /* * benc_node_add() * * Add new node to tail of node's child queue. */ void benc_node_add(struct benc_node *node, struct benc_node *new) { TAILQ_INSERT_TAIL(&node->children, new, benc_nodes); } /* * benc_node_add_head() * * Add new node to head of node's child queue */ void benc_node_add_head(struct benc_node *node, struct benc_node *new) { TAILQ_INSERT_HEAD(&node->children, new, benc_nodes); } /* * benc_node_create() * * Create and initialise a benc_node */ struct benc_node * benc_node_create(void) { struct benc_node *node; node = xmalloc(sizeof(*node)); memset(node, 0, sizeof(*node)); TAILQ_INIT(&(node->children)); return (node); } /* * benc_node_find() * * Find BDICT_ENTRY node with specified key. */ struct benc_node * benc_node_find(struct benc_node *node, char *key) { struct benc_node *childnode, *ret; if (node->flags & BDICT_ENTRY && strcmp(key, node->body.dict_entry.key) == 0) return (node->body.dict_entry.value); if (node->flags & BDICT_ENTRY && IS_CONTAINER_TYPE(node)) TAILQ_FOREACH(childnode, &node->body.dict_entry.value->children, benc_nodes) if ((ret = benc_node_find(childnode, key)) != NULL) return (ret); if (IS_CONTAINER_TYPE(node)) TAILQ_FOREACH(childnode, &node->children, benc_nodes) if ((ret = benc_node_find(childnode, key)) != NULL) return (ret); return (NULL); } /* * benc_node_print() * * Pretty-print a node tree. */ void benc_node_print(struct benc_node *node, int level) { struct benc_node *childnode; int i; for (i = 0; i < level; i++) printf("\t"); if (node->flags & BDICT_ENTRY) { printf("key: %s", node->body.dict_entry.key); benc_node_print(node->body.dict_entry.value, level); } else if (node->flags & BSTRING) { printf("string len: %zu value: %s\n", node->body.string.len, node->body.string.value); } else if (node->flags & BINT) { printf("int value: %lld\n", node->body.number); } if (node->flags & BLIST) { printf("blist\n"); TAILQ_FOREACH(childnode, &node->children, benc_nodes) benc_node_print(childnode, level + 1); } if (node->flags & BDICT) { printf("bdict, end: %zu\n", node->end); TAILQ_FOREACH(childnode, &node->children, benc_nodes) benc_node_print(childnode, level + 1); } } /* * benc_node_freeall() * * Walk the tree from this node down, freeing everything. */ void benc_node_freeall(struct benc_node *node) { struct benc_node *childnode; if (node->flags & BDICT_ENTRY) { xfree(node->body.dict_entry.key); benc_node_freeall(node->body.dict_entry.value); } else if (node->flags & BSTRING) { xfree(node->body.string.value); } if (IS_CONTAINER_TYPE(node)) { while ((childnode = TAILQ_FIRST(&node->children)) != NULL) { TAILQ_REMOVE(&node->children, childnode, benc_nodes); benc_node_freeall(childnode); } } if (node != NULL) xfree(node); } /* * benc_root_create() * * Create a root node, which parser needs to be passed during init */ struct benc_node * benc_root_create(void) { struct benc_node *n = benc_node_create(); n->flags = BLIST; return (n); } unworkable-0.53/util.c0000644000014500017510000001006311131521557014221 0ustar michaelstaff/* $Id: util.c,v 1.8 2009-01-09 01:07:59 niallo Exp $ */ /* * Copyright (c) 2006, 2007, 2008 Niall O'Higgins * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include "includes.h" int mkpath(const char *s, mode_t mode){ char *q, *r = NULL, *path = NULL, *up = NULL; int rv; rv = -1; if (strcmp(s, ".") == 0 || strcmp(s, "/") == 0) return 0; path = xstrdup(s); q = xstrdup(s); if ((r = dirname(q)) == NULL) goto out; up = xstrdup(r); if ((mkpath(up, mode) == -1) && (errno != EEXIST)) goto out; if ((mkdir(path, mode) == -1) && (errno != EEXIST)) rv = -1; else rv = 0; out: if (up != NULL) xfree(up); xfree(q); xfree(path); return (rv); } void print_len(void *ptr, size_t len) { char *out, *p; size_t i; out = xmalloc(len + 3); memset(out, '\0', len + 3); p = (char *)ptr; for (i = 0; i < len; i++) { snprintf(out, len+3, "%s%c", out, *p); p++; } printf("print_len: %s\n", out); } /* $OpenBSD: progressmeter.c,v 1.5 2007/12/03 21:07:31 niallo Exp $ */ /* * Copyright (c) 2006 Damien Miller. All rights reserved. * Copyright (c) 2005 Anil Madhavapeddy. All rights reserved. * Copyright (c) 1995,1999 Theo de Raadt. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* * ensure all of data on socket comes through. f==read || f==vwrite */ size_t atomicio(ssize_t (*f) (int, void *, size_t), int fd, void *_s, size_t n) { char *s = _s; size_t pos = 0; ssize_t res; while (n > pos) { res = (f) (fd, s + pos, n - pos); switch (res) { case -1: if (errno == EINTR || errno == EAGAIN) continue; return 0; case 0: errno = EPIPE; return pos; default: pos += (size_t)res; } } return (pos); } void util_setbit(u_int8_t *bitfield, u_int32_t bit) { u_int32_t byte; /* which byte is this bit in (divide by 8) */ byte = bit >> 3u; bitfield[byte] |= 1 << (7u - (bit & 7u)); } int util_getbit(u_int8_t *bitfield, u_int32_t bit) { u_int32_t byte; /* which byte is this bit in (divide by 8) */ byte = bit >> 3u; return ((bitfield[byte] & (1u << (7u - (bit & 7u)))) != 0); } unworkable-0.53/buf.c0000644000014500017510000001524011061134670014020 0ustar michaelstaff/* $Id: buf.c,v 1.11 2008-09-08 05:35:52 niallo Exp $ */ /* * Copyright (c) 2003 Jean-Francois Brousseau * Copyright (c) 2006, 2007, 2008 Niall O'Higgins * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include "includes.h" #define BUF_INCR 128 struct buf { u_int cb_flags; /* buffer handle and size */ u_char *cb_buf; size_t cb_size; /* start and length of valid data in buffer */ u_char *cb_cur; size_t cb_len; size_t cb_pos; }; #define SIZE_LEFT(b) (b->cb_size - (size_t)(b->cb_cur - b->cb_buf) \ - b->cb_len) static void buf_grow(BUF *, size_t); /* * buf_alloc() * * Create a new buffer structure and return a pointer to it. This structure * uses dynamically-allocated memory and must be freed with buf_free(), * once the buffer is no longer needed. */ BUF * buf_alloc(size_t len, u_int flags) { BUF *b; b = xmalloc(sizeof(*b)); /* Postpone creation of zero-sized buffers */ if (len > 0) b->cb_buf = xcalloc(1, len); else b->cb_buf = NULL; b->cb_flags = flags; b->cb_size = len; b->cb_cur = b->cb_buf; b->cb_len = 0; b->cb_pos = 0; return (b); } /* * buf_load() * * Open the file specified by and load all of its contents into a * buffer. * Returns the loaded buffer on success. */ BUF * buf_load(const char *path, u_int flags) { int fd; ssize_t ret; size_t len; u_char *bp; struct stat st; BUF *buf; if ((fd = open(path, O_RDONLY, 0600)) == -1) { warn("%s", path); return (NULL); } if (fstat(fd, &st) == -1) err(1, "%s", path); buf = buf_alloc((size_t)st.st_size, flags); for (bp = buf->cb_cur; ; bp += (size_t)ret) { len = SIZE_LEFT(buf); ret = read(fd, bp, len); if (ret == -1) { buf_free(buf); err(1, "buf_load"); } else if (ret == 0) break; buf->cb_len += (size_t)ret; } (void)close(fd); return (buf); } /* * buf_free() * * Free the buffer and all associated data. */ void buf_free(BUF *b) { if (b->cb_buf != NULL) xfree(b->cb_buf); xfree(b); } /* * buf_release() * * Free the buffer 's structural information but do not free the contents * of the buffer. Instead, they are returned and should be freed later using * xfree(). */ void * buf_release(BUF *b) { u_char *tmp; tmp = b->cb_buf; xfree(b); return (tmp); } /* * buf_set() * * Set the contents of the buffer at offset to the first * bytes of data found at . If the buffer was not created with * BUF_AUTOEXT, as many bytes as possible will be copied in the buffer. */ ssize_t buf_set(BUF *b, const void *src, size_t len, size_t off) { size_t rlen = 0; if (b->cb_size < (len + off)) { if ((b->cb_flags & BUF_AUTOEXT)) { buf_grow(b, len + off - b->cb_size); rlen = len + off; } else { rlen = b->cb_size - off; } } else { rlen = len; } b->cb_len = rlen; memcpy((b->cb_buf + off), src, rlen); if (b->cb_len == 0) { b->cb_cur = b->cb_buf + off; b->cb_len = rlen; } return (rlen); } /* * buf_getc() * * Return u_char at buffer position . * */ int buf_getc(BUF *b) { int c; if (b->cb_pos < b->cb_len) { c = b->cb_cur[b->cb_pos]; b->cb_pos++; return (c); } else { return (EOF); } } void buf_ungetc(BUF *b) { if (b->cb_len > 0) b->cb_pos--; } /* * buf_len() * * Returns the size of the buffer that is being used. */ size_t buf_len(BUF *b) { return (b->cb_len); } /* * buf_write_fd() * * Write the contents of the buffer to the specified */ int buf_write_fd(BUF *b, int fd) { u_char *bp; size_t len; ssize_t ret; len = b->cb_len; bp = b->cb_cur; do { ret = write(fd, bp, len); if (ret == -1) { if (errno == EINTR || errno == EAGAIN) continue; return (-1); } len -= (size_t)ret; bp += (size_t)ret; } while (len > 0); return (0); } /* * buf_write() * * Write the contents of the buffer to the file whose path is given in * . If the file does not exist, it is created with mode . */ int buf_write(BUF *b, const char *path, mode_t mode) { int fd; open: if ((fd = open(path, O_WRONLY|O_CREAT|O_TRUNC, mode)) == -1) { if (errno == EACCES && unlink(path) != -1) goto open; else err(1, "%s", path); } if (buf_write_fd(b, fd) == -1) { (void)unlink(path); errx(1, "buf_write: buf_write_fd: `%s'", path); } if (fchmod(fd, mode) < 0) warn("permissions not set on file %s", path); (void)close(fd); return (0); } /* * buf_write_stmp() * * Write the contents of the buffer to a temporary file whose path is * specified using