From: Richard Kettlewell Date: Sat, 14 Mar 2009 10:42:26 +0000 (+0000) Subject: Merge from uaudio branch. X-Git-Tag: 5.0~178 X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~mdw/git/disorder/commitdiff_plain/d76bbdea349bf3856172ce9599e657d566e91162?hp=89156c8c5c4bf2b751869ff53e006ac3a67b029f Merge from uaudio branch. In summary: - disorder-speaker now uses uaudio and should have more reliable gapless play - volume setting now uses uaudio - the command backend now uses the same scheduling mechanism as rtp, and sends silence for pauses - multicast API fixes - gcrypt API usage fixes --- diff --git a/CHANGES.html b/CHANGES.html index 3722b61..fe3c687 100644 --- a/CHANGES.html +++ b/CHANGES.html @@ -62,15 +62,29 @@ span.command {
-

Mac OS X

- +

Server

+
-

The device configuration option and --device option - to disorder-playrtp now work. Devices may be specified either - by UID or name. Fixes The device configuration option work under OS X. Devices may + be specified either by UID or name. Fixes Issue 27.

+ +

Confirmation URLs for online registrations are cleaner now.

+ +
+ +

RTP Player

+ +
+ +

There is a new --command option which allows the RTP player + to send audio data to a user-chosen command instead of an audio API. See + the man page for details.

+ +

The --device option to disorder-playrtp now works + under OS X (as above).

diff --git a/README.upgrades b/README.upgrades index c586dd8..cbe8637 100644 --- a/README.upgrades +++ b/README.upgrades @@ -17,6 +17,14 @@ all 1.1.x versions. If you install from .deb files then much of this work is automated. +* 4.x -> 4.4 + +The syntax of confirmation strings for online registrations has changed and old +ones no longer work. This only affects users who registered before the upgrade +but have not yet confirmed their login. You can delete such half-created users +with 'disorder deluser USERNAME' (as an administrative user, for instance as +root on the server) and they can start the registration process again. + * 3.0 -> 4.x If you customized any of the templates, you will pretty much have to start from diff --git a/debian/postrm.disorder-server b/debian/postrm.disorder-server index 7ba0933..4cd590a 100755 --- a/debian/postrm.disorder-server +++ b/debian/postrm.disorder-server @@ -30,6 +30,7 @@ cleanup_remove() { rm -f $state/__db.* rm -f $state/noticed.db rm -f $state/search.db + rm -f $state/isearch.db rm -f $state/tags.db rm -f $state/tracks.db } @@ -43,6 +44,7 @@ cleanup_purge() { rm -f $state/prefs.db rm -f $state/schedule.db rm -f $state/users.db + rm -f $state/playlists.db } case "$1" in diff --git a/lib/basen.c b/lib/basen.c index 87d2795..7c1f9ec 100644 --- a/lib/basen.c +++ b/lib/basen.c @@ -31,7 +31,7 @@ * @param nwords Length of bignum * @return !v */ -static int zero(const unsigned long *v, int nwords) { +static int zero(const uint32_t *v, int nwords) { int n; for(n = 0; n < nwords && !v[n]; ++n) @@ -47,7 +47,7 @@ static int zero(const unsigned long *v, int nwords) { * * The quotient is stored in @p v. */ -static unsigned divide(unsigned long *v, int nwords, unsigned long m) { +static unsigned divide(uint32_t *v, int nwords, unsigned long m) { unsigned long r = 0, a, b; int n; @@ -66,6 +66,31 @@ static unsigned divide(unsigned long *v, int nwords, unsigned long m) { return r; } +/** @brief Multiple v by m and add a + * @param v Pointer to bigendian bignum + * @param nwords Length of bignum + * @param m Value to multiply by + * @param a Value to add + * @return 0 on success, non-0 on overflow + * + * Does v = m * v + a. + */ +static int mla(uint32_t *v, int nwords, uint32_t m, uint32_t a) { + int n = nwords - 1; + uint32_t carry = a; + + while(n >= 0) { + const uint64_t p = (uint64_t)v[n] * m + carry; + carry = (uint32_t)(p >> 32); + v[n] = (uint32_t)p; + --n; + } + /* If there is still a carry then we overflowed */ + return !!carry; +} + +static const char basen_chars[] = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; + /** @brief Convert v to a chosen base * @param v Pointer to bigendian bignum (modified!) * @param nwords Length of bignum @@ -75,26 +100,53 @@ static unsigned divide(unsigned long *v, int nwords, unsigned long m) { * @return 0 on success, -1 if the buffer is too small * * Converts @p v to a string in the given base using decimal digits, lower case - * letter sand upper case letters as digits. + * letters and upper case letters as digits. + * + * The inverse of nesab(). */ -int basen(unsigned long *v, +int basen(uint32_t *v, int nwords, char buffer[], size_t bufsize, unsigned base) { size_t i = bufsize; - static const char chars[] = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; do { if(i <= 1) return -1; /* overflow */ - buffer[--i] = chars[divide(v, nwords, base)]; + buffer[--i] = basen_chars[divide(v, nwords, base)]; } while(!zero(v, nwords)); memmove(buffer, buffer + i, bufsize - i); buffer[bufsize - i] = 0; return 0; } +/** @brief Convert a string back to a large integer in an arbitrary base + * @param v Where to store result as a bigendian bignum + * @param nwords Space available in @p v + * @param s Input string + * @param base Number base (2..62) + * @return 0 on success, non-0 on overflow or invalid input + * + * The inverse of basen(). If the number is much smaller than the buffer then + * the first words will be 0. + */ +int nesab(uint32_t *v, + int nwords, + const char *s, + unsigned base) { + /* Initialize to 0 */ + memset(v, 0, nwords * sizeof *v); + while(*s) { + const char *dp = strchr(basen_chars, *s++); + if(!dp) + return -1; + if(mla(v, nwords, base, dp - basen_chars)) + return -1; + } + return 0; +} + /* Local Variables: c-basic-offset:2 diff --git a/lib/basen.h b/lib/basen.h index 717f2ea..c1a60f0 100644 --- a/lib/basen.h +++ b/lib/basen.h @@ -19,7 +19,7 @@ #ifndef BASEN_H #define BASEN_H -int basen(unsigned long *v, +int basen(uint32_t *v, int nwords, char buffer[], size_t bufsize, @@ -29,6 +29,11 @@ int basen(unsigned long *v, * Returns 0 on success or -1 on overflow. */ +int nesab(uint32_t *v, + int nwords, + const char *s, + unsigned base); + #endif /* BASEN_H */ /* diff --git a/lib/random.c b/lib/random.c index 3f8f714..d6be3d9 100644 --- a/lib/random.c +++ b/lib/random.c @@ -75,7 +75,7 @@ void random_get(void *ptr, size_t bytes) { /** @brief Return a random ID string */ char *random_id(void) { - unsigned long words[2]; + uint32_t words[2]; char id[128]; random_get(words, sizeof words); diff --git a/libtests/t-basen.c b/libtests/t-basen.c index 715490c..3cbc044 100644 --- a/libtests/t-basen.c +++ b/libtests/t-basen.c @@ -18,12 +18,22 @@ #include "test.h" static void test_basen(void) { - unsigned long v[64]; + uint32_t v[64]; char buffer[1024]; v[0] = 999; insist(basen(v, 1, buffer, sizeof buffer, 10) == 0); check_string(buffer, "999"); + memset(v, 0xFF, sizeof v); + insist(nesab(v, 1, buffer, 10) == 0); + check_integer(v[0], 999); + check_integer(v[1], 0xFFFFFFFF); + insist(nesab(v, 4, buffer, 10) == 0); + check_integer(v[0], 0); + check_integer(v[1], 0); + check_integer(v[2], 0); + check_integer(v[3], 999); + check_integer(v[4], 0xFFFFFFFF); v[0] = 1+2*7+3*7*7+4*7*7*7; insist(basen(v, 1, buffer, sizeof buffer, 7) == 0); @@ -35,6 +45,24 @@ static void test_basen(void) { v[3] = 0x0C0D0E0F; insist(basen(v, 4, buffer, sizeof buffer, 256) == 0); check_string(buffer, "123456789abcdef"); + memset(v, 0xFF, sizeof v); + insist(nesab(v, 4, buffer, 256) == 0); + check_integer(v[0], 0x00010203); + check_integer(v[1], 0x04050607); + check_integer(v[2], 0x08090A0B); + check_integer(v[3], 0x0C0D0E0F); + check_integer(v[4], 0xFFFFFFFF); + memset(v, 0xFF, sizeof v); + insist(nesab(v, 8, buffer, 256) == 0); + check_integer(v[0], 0); + check_integer(v[1], 0); + check_integer(v[2], 0); + check_integer(v[3], 0); + check_integer(v[4], 0x00010203); + check_integer(v[5], 0x04050607); + check_integer(v[6], 0x08090A0B); + check_integer(v[7], 0x0C0D0E0F); + check_integer(v[8], 0xFFFFFFFF); v[0] = 0x00010203; v[1] = 0x04050607; @@ -42,6 +70,24 @@ static void test_basen(void) { v[3] = 0x0C0D0E0F; insist(basen(v, 4, buffer, sizeof buffer, 16) == 0); check_string(buffer, "102030405060708090a0b0c0d0e0f"); + memset(v, 0xFF, sizeof v); + insist(nesab(v, 4, buffer, 16) == 0); + check_integer(v[0], 0x00010203); + check_integer(v[1], 0x04050607); + check_integer(v[2], 0x08090A0B); + check_integer(v[3], 0x0C0D0E0F); + check_integer(v[4], 0xFFFFFFFF); + memset(v, 0xFF, sizeof v); + insist(nesab(v, 8, buffer, 16) == 0); + check_integer(v[0], 0); + check_integer(v[1], 0); + check_integer(v[2], 0); + check_integer(v[3], 0); + check_integer(v[4], 0x00010203); + check_integer(v[5], 0x04050607); + check_integer(v[6], 0x08090A0B); + check_integer(v[7], 0x0C0D0E0F); + check_integer(v[8], 0xFFFFFFFF); v[0] = 0x00010203; v[1] = 0x04050607; diff --git a/server/server.c b/server/server.c index b00a70e..767fa31 100644 --- a/server/server.c +++ b/server/server.c @@ -17,13 +17,18 @@ */ #include "disorder-server.h" +#include "basen.h" #ifndef NONCE_SIZE # define NONCE_SIZE 16 #endif #ifndef CONFIRM_SIZE -# define CONFIRM_SIZE 10 +/** @brief Size of nonce in confirmation string in 32-bit words + * + * 64 bits gives 11 digits (in base 62). + */ +# define CONFIRM_SIZE 2 #endif int volume_left, volume_right; /* last known volume */ @@ -1319,30 +1324,23 @@ static int c_users(struct conn *c, return 1; /* completed */ } -/** @brief Base64 mapping table for confirmation strings - * - * This is used with generic_to_base64() and generic_base64(). We cannot use - * the MIME table as that contains '+' and '=' which get quoted when - * URL-encoding. (The CGI still does the URL encoding but it is desirable to - * avoid it being necessary.) - */ -static const char confirm_base64_table[] = - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789/.*"; - static int c_register(struct conn *c, char **vec, int attribute((unused)) nvec) { - char *buf, *cs; - size_t bufsize; - int offset; - - /* The confirmation string is base64(username;nonce) */ - bufsize = strlen(vec[0]) + CONFIRM_SIZE + 2; - buf = xmalloc_noptr(bufsize); - offset = byte_snprintf(buf, bufsize, "%s;", vec[0]); - gcry_randomize(buf + offset, CONFIRM_SIZE, GCRY_STRONG_RANDOM); - cs = generic_to_base64((uint8_t *)buf, offset + CONFIRM_SIZE, - confirm_base64_table); + char *cs; + uint32_t nonce[CONFIRM_SIZE]; + char nonce_str[(32 * CONFIRM_SIZE) / 5 + 1]; + + /* The confirmation string is username/base62(nonce). The confirmation + * process will pick the username back out to identify them but the _whole_ + * string is used as the confirmation string. Base 62 means we used only + * letters and digits, minimizing the chance of the URL being mispasted. */ + gcry_randomize(nonce, sizeof nonce, GCRY_STRONG_RANDOM); + if(basen(nonce, CONFIRM_SIZE, nonce_str, sizeof nonce_str, 62)) { + error(0, "buffer too small encoding confirmation string"); + sink_writes(ev_writer_sink(c->w), "550 Cannot create user\n"); + } + byte_xasprintf(&cs, "%s/%s", vec[0], nonce_str); if(trackdb_adduser(vec[0], vec[1], config->default_rights, vec[2], cs)) sink_writes(ev_writer_sink(c->w), "550 Cannot create user\n"); else @@ -1353,7 +1351,6 @@ static int c_register(struct conn *c, static int c_confirm(struct conn *c, char **vec, int attribute((unused)) nvec) { - size_t nuser; char *user, *sep; rights_type rights; const char *host; @@ -1363,12 +1360,12 @@ static int c_confirm(struct conn *c, sink_writes(ev_writer_sink(c->w), "530 Authentication failure\n"); return 1; } - if(!(user = generic_base64(vec[0], &nuser, confirm_base64_table)) - || !(sep = memchr(user, ';', nuser))) { + /* Picking the LAST / means we don't (here) rule out slashes in usernames. */ + if(!(sep = strrchr(vec[0], '/'))) { sink_writes(ev_writer_sink(c->w), "550 Malformed confirmation string\n"); return 1; } - *sep = 0; + user = xstrndup(vec[0], sep - vec[0]); if(trackdb_confirm(user, vec[0], &rights)) sink_writes(ev_writer_sink(c->w), "550 Incorrect confirmation string\n"); else {