X-Git-Url: http://git.cascardo.eti.br/?a=blobdiff_plain;f=rnetclient.c;h=04869fddaead4210e3feaad5e727c25b026f4c1d;hb=42160b3dd99ae655bf9bd274f3f33c681e4262a1;hp=2b98cc409adf915dfde4212d3128e46d2c9a6511;hpb=7421958f9ba1677463f8944edf0103f7841db604;p=cascardo%2Frnetclient.git diff --git a/rnetclient.c b/rnetclient.c index 2b98cc4..04869fd 100644 --- a/rnetclient.c +++ b/rnetclient.c @@ -31,6 +31,10 @@ #include "rnet_message.h" #include "rnet_encode.h" +static size_t chars2len (unsigned char buf[2]) { + return (buf[0] << 8 | buf[1]); +} + static void * get_creds(char *certfile) { static gnutls_certificate_credentials_t cred; @@ -94,7 +98,7 @@ static int inflateRecord(char *buffer, size_t len, char **out, size_t *olen) zstrm.opaque = Z_NULL; if ((r = inflateInit(&zstrm)) != Z_OK) return -1; - *olen = (buffer[3] << 8 | buffer[4]); + *olen = chars2len(buffer+3); *out = malloc(*olen); if (!out) { inflateEnd(&zstrm); @@ -157,8 +161,12 @@ static int handshake(int c) char buffer[16]; int r; buffer[0] = 1; - write(c, buffer, 1); - write(c, "00000000000000", 14); + r = write(c, buffer, 1); + if (r < 1) + return -1; + r = write(c, "00000000000000", 14); + if (r < 14) + return -1; r = read(c, buffer, 1); if (r != 1 && buffer[0] != 'E') return -1; @@ -176,12 +184,51 @@ static void usage(void) static int rnet_send(gnutls_session_t session, char *buffer, size_t len, int header) { - char *out; - size_t olen; - deflateRecord(buffer, len, &out, &olen, header); - gnutls_record_send(session, out, olen); - free(out); - return 0; + int r = 0; + /* Large files have to be uploaded as multiple + separately-deflated chunks, because the compressed and + uncompressed lengths in each record are encoded in unsigned + 16-bit integers each. + + The header can't be split into multiple chunks, and it + should never have to, since it won't ever get even close to + 64KiB. + + The uploaded file may be larger: to upload such large + files, it suffices to send multiple records till the entire + file is transferred, without waiting for a response. Since + we've alread informed the server of the file size in the + header, it knows exactly how much data to expect before + sending a response. It will only send an error message + before that if it times us out. + + Odds are that any reasonably large size will do, but it + can't be too close to 64KiB, otherwise there won't be room + for the compressed length should it not compress well, + which should never happen for capital-ASCII-only + declaration files, but who knows? + + This chunk size worked at the first try, uploading a + ~100KiB file, so let's stick with it. */ + const int maxc = 64472; + if (header && len > maxc) + return -1; + + do { + char *out = NULL; + size_t olen; + size_t clen = len < maxc ? len : maxc; + r = deflateRecord(buffer, clen, &out, &olen, header); + if (!r) { + size_t n = gnutls_record_send(session, out, olen); + if (n != olen) + r = -1; + } + free(out); + buffer += clen; + len -= clen; + } while (len && !r); + return r; } static int rnet_recv(gnutls_session_t session, struct rnet_message **message) @@ -195,7 +242,7 @@ static int rnet_recv(gnutls_session_t session, struct rnet_message **message) buffer = (*message)->buffer; r = gnutls_record_recv(session, buffer, 6); if (buffer[0] == 0x01) { - len = (buffer[1] << 8 | buffer[2]); + len = chars2len(buffer+1); rnet_message_expand(message, len); buffer = (*message)->buffer + 6; r = gnutls_record_recv(session, buffer, len); @@ -207,7 +254,7 @@ static int rnet_recv(gnutls_session_t session, struct rnet_message **message) (*message)->len = olen; free(out); } else { - len = (buffer[1] << 8 | buffer[2]); + len = chars2len(buffer+1); rnet_message_expand(message, len - 1); buffer = (*message)->buffer + 6; r = gnutls_record_recv(session, buffer, len - 1); @@ -311,7 +358,7 @@ int main(int argc, char **argv) r == EAI_SYSTEM ? strerror(errno) : gai_strerror(r)); exit(1); } - gnutls_transport_set_ptr(session, (gnutls_transport_ptr_t) c); + gnutls_transport_set_ptr(session, (gnutls_transport_ptr_t)(intptr_t) c); r = handshake(c); if (r < 0) { exit(1);