From e8fcf45f2b8b1e5f4d453ead744b6b07ebea9d10 Mon Sep 17 00:00:00 2001 From: zvezdochiot Date: Fri, 5 May 2023 20:47:16 +0300 Subject: [PATCH] 1.23.1: update --- COPYING | 2 +- ChangeLog | 38 ++++++ HACKING | 277 ------------------------------------- Makefile.am | 2 +- README | 14 +- configure.ac | 55 +++----- doc/gendoc.c | 2 +- doc/ncdc.pod.in | 73 +++++++++- src/bloom.c | 2 +- src/cc.c | 76 +++++++---- src/commands.c | 4 +- src/db.c | 29 ++-- src/dl.c | 20 ++- src/dlfile.c | 22 +-- src/doc.h | 44 +++--- src/fl_load.c | 88 +++++++----- src/fl_local.c | 35 +++-- src/fl_save.c | 2 +- src/fl_util.c | 2 +- src/geoip.c | 95 +++++++------ src/hub.c | 133 +++++++++++++----- src/listen.c | 2 +- src/main.c | 32 +---- src/ncdc.h | 53 +------- src/net.c | 77 +++++++---- src/proto.c | 2 +- src/search.c | 4 +- src/strutil.c | 2 +- src/tth.c | 2 +- src/ui.c | 12 +- src/ui_colors.c | 2 +- src/ui_listing.c | 2 +- src/ui_logwindow.c | 2 +- src/ui_textinput.c | 2 +- src/uit_conn.c | 26 ++-- src/uit_dl.c | 10 +- src/uit_fl.c | 10 +- src/uit_hub.c | 8 +- src/uit_main.c | 4 +- src/uit_msg.c | 2 +- src/uit_search.c | 10 +- src/uit_userlist.c | 6 +- src/util.c | 49 ++++--- src/vars.c | 15 ++- static/build.sh | 330 --------------------------------------------- 45 files changed, 637 insertions(+), 1042 deletions(-) delete mode 100644 HACKING delete mode 100755 static/build.sh diff --git a/COPYING b/COPYING index e574e72..b1ac9b0 100644 --- a/COPYING +++ b/COPYING @@ -1,4 +1,4 @@ -Copyright (c) 2011-2014 Yoran Heling +Copyright (c) 2011-2022 Yoran Heling Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the diff --git a/ChangeLog b/ChangeLog index ca41515..9b783ca 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,41 @@ +1.23.1 - 2022-09-27 + - Fix buffer overflow on connections tab for incoming IPv6 connections in handshake phase + +1.23 - 2022-05-30 + - Bump minimum glib version to 2.32 + - Re-open GeoIP database on SIGUSR1 + - Add tls_policy=force setting + - Fix TLS on Verlihub + - Various minor language fixes + - Add workaround rare compiler bug for aarch64 + +1.22.1 - 2019-06-03 + - Fix segfault with ADC client connections + +1.22 - 2019-04-30 + - Add 'b' and 'B' keys to connections tab (Daniel Kamil Kozar) + - Add 'max_ul_per_user' setting to support multiple upload slots per user (Daniel Kamil Kozar) + - Add support for TLS ALPN (Denys Smirnov) + - Fix build against ncurses with separate libtinfo (Lars Wendler) + +1.21 - 2019-03-26 + - Switch to libmaxminddb for GeoIP lookups + - Replaced 'geoip_cc4' and 'geoip_cc6' settings with a single 'geoip_cc' + - Mark already queued or shared files in search and file browser (Daniel Kamil Kozar) + - Add 'download_shared' setting (Daniel Kamil Kozar) + - Add 'show_free_slots' setting (Daniel Kamil Kozar) + - Add support for the "Free Slots" ADC extension + +1.20 - 2016-12-30 + - Support bracketed paste mode in input handling (cologic) + - Add 'geoip_cc4' and 'geoip_cc6' settings + - Add 'log_hubchat' setting + - Add 'local' option to 'active_ip' setting + - Add support for multistream bzip2 filelists + - Disable RC4 ciphers by default from tls_priority + - Fix potential null pointer deference + - Fix chmod of destination directories (Johannes Beisswenger) + 1.19.1 - 2014-04-23 - Fix remote null pointer dereference - Searching now works in the search results list diff --git a/HACKING b/HACKING deleted file mode 100644 index c1eb1aa..0000000 --- a/HACKING +++ /dev/null @@ -1,277 +0,0 @@ -This is a POD file. If you prefer reading a man page, run `perldoc ./HACKING`. -Or use `pod2text HACKING` to generate an easier to read text file. Both of -these programs should come with a default Perl installation. - - - -=head1 DEBUGGING - -=head2 General - -Any debugging messages are written to C<~/.ncdc/stderr.log>. Note that this -file is emptied every time ncdc starts up, so be sure to either make backups or -check the contents of the log file before this happens! - -Messages starting with C almost always indicate a bug. If you find -one, please report it! Messages starting with C can happen both -because something unexpected happened or because of a bug. If you see such a -message along the lines of "assertion failure" or "line should not be reached", -then this always indicates a bug. - -By default, debug-level messages are not logged, enable the I setting -within ncdc to get these as well. Be warned, however, that depending on how you -use ncdc, this may generate a lot of data. My ncdc generates about 1 GiB per -week, for instance. - - -=head2 GDB - -GDB can be used to debug various kinds of crashes (but not all of them), -deadlocks or to find out exactly when a C or C message is -being logged. Note that you usually want ncdc to be compiled with debugging -symbols when using GDB. Compile as follows: - - ./configure CFLAGS=-g && make clean && make - -How you run gdb depends on the kind of problem you are experiencing. Here are -some tips for various situations: - -=over - -=item ncdc crashes shortly after startup. - -The easiest way is to just start ncdc within gdb directly. - - $ gdb /path/to/ncdc - [..] - > run - -=item ncdc crashes after I perform a certain action. - -Running ncdc within gdb as above is in that case not very optimal, since the -gdb messages may garble the screen and make it annoying to use ncdc. In this -case it's easier attach gdb to a running ncdc. To do so, run ncdc as you -usually would, and then run the following in a different terminal: - - $ gdb /path/to/ncdc $ncdc_pid - [..] - > continue - -=item ncdc crashes randomly after a long period of time. - -In this case it may be easier to use core dumps, as follows: - - $ ulimit -c unlimited - $ ncdc # run ncdc as you usually would - [let it crash] - $ gdb /path/to/ncdc core - -=back - -The above tips work nice for debugging crashes, but gdb can also be used to -debug when a C or C message is logged. Run ncdc with the -I environment variable set to either I or -I. For example, the following will let ncdc crash as soon as a -C message is logged. - - $ G_DEBUG=fatal-warnings /path/to/ncdc - -If you happened to get gdb to see the crash, then you can use gdb commands to -fetch more information on the crash. There are many online resources on -debugging using gdb, but here are a few important commands: - -=over - -=item bt - -Get a backtrace. - -=item info threads - -List the active threads. - -=item thread $number - -Switch to another thread. - -=back - -In the off chance that you happened to have found a deadlock, these are easiest -to debug by attaching gdb to a deadlocked ncdc. Running ncdc within gdb or -using core dumps isn't very useful in these situations. - - -=head2 Valgrind - -Valgrind can be used to debug crashes for which GDB fails. Bugs caused by -incorrect handling of memory are very easily detected with valgrind. A major -downside, however, is that ncdc takes approximately 10 times as much memory and -is 10 times slower than when run natively or in GDB. As with GDB, make sure you -have ncdc compiled with debugging symbols. To run ncdc in valgrind, use the -following command: - - $ G_DEBUG=gc-friendly G_SLICE=always-malloc valgrind --leak-check=full\ - --num-callers=25 --log-file=valgrindlog /path/to/ncdc - -And any issue valgrind can find will be written to the file 'valgrindlog'. Note -that some of the reported problems, especially memory leaks, may not -necessarily come from ncdc itself, but are harmless issues within glib or other -libraries. - - - - - -=head1 IDENTIFIERS - -Ncdc uses all kinds of identifiers internally, let's explain some of them. - -=over - -=item hubid - -A "hub id" is 64bit integer that uniquely identifies a hub. The number is -randomly generated and assigned upon the first run of C for a hub with -that name. The use of hub IDs allow a user to change the name of a hub tab -without compromising any other IDs or related configuration. - -=item CID / PID - -These are defined in the ADC spec. A CID uniquely identifies a user across -multiple hubs, but only works on the ADC protocol. Ncdc doesn't try to assign a -CID to users on NMDC hubs. - -=item uid - -A user id, internal to ncdc. This uniquely identifies a single user on a single -hub. It's a 64bit unsigned integer, generated by taking the first 8 bytes of a -tiger hash. The data that is hashed depends on the protocol, for ADC it's -C, where C is the base32-encoded CID of the user. For -NMDC, it's C, where C is the name of the user as -sent by the hub (thus in the encoding that the hub uses). In both cases, the -byte representation of is hashed, so these IDs are dependent on the -byte order of the CPU architecture. - -This ID is used everywhere where a user should be identified, and is also -stored on disk in the database file (in the download queue) and are used as the -filename for user file lists in the C directory. They are usually -represented in ASCII as 16-character HEX values. - -=back - - - - - - -=head1 SQLITE SCHEMA - -This is the SQL schema used to store stuff in the db.sqlite3 file. C is set to 1. Note that this schema does not include foreign key -clauses or other checks, in order to improve portability with older SQLite -versions. - -=head2 Config & variables - - CREATE TABLE vars ( - name TEXT NOT NULL, - hub INTEGER NOT NULL DEFAULT 0, - value TEXT NOT NULL, - PRIMARY KEY(name, hub) - ); - -Stores key-value pairs for configuration data and various other variables that -need to be kept around for more than a single run. For global variables, C -is 0. For hub-local variables, C is a random 64-bit integer. It is treated -as unsigned in the code, but stored signed in the database. For every existing -value of C, there should be a I key indicating the name that -belongs to the hub tab, including the preceding C<#> character. - - CREATE TABLE share ( - name TEXT NOT NULL PRIMARY KEY, - path TEXT NOT NULL - ); - -Stores the shared directories. C is the virtual name, C is the -absolute filesystem path obtained by C. - - CREATE TABLE users ( - hub INTEGER NOT NULL, - uid INTEGER NOT NULL, - nick TEXT NOT NULL, - flags INTEGER NOT NULL - ) - -Stores information about "special" users. Currently only used for users who are -granted a slot. The C column is set, but its value is not currently used. -Matching is instead done on the C column. C is a bit mask of -flags, the flag for a granted slot is 1. - -=head2 Hash data - - CREATE TABLE hashdata ( - root TEXT NOT NULL PRIMARY KEY, - size INTEGER NOT NULL, - tthl BLOB NOT NULL - ); - -Unsurprisingly, this stores the hash data of shared files. C is the TTH -root, encoded in base32. C is the size of the file and C is the TTH -data. - - CREATE TABLE hashfiles ( - id INTEGER PRIMARY KEY, - filename TEXT NOT NULL UNIQUE, - tth TEXT NOT NULL, - lastmod INTEGER NOT NULL - ); - -A mapping of I to I. The C column is an alias for the SQLite -C, and used internally in ncdc to speed up certain operations. -C is the absolute and canonical path to the file, as obtained from -C. C refers to C, and C is the last -modification time, as a UNIX timestamp. - -It is not uncommon to have multiple files with the same TTH. An row in -C should B have a corresponding row in C. It is -possible to have an row in C with no row in C referring to -it, or to have entries in C that are not in a shared directory at -all. These are cleaned up with C. - -=head2 Download queue - - CREATE TABLE dl ( - tth TEXT NOT NULL PRIMARY KEY, - size INTEGER NOT NULL, - dest TEXT NOT NULL, - priority INTEGER NOT NULL DEFAULT 0, - error INTEGER NOT NULL DEFAULT 0, - error_msg TEXT, - tthl BLOB - ); - -Each row represents a file in the download queue. File list downloads are not -included. C is the base32-encoded TTH root of the file, C the file -size, in bytes and C is the full destination path where the file will be -moved to after downloading. Possible values for C are defined in the -C macros in dl.c. Possible C values are defined in the C -macros. C is NULL if there is no error. C is the downloaded -TTH data, NULL if it hasn't been fetched yet. - - CREATE TABLE dl_users ( - tth TEXT NOT NULL, - uid INTEGER NOT NULL, - error INTEGER NOT NULL DEFAULT 0, - error_msg TEXT, - PRIMARY KEY(tth, uid) - ); - -Stores the users from which a download queue item can be downloaded from. -C refers to C
, C is the user id, stored as a 64-bit signed -integer but internally represented as an unsigned integer in ncdc. C and - have the same meaning as for the C table, but obviously -represent errors that only affect the user rather than the file. (e.g. when the -file is not available from this user). - -It is possible for a C
row to have no corresponding rows in C, -but a C row must always refer to a row in C
. diff --git a/Makefile.am b/Makefile.am index 371be16..5fe0eeb 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,6 +1,6 @@ EXTRA_DIST=ChangeLog noinst_PROGRAMS= -AM_CFLAGS=$(GLIB_CFLAGS) $(GNUTLS_CFLAGS) $(SQLITE_CFLAGS) +AM_CFLAGS=${NCURSES_CFLAGS} $(GLIB_CFLAGS) $(GNUTLS_CFLAGS) $(SQLITE_CFLAGS) AM_CPPFLAGS=-I$(builddir)/src -I$(srcdir)/deps -I$(srcdir)/deps/ylib diff --git a/README b/README index e9308f7..b7e3ee0 100644 --- a/README +++ b/README @@ -1,4 +1,4 @@ -ncdc 1.19.1 +ncdc 1.23.1 =========== DESCRIPTION @@ -7,7 +7,7 @@ DESCRIPTION ncurses interface. Always make sure you run the latest version. You can check for updates and - find more information at http://dev.yorhel.nl/ncdc + find more information at https://dev.yorhel.nl/ncdc @@ -18,11 +18,9 @@ REQUIREMENTS zlib bzip2 sqlite >= 3.3.9 - glib >= 2.24.0 - gnutls >= 2.4.0 - geoip >= 1.2.0 (optional) - - If your gnutls is older than 3.0, you also need libgcrypt. + glib >= 2.32.0 + gnutls >= 3.0 + libmaxminddb (optional) @@ -76,6 +74,6 @@ CROSS COMPILING CONTACT Email: projects@yorhel.nl - Web: http://dev.yorhel.nl/ncdc + Web: https://dev.yorhel.nl/ncdc DC: adc://dc.blicky.net:2780/ or adcs://dc.blicky.net:2780/ diff --git a/configure.ac b/configure.ac index 435a213..4fcc96d 100644 --- a/configure.ac +++ b/configure.ac @@ -1,7 +1,7 @@ -AC_INIT([ncdc], [1.19.1], [projects@yorhel.nl]) +AC_INIT([ncdc],[1.23.1],[projects@yorhel.nl]) AC_CONFIG_SRCDIR([src/ncdc.h]) -AC_CONFIG_HEADER([config.h]) +AC_CONFIG_HEADERS([config.h]) m4_include([deps/lean.m4]) AM_INIT_AUTOMAKE([foreign subdir-objects]) PKG_PROG_PKG_CONFIG([0.18]) @@ -30,12 +30,6 @@ AM_CONDITIONAL([HAVE_MH], [test "x$have_mh" = "xyes"]) # Check for header files. AC_CHECK_HEADERS([zlib.h bzlib.h],[], AC_MSG_ERROR([Required header file not found])) -have_nch=no -AC_CHECK_HEADERS([ncursesw/ncurses.h ncurses/ncurses.h ncurses.h],[have_nch=yes]) -if test "x$have_nch" = "xno"; then - AC_MSG_ERROR([Could not find a header files for ncurses.]) -fi - # Check for posix_fadvise() AC_CHECK_FUNCS([posix_fadvise]) @@ -95,15 +89,11 @@ esac # Check for ncurses -AC_CHECK_LIB([ncursesw], [get_wch], [ - AC_SUBST([NCURSES_LIBS],[-lncursesw]) - ], [ - AC_CHECK_LIB([ncurses], - [get_wch], - [AC_SUBST([NCURSES_LIBS],[-lncurses])], - [AC_MSG_ERROR(ncursesw library is required)]) - ] -) +PKG_CHECK_MODULES(NCURSES, ncursesw,,[ + PKG_CHECK_MODULES(NCURSES, ncurses,,[ + AC_MSG_ERROR(ncurses library is required) + ]) +]) # Check for zlib AC_CHECK_LIB([z], @@ -131,35 +121,21 @@ PKG_CHECK_EXISTS([sqlite3],[ ) # Check for modules -PKG_CHECK_MODULES([GLIB], [glib-2.0 >= 2.24 gthread-2.0]) -PKG_CHECK_MODULES([GNUTLS], [gnutls >= 2.4]) +PKG_CHECK_MODULES([GLIB], [glib-2.0 >= 2.32 gthread-2.0]) +PKG_CHECK_MODULES([GNUTLS], [gnutls >= 3.0]) AC_ARG_WITH([geoip], [AS_HELP_STRING([--with-geoip], [support for IP-to-country lookups @<:@default=no@:>@])], [], [with_geoip=no]) -AS_IF([test "x$with_geoip" == xyes], +AS_IF([test "x$with_geoip" = xyes], [PKG_CHECK_MODULES([GEOIP], - [geoip >= 1.2.0], - [AC_DEFINE(USE_GEOIP, 1, [Use libgeoip for IP-to-country lookups])])]) - - -# Note that GnuTLS 2.12 also supports the nettle backend, and already provides -# an API to the low-level crypto functions. However, if GnuTLS uses the gcrypt -# backend, we still need to initialize gcrypt for threading. Since we have no -# reliable way to detect which backend is used, let's just assume gcrypt. And -# while we're linking against gcrypt anyway, we might as well use it for -# crypto, too. -PKG_CHECK_EXISTS([gnutls >= 3.0], [], [ - AC_CHECK_LIB([gcrypt], - [gcry_control], - [AC_SUBST([GCRYPT_LIBS], [-lgcrypt])], - [AC_MSG_ERROR([GnuTLS version is older than 3.0, but no libgcrypt found])]) - AC_DEFINE(USE_GCRYPT, 1, [Define to use libgcrypt for crypto]) -]) + [libmaxminddb >= 1.0], + [AC_DEFINE(USE_GEOIP, 1, [Use libmaxminddb for IP-to-country lookups])])]) + -# Check whether we should use the version string from AC_INIT(), or use +# Check whether we should use the version string from AC_INIT, or use # git-describe to create one. This trick is copied from the pacman source. AC_ARG_ENABLE(git-version, @@ -188,4 +164,5 @@ fi AM_CONDITIONAL([INSTALL_MANPAGE], [test "x$installmanpage" = "xyes"]) -AC_OUTPUT([Makefile]) +AC_CONFIG_FILES([Makefile]) +AC_OUTPUT diff --git a/doc/gendoc.c b/doc/gendoc.c index 0d5f41d..81926d1 100644 --- a/doc/gendoc.c +++ b/doc/gendoc.c @@ -1,6 +1,6 @@ /* ncdc - NCurses Direct Connect client - Copyright (c) 2011-2014 Yoran Heling + Copyright (c) 2011-2019 Yoran Heling Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the diff --git a/doc/ncdc.pod.in b/doc/ncdc.pod.in index 505afe2..62d42f2 100644 --- a/doc/ncdc.pod.in +++ b/doc/ncdc.pod.in @@ -168,6 +168,73 @@ C command, and is reproduced below. @keys@ +=head1 WEIRD UI FLAGS + +Some listings have a flags display, and their meaning may not be immediately obvious. + +=head2 File status + +You may see one-letter flags to the left of file names in search results and +file list tabs. Their meaning is as follows: + +=over + +=item H + +The file had been added to the file list, but has not been hashed yet and thus +is not visible to others. This flag can appear only when browsing your own list. + +=item S + +The file is already in your share. If C is set to C, +trying to download it will result only in an informational message. This flag +never appears when browsing your own file list. + +=item Q + +The file is currently in your download queue. Trying to download it will result +only in an informational message. + +=back + +=head2 Connection flags + +On the connection list window, there are two flags: + +=over + +=item S + +Connection state, can be either Bonnecting, Bandshake, Bdle, +Bownloading, Bploading or B<-> for disconnected. + +=item t + +This means that the connection is encrypted with TLS. + +=back + +=head2 User flags + +The user list has some flags too: + +=over + +=item o + +Set if the user is an operator in the hub. + +=item p + +When the user is passive. If this flag is not set, the user is active. + +=item t + +Set if connections with this user will be encrypted with TLS. + +=back + + =head1 ENVIRONMENT $NCDC_DIR is used to determine the session dir, it is only honoured if I<-c> is @@ -321,7 +388,7 @@ see the same uploaded file several times with a different file offset. =head1 LICENSE -Copyright (C) 2011-2013 Yoran Heling +Copyright (C) 2011-2019 Yoran Heling ncdc is distributed under the MIT license, please read the COPYING file for more information. @@ -329,7 +396,7 @@ more information. =head1 BUGS Please report bugs or feature requests to the bug tracker or the mailing list. -Both can be found on the ncdc homepage at L. There +Both can be found on the ncdc homepage at L. There is also an ADC hub available at C for general support and discussions. @@ -337,4 +404,4 @@ support and discussions. ncdc is written by Yoran Heling -Web: L +Web: L diff --git a/src/bloom.c b/src/bloom.c index e70bf4e..9f7bf24 100644 --- a/src/bloom.c +++ b/src/bloom.c @@ -1,6 +1,6 @@ /* ncdc - NCurses Direct Connect client - Copyright (c) 2011-2014 Yoran Heling + Copyright (c) 2011-2022 Yoran Heling Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the diff --git a/src/cc.c b/src/cc.c index 2ed09e8..afb3805 100644 --- a/src/cc.c +++ b/src/cc.c @@ -1,6 +1,6 @@ /* ncdc - NCurses Direct Connect client - Copyright (c) 2011-2014 Yoran Heling + Copyright (c) 2011-2022 Yoran Heling Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the @@ -470,15 +470,20 @@ static void xfer_log_add(cc_t *cc) { } -// Returns the cc object of a connection with the same user, if there is one. -static cc_t *cc_check_dupe(cc_t *cc) { +// Returns true if the connection doesn't exceed maximum per-user connection limits. +static gboolean cc_should_allow_connection(cc_t *cc) { GSequenceIter *i = g_sequence_get_begin_iter(cc_list); + int current_conns = 0; + const int max_conns = cc->dl ? 1 : var_get_int(cc->hub->id, VAR_max_ul_per_user); for(; !g_sequence_iter_is_end(i); i=g_sequence_iter_next(i)) { cc_t *c = g_sequence_get(i); - if(cc != c && c->state != CCS_DISCONN && !!c->adc == !!cc->adc && c->uid == cc->uid) - return c; + if(cc != c && c->state != CCS_DISCONN && !!c->adc == !!cc->adc && !!c->dl == !!cc->dl + && (c->cid && cc->cid ? strcmp(c->cid, cc->cid) == 0 : c->uid == cc->uid)) + ++current_conns; + if(current_conns >= max_conns) + return false; } - return NULL; + return true; } @@ -786,9 +791,11 @@ static void handle_adcget(cc_t *cc, char *type, char *id, guint64 start, gint64 } -// To be called when we know with which user and on which hub this connection is. +// To be called when we know with which user and on which hub this connection +// is. May be called multiple times for the same connection. static void handle_id(cc_t *cc, hub_user_t *u) { - cc->nick = g_strdup(u->name); + if(!cc->nick) + cc->nick = g_strdup(u->name); cc->isop = u->isop; cc->uid = u->uid; @@ -812,16 +819,27 @@ static void handle_id(cc_t *cc, hub_user_t *u) { } } - // Don't allow multiple connections with the same user for the same purpose + // Check the number of connections with the same user for the same purpose // (up/down). For NMDC, the purpose of this connection is determined when we // receive a $Direction, so it's only checked here for ADC. - if(cc->adc) { - cc_t *dup = cc_check_dupe(cc); - if(dup && !!cc->dl == !!dup->dl) { - g_set_error_literal(&(cc->err), 1, 0, "too many open connections with this user"); - cc_disconnect(cc, FALSE); - return; - } + if(cc->adc && !cc_should_allow_connection(cc)) { + g_set_error_literal(&(cc->err), 1, 0, "too many open connections with this user"); + cc_disconnect(cc, FALSE); + return; + } + + // Because tls_policy is a hub-local setting, we can only verify TLS settings + // for incoming connections once we know from which hub this connection came. + // The check is perhaps a bit late, but better disconnect late than never. + if(cc->tls && var_get_int(cc->hub->id, VAR_tls_policy) == VAR_TLSP_DISABLE) { + g_set_error_literal(&(cc->err), 1, 0, "TLS connection"); + cc_disconnect(cc, FALSE); + return; + } + if(!cc->tls && var_get_int(cc->hub->id, VAR_tls_policy) == VAR_TLSP_FORCE) { + g_set_error_literal(&(cc->err), 1, 0, "non-TLS connection"); + cc_disconnect(cc, FALSE); + return; } cc->slot_granted = db_users_get(u->hub->id, u->name) & DB_USERFLAG_GRANT ? TRUE : FALSE; @@ -897,20 +915,21 @@ static void adc_handle(net_t *net, char *msg, int _len) { g_message("CC:%s: Incorrect CID: %s", net_remoteaddr(cc->net), msg); cc_disconnect(cc, TRUE); break; - } else if(cc->active) { - cc->token = g_strdup(token); + } else { cc->cid = g_strdup(id); - cc_expect_adc_rm(cc, 0); + if(cc->active) { + cc->token = g_strdup(token); + cc_expect_adc_rm(cc, 0); + } hub_user_t *u = cc->uid ? g_hash_table_lookup(hub_uids, &cc->uid) : NULL; if(!u) { g_set_error_literal(&cc->err, 1, 0, "Protocol error."); g_message("CC:%s: Unexpected ADC connection: %s", net_remoteaddr(cc->net), msg); cc_disconnect(cc, TRUE); break; - } else - handle_id(cc, u); - } else - cc->cid = g_strdup(id); + } + handle_id(cc, u); + } // Perform keyprint validation // TODO: Throw an error if kp_user is set but we've not received a kp_real? if(cc->kp_real && cc->kp_user && memcmp(cc->kp_real, cc->kp_user, 32) != 0) { @@ -1123,9 +1142,8 @@ static void nmdc_direction(cc_t *cc, gboolean down, int num) { } else cc->dl = cc->dir > num; - // Now that this connection has a purpose, make sure it's the only connection with that purpose. - cc_t *dup = cc_check_dupe(cc); - if(dup && !!cc->dl == !!dup->dl) { + // Now that this connection has a purpose, check the connection number limit. + if(!cc_should_allow_connection(cc)) { g_set_error_literal(&cc->err, 1, 0, "Too many open connections with this user"); cc_disconnect(cc, FALSE); return; @@ -1351,7 +1369,7 @@ cc_t *cc_create(hub_t *hub) { // Simply stores the keyprint of the certificate in cc->kp_real, it will be // checked when receiving CINF. -static void handle_handshake(net_t *n, const char *kpr) { +static void handle_handshake(net_t *n, const char *kpr, int proto) { cc_t *c = net_handle(n); if(kpr) { if(!c->kp_real) @@ -1375,7 +1393,7 @@ static void handle_connect(net_t *n, const char *addr) { } if(cc->tls) - net_settls(cc->net, FALSE, handle_handshake); + net_settls(cc->net, FALSE, FALSE, handle_handshake); if(!net_is_connected(cc->net)) return; @@ -1445,7 +1463,7 @@ static void handle_detectprotocol(net_t *net, char *dat, int len) { // Enable TLS if(!cc->tls && *dat >= 0x14 && *dat <= 0x17) { cc->tls = TRUE; - net_settls(cc->net, TRUE, handle_handshake); + net_settls(cc->net, TRUE, FALSE, handle_handshake); if(net_is_connected(cc->net)) net_peekbytes(cc->net, 1, handle_detectprotocol); // Queue another detectprotocol to detect NMDC/ADC return; diff --git a/src/commands.c b/src/commands.c index 6c83267..11ee788 100644 --- a/src/commands.c +++ b/src/commands.c @@ -1,6 +1,6 @@ /* ncdc - NCurses Direct Connect client - Copyright (c) 2011-2014 Yoran Heling + Copyright (c) 2011-2022 Yoran Heling Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the @@ -585,7 +585,7 @@ static void c_unshare(char *args) { listshares(); return; // otherwise we may crash - } else if(fl_refresh_queue && fl_refresh_queue->head) { + } else if(fl_is_refreshing()) { ui_m(NULL, 0, "Sorry, can't remove directories from the share while refreshing."); return; } diff --git a/src/db.c b/src/db.c index 17941ae..fa26780 100644 --- a/src/db.c +++ b/src/db.c @@ -1,6 +1,6 @@ /* ncdc - NCurses Direct Connect client - Copyright (c) 2011-2014 Yoran Heling + Copyright (c) 2011-2022 Yoran Heling Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the @@ -292,7 +292,7 @@ static int db_queue_process_begin(sqlite3 *db) { static void db_queue_process(sqlite3 *db) { - GTimeVal trans_end = {}; // tv_sec = 0 if no transaction is active + gint64 trans_end = 0; // 0 if no transaction is active gboolean donext = FALSE; gboolean errtrans = FALSE; @@ -302,7 +302,7 @@ static void db_queue_process(sqlite3 *db) { while(1) { char *q = donext ? g_async_queue_try_pop(db_queue) : - trans_end.tv_sec ? g_async_queue_timed_pop(db_queue, &trans_end) : + trans_end ? g_async_queue_timeout_pop(db_queue, trans_end - g_get_monotonic_time()) : g_async_queue_pop(db_queue); int flags = q ? darray_get_int32(q) : 0; @@ -311,9 +311,9 @@ static void db_queue_process(sqlite3 *db) { // Commit state if we need to if(!q || flags & DBF_SINGLE || flags & DBF_END) { g_warn_if_fail(!donext); - if(trans_end.tv_sec) + if(trans_end) db_queue_process_commit(db); - trans_end.tv_sec = 0; + trans_end = 0; donext = errtrans = FALSE; } @@ -343,7 +343,7 @@ static void db_queue_process(sqlite3 *db) { donext = flags & DBF_NEXT ? TRUE : FALSE; if(!donext) { errtrans = FALSE; - trans_end.tv_sec = 0; + trans_end = 0; } g_free(q); continue; @@ -351,15 +351,15 @@ static void db_queue_process(sqlite3 *db) { // handle LAST queries if(flags & DBF_LAST) { - r = db_queue_process_one(db, q, nocache, trans_end.tv_sec?TRUE:FALSE, &res, &lastid); + r = db_queue_process_one(db, q, nocache, trans_end?TRUE:FALSE, &res, &lastid); // Commit first, then send back the final result - if(trans_end.tv_sec) { + if(trans_end) { if(r == SQLITE_DONE) r = db_queue_process_commit(db); if(r != SQLITE_DONE) db_queue_process_rollback(db); } - trans_end.tv_sec = 0; + trans_end = 0; donext = FALSE; db_queue_item_final(res, r, lastid); g_free(q); @@ -367,15 +367,14 @@ static void db_queue_process(sqlite3 *db) { } // start a new transaction for normal/NEXT queries - if(!trans_end.tv_sec) { - g_get_current_time(&trans_end); - g_time_val_add(&trans_end, DB_FLUSH_TIMEOUT); + if(!trans_end) { + trans_end = g_get_monotonic_time() + DB_FLUSH_TIMEOUT; r = db_queue_process_begin(db); if(r != SQLITE_DONE) { if(flags & DBF_NEXT) donext = errtrans = TRUE; else - trans_end.tv_sec = 0; + trans_end = 0; db_queue_item_error(q); g_free(q); continue; @@ -393,7 +392,7 @@ static void db_queue_process(sqlite3 *db) { if(flags & DBF_NEXT) errtrans = TRUE; else - trans_end.tv_sec = 0; + trans_end = 0; } } } @@ -1611,7 +1610,7 @@ void db_init() { // start database thread db_queue = g_async_queue_new(); - db_thread = g_thread_create(db_thread_func, g_build_filename(db_dir, "db.sqlite3", NULL), TRUE, NULL); + db_thread = g_thread_new("database thread", db_thread_func, g_build_filename(db_dir, "db.sqlite3", NULL)); db_init_schema(); } diff --git a/src/dl.c b/src/dl.c index e546462..4b11f82 100644 --- a/src/dl.c +++ b/src/dl.c @@ -1,6 +1,6 @@ /* ncdc - NCurses Direct Connect client - Copyright (c) 2011-2014 Yoran Heling + Copyright (c) 2011-2022 Yoran Heling Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the @@ -128,7 +128,7 @@ struct dl_t { * downloading thread is active and thus do not need synchronisation. These * include dl_t.{size,islist,hash,hash_block,incfd} and possibly more. * TODO: dl.have isn't always protected yet! */ - GStaticMutex lock; + GMutex lock; }; #endif @@ -537,7 +537,7 @@ void dl_queue_addlist(hub_user_t *u, const char *sel, ui_tab_t *parent, gboolean g_return_if_fail(u && u->hasinfo); dl_t *dl = g_slice_new0(dl_t); dl->islist = TRUE; - g_static_mutex_init(&dl->lock); + g_mutex_init(&dl->lock); if(sel) dl->flsel = g_strdup(sel); dl->flpar = parent; @@ -576,7 +576,7 @@ static gboolean dl_queue_addfile(guint64 uid, char *hash, guint64 size, char *fn if(g_hash_table_lookup(dl_queue, hash)) return FALSE; dl_t *dl = g_slice_new0(dl_t); - g_static_mutex_init(&dl->lock); + g_mutex_init(&dl->lock); memcpy(dl->hash, hash, 24); dl->size = size; // Figure out dl->dest @@ -602,6 +602,16 @@ void dl_queue_add_fl(guint64 uid, fl_list_t *fl, char *base, GRegex *excl) { ui_mf(NULL, 0, "Ignoring `%s': excluded by regex.", fl->name); return; } + { + // don't download already shared files if download_shared is set to false. + GSList *localfl = fl_local_from_tth(fl->tth); + if(!var_get_bool(0, VAR_download_shared) && fl->hastth && localfl && localfl->data) { + fl_list_t *localf = localfl->data; + ui_mf(NULL, 0, "Ignoring `%s' : already shared as `%s'", fl->name, localf->name); + return; + } + } + char *name = base ? g_build_filename(base, fl->name, NULL) : g_strdup(fl->name); if(fl->isfile) { @@ -914,7 +924,7 @@ void dl_load_dl(const char *tth, guint64 size, const char *dest, signed char pri g_return_if_fail(dest); dl_t *dl = g_slice_new0(dl_t); - g_static_mutex_init(&dl->lock); + g_mutex_init(&dl->lock); memcpy(dl->hash, tth, 24); dl->size = size; dl->prio = prio; diff --git a/src/dlfile.c b/src/dlfile.c index 5d2e251..3698836 100644 --- a/src/dlfile.c +++ b/src/dlfile.c @@ -1,6 +1,6 @@ /* ncdc - NCurses Direct Connect client - Copyright (c) 2011-2014 Yoran Heling + Copyright (c) 2011-2022 Yoran Heling Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the @@ -119,7 +119,7 @@ static gboolean dlfile_save_bitmap(dl_t *dl, int fd) { static gboolean dlfile_save_bitmap_timeout(gpointer dat) { dl_t *dl = dat; - g_static_mutex_lock(&dl->lock); + g_mutex_lock(&dl->lock); dl->bitmap_src = 0; if(dl->incfd > 0 && !dlfile_save_bitmap(dl, dl->incfd)) { g_warning("Error writing bitmap for `%s': %s.", dl->dest, g_strerror(errno)); @@ -129,7 +129,7 @@ static gboolean dlfile_save_bitmap_timeout(gpointer dat) { close(dl->incfd); dl->incfd = 0; } - g_static_mutex_unlock(&dl->lock); + g_mutex_unlock(&dl->lock); return FALSE; } @@ -418,7 +418,7 @@ void dlfile_finished(dl_t *dl) { /* Create destination directory, if it does not exist yet. */ char *parent = g_path_get_dirname(fdest); - r = g_mkdir_with_parents(parent, 0755); + r = g_mkdir_with_parents(parent, 0777); g_free(parent); if(r < 0) { g_warning("Error creating directory for `%s': %s.", dl->dest, g_strerror(errno)); @@ -483,7 +483,7 @@ dlfile_thread_t *dlfile_getchunk(dl_t *dl, guint64 uid, guint64 speed) { dlfile_thread_t *tsec = NULL; GSList *l; - g_static_mutex_lock(&dl->lock); + g_mutex_lock(&dl->lock); dlfile_threaddump(dl, 1); for(l=dl->threads; l; l=l->next) { dlfile_thread_t *ti = l->data; @@ -531,7 +531,7 @@ dlfile_thread_t *dlfile_getchunk(dl_t *dl, guint64 uid, guint64 speed) { dl->allbusy = !l; dlfile_threaddump(dl, 2); - g_static_mutex_unlock(&dl->lock); + g_mutex_unlock(&dl->lock); g_debug("Allocating: allbusy = %d, chunk = %u, allocated = %u, avail = %u, chunksinblock = %u, chunksinfile = %u", dl->allbusy, t->chunk, t->allocated, t->avail, (guint32)dl->hash_block/DLFILE_CHUNKSIZE, dlfile_chunks(dl->size)); return t; @@ -543,7 +543,7 @@ static gboolean dlfile_recv_check(dlfile_thread_t *t, char *leaf) { if(t->dl->size < t->dl->hash_block ? memcmp(leaf, t->dl->hash, 24) == 0 : db_dl_checkhash(t->dl->hash, num, leaf)) return TRUE; - g_static_mutex_lock(&t->dl->lock); + g_mutex_lock(&t->dl->lock); /* Hash failure, remove the failed block from the bitmap and dl->have, and * reset this thread so that the block can be re-downloaded. */ @@ -560,7 +560,7 @@ static gboolean dlfile_recv_check(dlfile_thread_t *t, char *leaf) { bita_set(t->dl->bitmap, i); dlfile_save_bitmap_defer(t->dl); - g_static_mutex_unlock(&t->dl->lock); + g_mutex_unlock(&t->dl->lock); t->uerr = DLE_HASH; t->uerr_msg = g_strdup_printf("Hash for block %u (chunk %u-%u) does not match.", num, startchunk, startchunk+chunksinblock); @@ -609,11 +609,11 @@ gboolean dlfile_recv(void *vt, const char *buf, int len) { buf += inchunk; len -= inchunk; - g_static_mutex_lock(&t->dl->lock); + g_mutex_lock(&t->dl->lock); t->dl->have += inchunk; if(!islast && t->len < DLFILE_CHUNKSIZE) { - g_static_mutex_unlock(&t->dl->lock); + g_mutex_unlock(&t->dl->lock); continue; } @@ -625,7 +625,7 @@ gboolean dlfile_recv(void *vt, const char *buf, int len) { t->allocated--; t->avail--; t->len = 0; - g_static_mutex_unlock(&t->dl->lock); + g_mutex_unlock(&t->dl->lock); if(!t->dl->islist && (islast || t->chunk % (t->dl->hash_block / DLFILE_CHUNKSIZE) == 0)) { char leaf[24]; diff --git a/src/doc.h b/src/doc.h index c9c7fa4..654c787 100644 --- a/src/doc.h +++ b/src/doc.h @@ -1,6 +1,6 @@ /* ncdc - NCurses Direct Connect client - Copyright (c) 2011-2014 Yoran Heling + Copyright (c) 2011-2022 Yoran Heling Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the @@ -404,6 +404,12 @@ static const doc_set_t doc_sets[] = { "Minimum segment size to use when requesting file data from another user." " Set to 0 to disable segmented downloading." }, +{ + "download_shared", 0, "", + "Whether to download files which are already present in your share. When this" + " is set to `false', adding already shared files results in a UI message" + " instead of adding the file to the download queue." +}, { "download_slots", 0, "", "Maximum number of simultaneous downloads." }, @@ -439,13 +445,10 @@ static const doc_set_t doc_sets[] = { " your system for other things besides ncdc, you share large files (>100MB)" " and people are not constantly downloading the same file from you." }, -{ "geoip_cc4", 0, "|disabled", - "Path to the GeoIP Country database file for IPv4, or 'disabled' to disable" - " GeoIP lookup for IPv4 addresses." -}, -{ "geoip_cc6", 0, "|disabled", - "Path to the GeoIP Country database file for IPv6, or 'disabled' to disable" - " GeoIP lookup for IPv6 addresses." +{ "geoip_cc", 0, "|disabled", + "Path to the GeoIP2 Country database file (GeoLite2-Country.mmdb), or" + " 'disabled' to disable GeoIP lookups. The database can be downloaded " + " from https://dev.maxmind.com/geoip/geoip2/geolite2/." }, { "hash_rate", 0, "", "Maximum file hashing speed. See the `download_rate' setting for allowed" @@ -490,6 +493,10 @@ static const doc_set_t doc_sets[] = { { "log_uploads", 0, "", "Log file uploads to transfers.log." }, +{ + "max_ul_per_user", 0, "", + "The maximum number of simultaneous upload connections to one user." +}, { "minislots", 0, "", "Set the number of available minislots. A `minislot' is a special slot that" " is used when all regular upload slots are in use and someone is requesting" @@ -559,6 +566,11 @@ static const doc_set_t doc_sets[] = { " enabled, any symlinks in your shared directories will be followed, even" " when they point to a directory outside your share." }, +{ + "show_free_slots", 1, "", + "When set to true, [n sl] will be prepended to your description, where n is" + " the number of currently available upload slots." +}, { "show_joinquit", 1, "", "Whether to display join/quit messages in the hub chat." }, @@ -579,18 +591,17 @@ static const doc_set_t doc_sets[] = { " note that, even if you set this to `prefer', encryption is still only used" " when the client on the other side of the connection also supports it." }, -{ "tls_policy", 1, "", +{ "tls_policy", 1, "", "Set the policy for secure client-to-client connections. Setting this to" " `disabled' disables TLS support for client connections, but still allows" " you to connect to TLS-enabled hubs. `allow' will allow the use of TLS if" " the other client requests this, but ncdc itself will not request TLS when" - " connecting to others. Setting this to `prefer' tells ncdc to also request" - " TLS when connecting to others.\n\n" + " connecting to others, `prefer' tells ncdc to request TLS when connecting to" + " others. Setting this to 'force' will disallow non-TLS connections and also" + " requires that the hub connection itself is TLS.\n\n" "The use of TLS for client connections usually results in less optimal" " performance when uploading and downloading, but is quite effective at" - " avoiding protocol-specific traffic shaping that some ISPs may do. Also note" - " that, even if you set this to `prefer', TLS will only be used if the" - " connecting party also supports it." + " avoiding protocol-specific traffic shaping that some ISPs may do." }, { "tls_priority", 0, "", "Set the GnuTLS priority string used for all TLS-enabled connections. See the" @@ -695,7 +706,8 @@ static const doc_key_t doc_keys[] = { "i/Return Toggle information box.\n" "f Find user in user list.\n" "m Send a PM to the selected user.\n" - "q Find file in download queue." + "q Find file in download queue.\n" + "b/B Browse the selected user's list, B to force a redownload." }, { "queue", "Download queue", LISTING_KEYS @@ -719,7 +731,7 @@ static const doc_key_t doc_keys[] = { { "search", "Search results tab", LISTING_KEYS "f Find user in user list.\n" - "b/B Browse the selected users' list, B to force a redownload.\n" + "b/B Browse the selected user's list, B to force a redownload.\n" "d Add selected file to the download queue.\n" "h Toggle hub column visibility.\n" "u Order by username.\n" diff --git a/src/fl_load.c b/src/fl_load.c index a986369..499068d 100644 --- a/src/fl_load.c +++ b/src/fl_load.c @@ -1,6 +1,6 @@ /* ncdc - NCurses Direct Connect client - Copyright (c) 2011-2014 Yoran Heling + Copyright (c) 2011-2022 Yoran Heling Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the @@ -205,7 +205,37 @@ static void fl_load_token(ctx_t *x, yxml_ret_t r, GError **err) { } -static fl_list_t *fl_load_parse(FILE *fh, BZFILE *bzfh, gboolean local, GError **err) { +static int fl_load_readbz(bz_stream *bzs, int fd, char *bzbuf, GError **err) { + int buflen; + + bzs->next_in = bzbuf; + if(bzs->avail_in == 0) { + buflen = read(fd, bzs->next_in + bzs->avail_in, READBUFSIZE - bzs->avail_in); + if(buflen == 0) + return -1; + if(buflen < 0) { + g_set_error(err, 1, 0, "Read error: %s", g_strerror(errno)); + return -1; + } + bzs->avail_in += buflen; + } + + int bzerr = BZ2_bzDecompress(bzs); + if(bzerr == BZ_STREAM_END) { + BZ2_bzDecompressEnd(bzs); + BZ2_bzDecompressInit(bzs, 0, 0); + } else if(bzerr != BZ_OK) { + g_set_error(err, 1, 0, "bzip2 decompression error (%d): %s", bzerr, g_strerror(errno)); + return -1; + } + + memmove(bzbuf, bzs->next_in, bzs->avail_in); + bzs->next_in = bzbuf; + return READBUFSIZE-bzs->avail_out; +} + + +static fl_list_t *fl_load_parse(int fd, bz_stream *bzs, gboolean local, GError **err) { ctx_t *x = g_new(ctx_t, 1); x->state = S_START; x->root = fl_list_create("", FALSE); @@ -219,24 +249,21 @@ static fl_list_t *fl_load_parse(FILE *fh, BZFILE *bzfh, gboolean local, GError * yxml_init(&x->x, x->stack, STACKSIZE); int buflen = 0; - int bzeof = 0; + char *bzbuf = NULL; while(1) { // Fill buffer - if(bzfh) { - if(bzeof) + if(bzs) { + if(!bzbuf) + bzbuf = g_malloc(READBUFSIZE); + bzs->next_out = x->buf; + bzs->avail_out = READBUFSIZE; + buflen = fl_load_readbz(bzs, fd, bzbuf, err); + if(buflen < 0) break; - int bzerr; - buflen = BZ2_bzRead(&bzerr, bzfh, x->buf, READBUFSIZE); - if(bzerr == BZ_STREAM_END) - bzeof = 1; - else if(bzerr != BZ_OK) { - g_set_error(err, 1, 0, "bzip2 decompression error (%d): %s", bzerr, g_strerror(errno)); - break; - } } else { - buflen = fread(x->buf, 1, READBUFSIZE, fh); - if(buflen < 0 && feof(fh)) + buflen = read(fd, x->buf, READBUFSIZE); + if(buflen == 0) break; if(buflen < 0) { g_set_error(err, 1, 0, "Read error: %s", g_strerror(errno)); @@ -268,6 +295,7 @@ static fl_list_t *fl_load_parse(FILE *fh, BZFILE *bzfh, gboolean local, GError * g_set_error_literal(err, 1, 0, "XML document did not end correctly"); fl_list_t *root = x->root; + g_free(bzbuf); g_free(x->name); g_free(x); return root; @@ -278,36 +306,32 @@ fl_list_t *fl_load(const char *file, GError **err, gboolean local) { g_return_val_if_fail(err == NULL || *err == NULL, NULL); fl_list_t *root = NULL; - FILE *fh; - BZFILE *bzfh = NULL; + int fd; + bz_stream *bzs = NULL; GError *ierr = NULL; // open file - fh = fopen(file, "r"); - if(!fh) { + fd = open(file, O_RDONLY); + if(fd < 0) { g_set_error_literal(&ierr, 1, 0, g_strerror(errno)); goto end; } - // open BZ2 decompression + // Create BZ2 stream object if this is a bzip2 file if(strlen(file) > 4 && strcmp(file+(strlen(file)-4), ".bz2") == 0) { - int bzerr; - bzfh = BZ2_bzReadOpen(&bzerr, fh, 0, 0, NULL, 0); - if(bzerr != BZ_OK) { - g_set_error(&ierr, 1, 0, "Unable to open bzip2 file (%d): %s", bzerr, g_strerror(errno)); - goto end; - } + bzs = g_new0(bz_stream, 1); + BZ2_bzDecompressInit(bzs, 0, 0); } - root = fl_load_parse(fh, bzfh, local, &ierr); + root = fl_load_parse(fd, bzs, local, &ierr); end: - if(bzfh) { - int bzerr; - BZ2_bzReadClose(&bzerr, bzfh); + if(bzs) { + BZ2_bzDecompressEnd(bzs); + g_free(bzs); } - if(fh) - fclose(fh); + if(fd >= 0) + close(fd); if(ierr) { g_propagate_error(err, ierr); if(root) diff --git a/src/fl_local.c b/src/fl_local.c index bb36047..00802b0 100644 --- a/src/fl_local.c +++ b/src/fl_local.c @@ -1,6 +1,6 @@ /* ncdc - NCurses Direct Connect client - Copyright (c) 2011-2014 Yoran Heling + Copyright (c) 2011-2022 Yoran Heling Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the @@ -46,8 +46,8 @@ guint64 fl_hash_queue_size = 0; static fl_list_t *fl_hash_cur = NULL; // most recent file initiated for hashing ratecalc_t fl_hash_rate; static guint64 fl_hash_reset = 0; // increased when fl_hash_cur is removed from the queue (to stop current hash operation) -static GMutex *fl_hash_resetlock; -static GCond *fl_hash_resetcond; +static GMutex fl_hash_resetlock; +static GCond fl_hash_resetcond; #define TTH_BUFSIZE (512*1024) @@ -134,9 +134,10 @@ gboolean fl_flush(gpointer dat) { return TRUE; } - - - +// are we currently refreshing the share? +gboolean fl_is_refreshing(void) { + return fl_refresh_queue && fl_refresh_queue->head; +} // Hash index interface. These operate on fl_hash_index and make sure // fl_local_list_size and _length stay correct. @@ -475,10 +476,10 @@ typedef struct fl_hash_t { fl_hash_queue_size -= fl->size;\ g_hash_table_remove(fl_hash_queue, fl);\ if((fl) == fl_hash_cur) {\ - g_mutex_lock(fl_hash_resetlock);\ + g_mutex_lock(&fl_hash_resetlock);\ fl_hash_reset++;\ - g_cond_signal(fl_hash_resetcond);\ - g_mutex_unlock(fl_hash_resetlock);\ + g_cond_signal(&fl_hash_resetcond);\ + g_mutex_unlock(&fl_hash_resetlock);\ }\ }\ } while(0) @@ -502,14 +503,10 @@ static void fl_hash_queue_delrec(fl_list_t *f) { // Returns the allowed burst, or 0 on cancellation. static int fl_hash_burst(guint64 lastreset) { int b = 0; - g_mutex_lock(fl_hash_resetlock); - while(fl_hash_reset == lastreset && (b = ratecalc_burst(&fl_hash_rate)) <= 0) { - GTimeVal end; - g_get_current_time(&end); - g_time_val_add(&end, 100*1000); // Wake up every 100ms. - g_cond_timed_wait(fl_hash_resetcond, fl_hash_resetlock, &end); - } - g_mutex_unlock(fl_hash_resetlock); + g_mutex_lock(&fl_hash_resetlock); + while(fl_hash_reset == lastreset && (b = ratecalc_burst(&fl_hash_rate)) <= 0) + g_cond_wait_until(&fl_hash_resetcond, &fl_hash_resetlock, g_get_monotonic_time() + 100*G_TIME_SPAN_MILLISECOND); + g_mutex_unlock(&fl_hash_resetlock); return b; } @@ -984,7 +981,7 @@ static void fl_init_list(fl_list_t *fl) { static gboolean fl_init_autorefresh(gpointer dat) { int r = var_get_int(0, VAR_autorefresh); time_t t = time(NULL); - if(r && fl_refresh_last+r < t) + if(r && fl_refresh_last+r < t && !fl_is_refreshing() && fl_hash_queue_size == 0) fl_refresh(NULL); return TRUE; } @@ -1000,8 +997,6 @@ void fl_init() { fl_refresh_queue = g_queue_new(); fl_scan_pool = g_thread_pool_new(fl_scan_thread, NULL, 1, FALSE, NULL); fl_hash_pool = g_thread_pool_new(fl_hash_thread, NULL, 1, FALSE, NULL); - fl_hash_resetlock = g_mutex_new(); - fl_hash_resetcond = g_cond_new(); fl_hash_queue = g_hash_table_new(g_direct_hash, g_direct_equal); // Even though the keys are the tth roots, we can just use g_int_hash. The // first four bytes provide enough unique data anyway. diff --git a/src/fl_save.c b/src/fl_save.c index 1bbb4ed..7ee5ed4 100644 --- a/src/fl_save.c +++ b/src/fl_save.c @@ -1,6 +1,6 @@ /* ncdc - NCurses Direct Connect client - Copyright (c) 2011-2014 Yoran Heling + Copyright (c) 2011-2022 Yoran Heling Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the diff --git a/src/fl_util.c b/src/fl_util.c index b03261e..645c4b9 100644 --- a/src/fl_util.c +++ b/src/fl_util.c @@ -1,6 +1,6 @@ /* ncdc - NCurses Direct Connect client - Copyright (c) 2011-2014 Yoran Heling + Copyright (c) 2011-2022 Yoran Heling Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the diff --git a/src/geoip.c b/src/geoip.c index e5aaa71..6868e87 100644 --- a/src/geoip.c +++ b/src/geoip.c @@ -1,6 +1,6 @@ /* ncdc - NCurses Direct Connect client - Copyright (c) 2011-2014 Yoran Heling + Copyright (c) 2011-2022 Yoran Heling Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the @@ -30,60 +30,67 @@ gboolean geoip_available = FALSE; #ifdef USE_GEOIP -static GeoIP *geoip4; -static GeoIP *geoip6; -#endif +#include -void geoip_reinit(int v) { -#ifdef USE_GEOIP - GeoIP **var = v == 4 ? &geoip4 : &geoip6; - if(*var) { - GeoIP_delete(*var); - *var = NULL; +static MMDB_s *db; + +void geoip_reinit() { + if(db) { + MMDB_close(db); + free(db); + db = NULL; + geoip_available = FALSE; } - const char *fn = var_get(0, v == 4 ? VAR_geoip_cc4 : VAR_geoip_cc6); - if(!fn) { - /* Get the file paths directly, so that we can offer more useful diagnostic - * messages in case we fail to open it. Calling GeoIP_db_avail() ensures that - * the GeoIPDBFileName variable has been initialized. */ - if(!GeoIPDBFileName) - GeoIP_db_avail(GEOIP_COUNTRY_EDITION); - fn = GeoIPDBFileName[v == 4 ? GEOIP_COUNTRY_EDITION : GEOIP_COUNTRY_EDITION_V6]; + const char *fn = var_get(0, VAR_geoip_cc); + if(!fn || strcmp(fn, "disabled") == 0) + return; + + db = malloc(sizeof(MMDB_s)); + int status = MMDB_open(fn, 0, db); + + if(status != MMDB_SUCCESS) { + ui_mf(NULL, 0, "Can't open '%s' (%s), no country codes will be displayed.", fn, MMDB_strerror(status)); + free(db); + db = NULL; + } else { + geoip_available = TRUE; } +} + +const char *geoip_country(const struct sockaddr *const ip) { + if(!db) + return NULL; - if(strcmp(fn, "disabled") != 0) { - /* The '16' flag is GEOIP_SILENCE, but it's a fairly new option and not - * defined in older versions. Just pass it along directly, ABI compatibility - * should ensure this works with both old and new versions. - * Also perform a g_file_test() first to ensure we're not opening - * non-existing files. GeoIP versions that do not support GEOIP_SILENCE - * will throw error messages on stdout/stderr, which screws up our ncurses - * UI, so catching the most common error here is worth it. */ - *var = g_file_test(fn, G_FILE_TEST_EXISTS) ? GeoIP_open(fn, 16 | GEOIP_MEMORY_CACHE) : NULL; - if(!*var) - ui_mf(NULL, 0, "Can't open '%s', no country codes will be displayed for IPv%d addresses.", fn, v); + int status; + MMDB_lookup_result_s result = MMDB_lookup_sockaddr(db, ip, &status); + if(status != MMDB_SUCCESS) { + ui_mf(NULL, 0, "Error looking up IP address: %s", MMDB_strerror(status)); + return NULL; } + if(!result.found_entry) + return NULL; + + MMDB_entry_data_s data; + status = MMDB_get_value(&result.entry, &data, "country", "iso_code", (void*)NULL); + if(status != MMDB_SUCCESS) { + ui_mf(NULL, 0, "Error looking up IP address: %s", MMDB_strerror(status)); + return NULL; + } + if(!data.has_data || data.type != MMDB_DATA_TYPE_UTF8_STRING) + return NULL; - geoip_available = geoip4 || geoip6; -#endif + static char buf[8]; + memset(buf, 0, sizeof(buf)); + memcpy(buf, data.utf8_string, data.data_size); + return buf; } -const char *geoip_country4(const char *ip) { -#ifdef USE_GEOIP - if(geoip4) - return GeoIP_country_code_by_addr(geoip4, ip); -#endif - return NULL; -} +#else /* No GEOIP */ +void geoip_reinit() {} +const char *geoip_country(const struct sockaddr *const ip) { return NULL; } -const char *geoip_country6(const char *ip) { -#ifdef USE_GEOIP - if(geoip6) - return GeoIP_country_code_by_addr_v6(geoip6, ip); #endif - return NULL; -} diff --git a/src/hub.c b/src/hub.c index 36aec55..0365c67 100644 --- a/src/hub.c +++ b/src/hub.c @@ -1,6 +1,6 @@ /* ncdc - NCurses Direct Connect client - Copyright (c) 2011-2014 Yoran Heling + Copyright (c) 2011-2022 Yoran Heling Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the @@ -106,7 +106,7 @@ struct hub_t { // last info we sent to the hub char *nfo_desc, *nfo_conn, *nfo_mail, *nfo_ip; - unsigned char nfo_slots, nfo_h_norm, nfo_h_reg, nfo_h_op; + unsigned char nfo_slots, nfo_free_slots, nfo_h_norm, nfo_h_reg, nfo_h_op; guint64 nfo_share; guint16 nfo_udp_port; gboolean nfo_sup_tls, nfo_sup_sudp; @@ -563,13 +563,13 @@ void hub_opencc(hub_t *hub, hub_user_t *u) { char token[14] = {}; if(hub->adc) { char nonce[8]; - crypt_nonce(nonce, 8); + g_warn_if_fail(gnutls_rnd(GNUTLS_RND_NONCE, nonce, 8) == 0); base32_encode_dat(nonce, token, 8); } - guint16 wanttls = var_get_int(hub->id, VAR_tls_policy) == VAR_TLSP_PREFER; + int tlspolicy = var_get_int(hub->id, VAR_tls_policy); int port = listen_hub_tcp(hub->id); - gboolean usetls = wanttls && u->hastls; + gboolean usetls = tlspolicy == VAR_TLSP_FORCE || (tlspolicy == VAR_TLSP_PREFER && u->hastls); char *adcproto = !usetls ? "ADC/1.0" : u->hasadc0 ? "ADCS/0.10" : "ADCS/1.0"; // we're active, send CTM @@ -668,19 +668,42 @@ void hub_search(hub_t *hub, search_q_t *q) { #define eq(a) (a == hub->nfo_##a) #define beq(a) (!!a == !!hub->nfo_##a) +static unsigned char num_free_slots(unsigned char hub_slots) { + int rv = hub_slots - cc_slots_in_use(NULL); + return rv > 0 ? rv : 0; +} + +static GString* format_desc(hub_t *hub, unsigned char free_slots) { + GString *desc; + const char *static_desc = var_get(hub->id, VAR_description); + if(var_get_bool(hub->id, VAR_show_free_slots)) { + desc = g_string_sized_new(128); + if(static_desc) + g_string_printf(desc, "[%d sl] %s", free_slots, static_desc); + else + g_string_printf(desc, "[%d sl]", free_slots); + } else + desc = g_string_new(static_desc); + return desc; +} + void hub_send_nfo(hub_t *hub) { if(!net_is_connected(hub->net)) return; // get info, to be compared with hub->nfo_ char *desc, *conn = NULL, *mail, *ip; - unsigned char slots, h_norm, h_reg, h_op; + unsigned char slots, free_slots, h_norm, h_reg, h_op; guint64 share; guint16 udp_port; gboolean sup_tls, sup_sudp; + GString *fmt_desc; - desc = var_get(hub->id, VAR_description); mail = var_get(hub->id, VAR_email); + slots = var_get_int(0, VAR_slots); + free_slots = num_free_slots(slots); + fmt_desc = format_desc(hub, free_slots); + desc = fmt_desc->str; char buf[50] = {}; if(var_get_int(0, VAR_upload_rate)) { @@ -703,7 +726,6 @@ void hub_send_nfo(hub_t *hub) { else h_norm++; } - slots = var_get_int(0, VAR_slots); ip = listen_hub_active(hub->id) ? hub_ip(hub) : NULL; udp_port = listen_hub_udp(hub->id); share = fl_local_list_size; @@ -711,9 +733,11 @@ void hub_send_nfo(hub_t *hub) { sup_sudp = hub->tls && var_get_int(0, VAR_sudp_policy) != VAR_SUDPP_DISABLE ? TRUE : FALSE; // check whether we need to make any further effort - if(hub->nick_valid && streq(desc) && streq(conn) && streq(mail) && eq(slots) && streq(ip) - && eq(h_norm) && eq(h_reg) && eq(h_op) && eq(share) && eq(udp_port) && beq(sup_tls) && beq(sup_sudp)) + if(hub->nick_valid && streq(desc) && streq(conn) && streq(mail) && eq(slots) && eq(free_slots) && streq(ip) + && eq(h_norm) && eq(h_reg) && eq(h_op) && eq(share) && eq(udp_port) && beq(sup_tls) && beq(sup_sudp)) { + g_string_free(fmt_desc, TRUE); return; + } char *nfo; // ADC @@ -753,6 +777,8 @@ void hub_send_nfo(hub_t *hub) { g_string_append_printf(cmd, " SS%"G_GUINT64_FORMAT" SF%d", share, fl_local_list_length); if(f || !eq(slots)) g_string_append_printf(cmd, " SL%d", slots); + if(f || !eq(free_slots)) + g_string_append_printf(cmd, " FS%d", free_slots); if(f || !eq(h_norm)) g_string_append_printf(cmd, " HN%d", h_norm); if(f || !eq(h_reg)) @@ -786,11 +812,12 @@ void hub_send_nfo(hub_t *hub) { g_free(nfo); // update - g_free(hub->nfo_desc); hub->nfo_desc = g_strdup(desc); + g_free(hub->nfo_desc); hub->nfo_desc = g_string_free(fmt_desc, FALSE); g_free(hub->nfo_conn); hub->nfo_conn = g_strdup(conn); g_free(hub->nfo_mail); hub->nfo_mail = g_strdup(mail); g_free(hub->nfo_ip); hub->nfo_ip = g_strdup(ip); hub->nfo_slots = slots; + hub->nfo_free_slots = free_slots; hub->nfo_h_norm = h_norm; hub->nfo_h_reg = h_reg; hub->nfo_h_op = h_op; @@ -882,7 +909,7 @@ static void adc_sch_reply_send(hub_t *hub, net_udp_t *udp, GString *r, const cha // prepend 16 random bytes to message char nonce[16]; - crypt_nonce(nonce, 16); + g_warn_if_fail(gnutls_rnd(GNUTLS_RND_NONCE, nonce, 16) == 0); g_string_prepend_len(r, nonce, 16); // use PKCS#5 padding to align the message length to the cypher block size (16) @@ -1032,7 +1059,7 @@ static void adc_sch(hub_t *hub, adc_cmd_t *cmd) { // Many ways to say the same thing #define is_adcs_proto(p) (strcmp(p, "ADCS/1.0") == 0 || strcmp(p, "ADCS/0.10") == 0 || strcmp(p, "ADC0/0.10") == 0) #define is_adc_proto(p) (strcmp(p, "ADC/1.0") == 0 || strcmp(p, "ADC/0.10") == 0) -#define is_valid_proto(p) (is_adc_proto(p) || is_adcs_proto(p)) +#define is_valid_proto(pol, p) ((pol) == VAR_TLSP_DISABLE ? is_adc_proto(p) : (pol) == VAR_TLSP_FORCE ? is_adcs_proto(p) : is_adc_proto(p) || is_adcs_proto(p)) static void adc_handle(net_t *net, char *msg, int _len) { hub_t *hub = net_handle(net); @@ -1170,7 +1197,7 @@ static void adc_handle(net_t *net, char *msg, int _len) { case ADCC_CTM: if(cmd.argc < 3 || cmd.type != 'D' || cmd.dest != hub->sid) g_message("Invalid message from %s: %s", net_remoteaddr(hub->net), msg); - else if(var_get_int(hub->id, VAR_tls_policy) == VAR_TLSP_DISABLE ? !is_adc_proto(cmd.argv[0]) : !is_valid_proto(cmd.argv[0])) { + else if(!is_valid_proto(var_get_int(hub->id, VAR_tls_policy), cmd.argv[0])) { GString *r = adc_generate('D', ADCC_STA, hub->sid, cmd.source); g_string_append(r, " 141 Unknown\\sprotocol"); adc_append(r, "PR", cmd.argv[0]); @@ -1199,7 +1226,7 @@ static void adc_handle(net_t *net, char *msg, int _len) { case ADCC_RCM: if(cmd.argc < 2 || cmd.type != 'D' || cmd.dest != hub->sid) g_message("Invalid message from %s: %s", net_remoteaddr(hub->net), msg); - else if(var_get_int(hub->id, VAR_tls_policy) == VAR_TLSP_DISABLE ? !is_adc_proto(cmd.argv[0]) : !is_valid_proto(cmd.argv[0])) { + else if(!is_valid_proto(var_get_int(hub->id, VAR_tls_policy), cmd.argv[0])) { GString *r = adc_generate('D', ADCC_STA, hub->sid, cmd.source); g_string_append(r, " 141 Unknown\\protocol"); adc_append(r, "PR", cmd.argv[0]); @@ -1325,7 +1352,7 @@ static void adc_handle(net_t *net, char *msg, int _len) { // If port = 0, 'from' is interpreted as a nick. Otherwise, from should be an IP address. static void nmdc_search(hub_t *hub, char *from, unsigned short port, int size_m, guint64 size, int type, char *query) { int max = port ? 10 : 5; - fl_list_t *res[max]; + fl_list_t *res[10]; fl_search_t s = {}; s.filedir = type == 1 ? 3 : type == 8 ? 2 : 1; s.ext = search_types[type].exts; @@ -1445,7 +1472,7 @@ static void nmdc_handle(net_t *net, char *cmd, int _len) { if(g_regex_match(lock, cmd, 0, &nfo)) { // 1 = lock char *lock = g_match_info_fetch(nfo, 1); if(strncmp(lock, "EXTENDEDPROTOCOL", 16) == 0) - net_writestr(hub->net, "$Supports NoGetINFO NoHello UserIP2|"); + net_writestr(hub->net, "$Supports NoGetINFO NoHello UserIP2 TLS|"); char *key = nmdc_lock2key(lock); net_writef(hub->net, "$Key %s|", key); hub->nick = g_strdup(var_get(hub->id, VAR_nick)); @@ -1659,6 +1686,10 @@ static void nmdc_handle(net_t *net, char *cmd, int _len) { if(yuri_parse(addr, &uri) != 0 || *uri.scheme || uri.port == 0 || uri.hosttype == YURI_DOMAIN || *uri.path || *uri.query || *uri.fragment) g_message("Invalid host:port in $ConnectToMe (%s)", addr); + else if(*tls && var_get_int(hub->id, VAR_tls_policy) == VAR_TLSP_DISABLE) + g_message("$ConnectToMe from (%s) requires TLS, but TLS has been disabled in our tls_policy", addr); + else if(!*tls && var_get_int(hub->id, VAR_tls_policy) == VAR_TLSP_FORCE) + g_message("$ConnectToMe from (%s) without TLS, but TLS is required according to our tls_policy", addr); else cc_nmdc_connect(cc_create(hub), uri.host, uri.port, var_get(hub->id, VAR_local_address), *tls ? TRUE : FALSE); } @@ -1677,11 +1708,13 @@ static void nmdc_handle(net_t *net, char *cmd, int _len) { g_message("Received a $RevConnectToMe for someone else (to %s from %s)", me, other); else if(!u) g_message("Received a $RevConnectToMe from someone not on the hub."); + else if(!u->hastls && var_get_int(hub->id, VAR_tls_policy) == VAR_TLSP_FORCE) + g_message("Received a $RevConnectToMe from client that does not support TLS."); else if(listen_hub_active(hub->id)) { // Unlike with ADC, the client sending the $RCTM can not indicate it // wants to use TLS or not, so the decision is with us. Let's require - // tls_policy to be PREFER here. - int usetls = u->hastls && var_get_int(hub->id, VAR_tls_policy) == VAR_TLSP_PREFER; + // tls_policy to be PREFER or FORCE here. + int usetls = u->hastls && (var_get_int(hub->id, VAR_tls_policy) & (VAR_TLSP_PREFER|VAR_TLSP_FORCE)); int port = listen_hub_tcp(hub->id); net_writef(hub->net, net_is_ipv6(hub->net) ? "$ConnectToMe %s [%s]:%d%s|" : "$ConnectToMe %s %s:%d%s|", other, hub_ip(hub), port, usetls ? "S" : ""); @@ -1812,7 +1845,7 @@ hub_t *hub_create(ui_tab_t *tab) { // Get or create the hub id hub->id = db_vars_hubid(tab->name); if(!hub->id) { - crypt_rnd(&hub->id, 8); + g_warn_if_fail(gnutls_rnd(GNUTLS_RND_RANDOM, &hub->id, 8) == 0); var_set(hub->id, VAR_hubname, tab->name, NULL); } @@ -1826,12 +1859,38 @@ hub_t *hub_create(ui_tab_t *tab) { return hub; } +static void handle_fully_connected(hub_t *hub) { + net_set_keepalive(hub->net, hub->adc ? "\n" : "|"); + + /* If we have a pre-configured active IP, make sure to enable active mode + * immediately. */ + if(hub_ip(hub)) + listen_refresh(); + + if(hub->adc) + net_writef(hub->net, "HSUP ADBASE ADTIGR%s\n", var_get_bool(hub->id, VAR_adc_blom) ? " ADBLO0 ADBLOM" : ""); + + // In the case that the joincomplete detection fails, consider the join to be + // complete anyway after a 2-minute timeout. + hub->joincomplete_timer = g_timeout_add_seconds(120, joincomplete_timer, hub); -static void handle_handshake(net_t *n, const char *kpr) { + // Start handling incoming messages + net_readmsg(hub->net, hub->adc ? '\n' : '|', hub->adc ? adc_handle : nmdc_handle); +} + +static void handle_handshake(net_t *n, const char *kpr, int proto) { g_return_if_fail(kpr != NULL); hub_t *hub = net_handle(n); g_return_if_fail(!hub->kp); + if(proto == ALPN_NMDC) { + hub->adc = FALSE; + ui_mf(hub->tab, 0, "ALPN: negotiated NMDC."); + } else if(proto == ALPN_ADC) { + hub->adc = TRUE; + ui_mf(hub->tab, 0, "ALPN: negotiated ADC."); + } + char kpf[53] = {}; base32_encode_dat(kpr, kpf, 32); @@ -1842,12 +1901,15 @@ static void handle_handshake(net_t *n, const char *kpr) { if(!old) { ui_mf(hub->tab, 0, "No previous TLS keyprint known. Storing `%s' for future validation.", kpf); var_set(hub->id, VAR_hubkp, kpf, NULL); + handle_fully_connected(hub); return; } // Keyprint matches? no problems! - if(strcmp(old, kpf) == 0) + if(strcmp(old, kpf) == 0) { + handle_fully_connected(hub); return; + } // Keyprint doesn't match... now we have a problem! ui_mf(hub->tab, UIP_HIGH, @@ -1872,28 +1934,19 @@ static void handle_connect(net_t *n, const char *addr) { return; } - net_set_keepalive(n, hub->adc ? "\n" : "|"); ui_mf(hub->tab, 0, "Connected to %s.", net_remoteaddr(n)); if(hub->tls) - net_settls(hub->net, FALSE, handle_handshake); + net_settls(hub->net, FALSE, TRUE, handle_handshake); if(!net_is_connected(hub->net)) return; - /* If we have a pre-configured active IP, make sure to enable active mode - * immediately. */ - if(hub_ip(hub)) - listen_refresh(); - - if(hub->adc) - net_writef(hub->net, "HSUP ADBASE ADTIGR%s\n", var_get_bool(hub->id, VAR_adc_blom) ? " ADBLO0 ADBLOM" : ""); - - // In the case that the joincomplete detection fails, consider the join to be - // complete anyway after a 2-minute timeout. - hub->joincomplete_timer = g_timeout_add_seconds(120, joincomplete_timer, hub); + // TLS may negotiate a different protocol, and handle_handshake + // will eventually call handle_fully_connected as well. + if(hub->tls) + return; - // Start handling incoming messages - net_readmsg(hub->net, hub->adc ? '\n' : '|', hub->adc ? adc_handle : nmdc_handle); + handle_fully_connected(hub); } @@ -1906,6 +1959,12 @@ void hub_connect(hub_t *hub) { hub->adc = strncmp(addr.scheme, "adc", 3) == 0; hub->tls = strcmp(addr.scheme, "adcs") == 0 || strcmp(addr.scheme, "nmdcs") == 0; + if(!hub->tls && var_get_int(hub->id, VAR_tls_policy) == VAR_TLSP_FORCE) { + ui_mf(hub->tab, 0, "Refusing to connect to %s; tls_policy is set to 'force' but this is not a TLS address.", oaddr); + free(addr.buf); + return; + } + if(hub->reconnect_timer) { g_source_remove(hub->reconnect_timer); hub->reconnect_timer = 0; diff --git a/src/listen.c b/src/listen.c index 285fce9..c64d472 100644 --- a/src/listen.c +++ b/src/listen.c @@ -1,6 +1,6 @@ /* ncdc - NCurses Direct Connect client - Copyright (c) 2011-2014 Yoran Heling + Copyright (c) 2011-2022 Yoran Heling Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the diff --git a/src/main.c b/src/main.c index 0ba6a21..90fffa7 100644 --- a/src/main.c +++ b/src/main.c @@ -1,6 +1,6 @@ /* ncdc - NCurses Direct Connect client - Copyright (c) 2011-2014 Yoran Heling + Copyright (c) 2011-2022 Yoran Heling Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the @@ -342,6 +342,7 @@ static gboolean sighandle_sourcefunc(gpointer dat) { } if(main_sig_log) { logfile_global_reopen(); + geoip_reinit(); main_sig_log = FALSE; } return TRUE; @@ -376,29 +377,6 @@ static GOptionEntry cli_options[] = { -#ifdef USE_GCRYPT -/* These hacks with static exist to trick makeheaders into not copying the - * gcrypt macro into the header file. It does cause some of the functions to be - * exported, but they're pretty unique anyway. */ -#define static -static GCRY_THREAD_OPTION_PTHREAD_IMPL; -#undef static -#endif - -static void init_crypt() { -#ifdef USE_GCRYPT - gcry_control(GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread); - if(!gcry_check_version(GCRYPT_VERSION)) { - fputs("libgcrypt version mismatch\n", stderr); - exit(1); - } - gcry_control(GCRYCTL_ENABLE_QUICK_RANDOM); - gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0); -#endif - gnutls_global_init(); -} - - int main(int argc, char **argv) { setlocale(LC_ALL, ""); // Early logging goes to stderr @@ -423,8 +401,7 @@ int main(int argc, char **argv) { } // init stuff - init_crypt(); - g_thread_init(NULL); + gnutls_global_init(); // Create main loop main_loop = g_main_loop_new(NULL, FALSE); @@ -453,8 +430,7 @@ int main(int argc, char **argv) { dl_init_global(); ui_cmdhist_init("history"); ui_init(bracketed_paste); - geoip_reinit(4); - geoip_reinit(6); + geoip_reinit(); // setup SIGWINCH struct sigaction act; diff --git a/src/ncdc.h b/src/ncdc.h index 944d04f..8cd266b 100644 --- a/src/ncdc.h +++ b/src/ncdc.h @@ -1,6 +1,6 @@ /* ncdc - NCurses Direct Connect client - Copyright (c) 2011-2014 Yoran Heling + Copyright (c) 2011-2022 Yoran Heling Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the @@ -63,56 +63,9 @@ #include #include #include -#ifdef USE_GCRYPT -#include -#else #include -#endif +#ifndef _XOPEN_SOURCE_EXTENDED #define _XOPEN_SOURCE_EXTENDED -#ifdef HAVE_NCURSESW_NCURSES_H -#include -#elif HAVE_NCURSES_NCURSES_H -#include -#else -#include -#endif - -#ifdef USE_GEOIP -#include -#endif - - -// GnuTLS / libgcrypt functions -// crypt_aes128cbc() uses a 16-byte zero'd IV. Data is encrypted or decrypted in-place. -#ifdef USE_GCRYPT -#define crypt_rnd(buf, len) gcry_randomize(buf, len, GCRY_STRONG_RANDOM) -#define crypt_nonce(buf, len) gcry_create_nonce(buf, len) -static inline void crypt_aes128cbc(gboolean encrypt, const char *key, size_t keylen, char *data, size_t len) { - gcry_cipher_hd_t ciph; - char iv[16] = {}; - gcry_cipher_open(&ciph, GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_CBC, 0); - gcry_cipher_setkey(ciph, key, keylen); - gcry_cipher_setiv(ciph, iv, 16); - if(encrypt) - g_warn_if_fail(gcry_cipher_encrypt(ciph, data, len, NULL, 0) == 0); - else - g_warn_if_fail(gcry_cipher_decrypt(ciph, data, len, NULL, 0) == 0); - gcry_cipher_close(ciph); -} -#else -#define crypt_rnd(buf, len) g_warn_if_fail(gnutls_rnd(GNUTLS_RND_RANDOM, buf, len) == 0) -#define crypt_nonce(buf, len) g_warn_if_fail(gnutls_rnd(GNUTLS_RND_NONCE, buf, len) == 0) -static inline void crypt_aes128cbc(gboolean encrypt, const char *key, size_t keylen, char *data, size_t len) { - gnutls_cipher_hd_t ciph; - char iv[16] = {}; - gnutls_datum_t ivd = { (unsigned char *)iv, 16 }; - gnutls_datum_t keyd = { (unsigned char *)key, keylen }; - gnutls_cipher_init(&ciph, GNUTLS_CIPHER_AES_128_CBC, &keyd, &ivd); - if(encrypt) - g_warn_if_fail(gnutls_cipher_encrypt(ciph, data, len) == 0); - else - g_warn_if_fail(gnutls_cipher_decrypt(ciph, data, len) == 0); - gnutls_cipher_deinit(ciph); -} #endif +#include diff --git a/src/net.c b/src/net.c index 02bb121..6f76f8f 100644 --- a/src/net.c +++ b/src/net.c @@ -1,6 +1,6 @@ /* ncdc - NCurses Direct Connect client - Copyright (c) 2011-2014 Yoran Heling + Copyright (c) 2011-2022 Yoran Heling Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the @@ -46,6 +46,11 @@ ratecalc_t net_in, net_out; typedef struct net_t net_t; +// ALPN protocols +#define ALPN_DEFAULT 0 // default, no ALPN +#define ALPN_NMDC 1 +#define ALPN_ADC 2 + #endif @@ -73,7 +78,7 @@ struct net_t { char laddr[40]; // state ASY,SYN,DIS, ip only gnutls_session_t tls; // state ASY,SYN,DIS (only if tls is enabled) - void (*cb_handshake)(net_t *, const char *); // state ASY, called after complete handshake. + void (*cb_handshake)(net_t *, const char *, int); // state ASY, called after complete handshake. void (*cb_shutdown)(net_t *); // state DIS, called after complete disconnect. gboolean v6 : 4; // state ASY,SYN, whether we're on IPv6 @@ -235,7 +240,7 @@ static int low_send(net_t *n, const char *buf, int len, const char **err) { static void asy_setuppoll(net_t *n); struct synfer_t { - GStaticMutex lock; // protects n->left, any data used within the low_* functions and, in the case of a disconnect, net->sock and net->tls. + GMutex lock; // protects n->left, any data used within the low_* functions and, in the case of a disconnect, net->sock and net->tls. net_t *net; guint64 left; // The transfer thread itself does not need the lock to read this value, only to write. (It is the only writer) int fd; // for uploads @@ -259,7 +264,7 @@ static void syn_new(net_t *n, gboolean upl, guint64 len) { n->syn->net = n; n->syn->upl = upl; - g_static_mutex_init(&n->syn->lock); + g_mutex_init(&n->syn->lock); if(pipe(n->syn->can) < 0) { g_critical("pipe() failed: %s", g_strerror(errno)); g_return_if_reached(); @@ -328,13 +333,13 @@ static gboolean syn_done(gpointer dat) { // success, 0 if the operation has been cancelled. static int syn_wait(synfer_t *s, int sock, gboolean write) { // Lock to get the socket fd - g_static_mutex_lock(&s->lock); + g_mutex_lock(&s->lock); GPollFD fds[2] = {}; fds[0].fd = s->can[0]; fds[0].events = G_IO_IN; fds[1].fd = sock; fds[1].events = write ? G_IO_OUT : G_IO_IN; - g_static_mutex_unlock(&s->lock); + g_mutex_unlock(&s->lock); // Poll for burst int b = 0; @@ -400,10 +405,10 @@ static void syn_upload_sendfile(synfer_t *s, int sock, fadv_t *adv) { // ratecalc thing and update timeout_last. ratecalc_add(&net_out, r); ratecalc_add(&s->net->rate_out, r); - g_static_mutex_lock(&s->lock); + g_mutex_lock(&s->lock); time(&s->net->timeout_last); s->left -= r; - g_static_mutex_unlock(&s->lock); + g_mutex_unlock(&s->lock); continue; } else if(errno == EAGAIN || errno == EINTR) { continue; @@ -447,7 +452,7 @@ static void syn_upload_buf(synfer_t *s, int sock, fadv_t *adv) { if(b <= 0) goto done; - g_static_mutex_lock(&s->lock); + g_mutex_lock(&s->lock); const char *err = NULL; int wr = s->cancel || !s->net->sock ? 0 : low_send(s->net, p, MIN(rd, b), &err); // successful write @@ -456,7 +461,7 @@ static void syn_upload_buf(synfer_t *s, int sock, fadv_t *adv) { s->left -= wr; rd -= wr; } - g_static_mutex_unlock(&s->lock); + g_mutex_unlock(&s->lock); if(!wr) // cancelled goto done; @@ -482,12 +487,12 @@ static void syn_download(synfer_t *s, int sock) { if(b <= 0) break; - g_static_mutex_lock(&s->lock); + g_mutex_lock(&s->lock); const char *err = NULL; int r = s->cancel || !s->net->sock ? 0 : low_recv(s->net, buf, MIN(NET_TRANS_BUF, s->left), &err); if(r > 0) s->left -= r; - g_static_mutex_unlock(&s->lock); + g_mutex_unlock(&s->lock); if(!r) break; @@ -513,10 +518,10 @@ static void syn_thread(gpointer dat, gpointer udat) { // Make a copy of sock to make sure it doesn't disappear on us. // (Still need to obtain the lock to make use of it). - g_static_mutex_lock(&s->lock); + g_mutex_lock(&s->lock); int sock = s->net->sock; gboolean tls = !!s->net->tls; - g_static_mutex_unlock(&s->lock); + g_mutex_unlock(&s->lock); if(sock && !s->cancel && s->upl) { fadv_t adv; @@ -555,9 +560,9 @@ static void syn_start(net_t *n) { guint64 net_left(net_t *n) { if(!n->syn) return 0; - g_static_mutex_lock(&n->syn->lock); + g_mutex_lock(&n->syn->lock); guint64 r = n->syn->left; - g_static_mutex_unlock(&n->syn->lock); + g_mutex_unlock(&n->syn->lock); return r; } @@ -753,7 +758,6 @@ static gboolean asy_write(net_t *n) { return TRUE; } - static gboolean asy_handshake(net_t *n) { if(!n->tls_handshake) return TRUE; @@ -772,9 +776,24 @@ static gboolean asy_handshake(net_t *n) { g_debug("%s: TLS Handshake successful, KP=SHA256/%s", net_remoteaddr(n), kpf); n->tls_handshake = FALSE; gboolean ret = TRUE; + + int alpn_selected = ALPN_DEFAULT; + +#if GNUTLS_VERSION_NUMBER >= 0x030200 + gnutls_datum_t neg; + if(gnutls_alpn_get_selected_protocol(n->tls, &neg) == GNUTLS_E_SUCCESS) { + g_debug("%s: ALPN negotiated: %.*s", net_remoteaddr(n), (int)neg.size, neg.data); + if (neg.size == 3 && !memcmp(neg.data, "adc", neg.size)) { + alpn_selected = ALPN_ADC; + } else if (neg.size == 4 && !memcmp(neg.data, "nmdc", neg.size)) { + alpn_selected = ALPN_NMDC; + } + } +#endif + if(n->cb_handshake) { net_ref(n); - n->cb_handshake(n, *kpf ? kpr : NULL); + n->cb_handshake(n, *kpf ? kpr : NULL, alpn_selected); n->cb_handshake = NULL; ret = n->state == NETST_ASY; net_unref(n); @@ -969,6 +988,12 @@ void net_shutdown(net_t *n, void(*cb)(net_t *)) { dis_shutdown(n); } +#if GNUTLS_VERSION_NUMBER >= 0x030200 +static const gnutls_datum_t alpn_protos[2] = { + { (unsigned char *)"adc", 3 }, + { (unsigned char *)"nmdc", 4 } +}; +#endif // Enables TLS-mode and initates the handshake. May not be called when there's // something in the write buffer. If the read buffer is not empty, its contents @@ -978,7 +1003,7 @@ void net_shutdown(net_t *n, void(*cb)(net_t *)) { // be sent as first argument. NULL otherwise. // Once TLS is enabled, it's not possible to switch back to a raw connection // again. -void net_settls(net_t *n, gboolean serv, void (*cb)(net_t *, const char *)) { +void net_settls(net_t *n, gboolean serv, gboolean negotiate, void (*cb)(net_t *, const char *, int)) { g_return_if_fail(n->state == NETST_ASY); g_return_if_fail(!n->wbuf->len); g_return_if_fail(!n->tls); @@ -995,6 +1020,12 @@ void net_settls(net_t *n, gboolean serv, void (*cb)(net_t *, const char *)) { gnutls_transport_set_push_function(n->tls, tls_push); gnutls_transport_set_pull_function(n->tls, tls_pull); +#if GNUTLS_VERSION_NUMBER >= 0x030200 + if(negotiate) { + gnutls_alpn_set_protocols(n->tls, alpn_protos, 2, 0); + } +#endif + n->cb_handshake = cb; n->tls_handshake = TRUE; asy_handshake(n); @@ -1207,10 +1238,10 @@ static void dnscon_thread(gpointer dat, gpointer udat) { time_t net_last_activity(net_t *n) { if(n->syn) - g_static_mutex_lock(&n->syn->lock); + g_mutex_lock(&n->syn->lock); time_t last = n->timeout_last; if(n->syn) - g_static_mutex_unlock(&n->syn->lock); + g_mutex_unlock(&n->syn->lock); return last; } @@ -1339,7 +1370,7 @@ void net_disconnect(net_t *n) { // If we're in the SYN state, then the socket and tls session are in control // of the file transfer thread. Hence the need for the conditional locks. if(s) - g_static_mutex_lock(&s->lock); + g_mutex_lock(&s->lock); if(n->tls) { gnutls_deinit(n->tls); n->tls = NULL; @@ -1350,7 +1381,7 @@ void net_disconnect(net_t *n) { } time(&n->timeout_last); if(s) - g_static_mutex_unlock(&s->lock); + g_mutex_unlock(&s->lock); if(n->rbuf) { g_string_free(n->rbuf, TRUE); diff --git a/src/proto.c b/src/proto.c index 2a8b3d2..b9323c3 100644 --- a/src/proto.c +++ b/src/proto.c @@ -1,6 +1,6 @@ /* ncdc - NCurses Direct Connect client - Copyright (c) 2011-2014 Yoran Heling + Copyright (c) 2011-2022 Yoran Heling Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the diff --git a/src/search.c b/src/search.c index 5c77473..16a4a79 100644 --- a/src/search.c +++ b/src/search.c @@ -1,6 +1,6 @@ /* ncdc - NCurses Direct Connect client - Copyright (c) 2011-2014 Yoran Heling + Copyright (c) 2011-2022 Yoran Heling Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the @@ -169,7 +169,7 @@ gboolean search_add(hub_t *hub, search_q_t *q, GError **err) { } if(var_get_int(0, VAR_sudp_policy) == VAR_SUDPP_PREFER) - crypt_nonce(q->key, 16); + g_warn_if_fail(gnutls_rnd(GNUTLS_RND_NONCE, q->key, 16) == 0); // Search a single hub if(hub) { diff --git a/src/strutil.c b/src/strutil.c index 2be37cc..1930ee6 100644 --- a/src/strutil.c +++ b/src/strutil.c @@ -1,6 +1,6 @@ /* ncdc - NCurses Direct Connect client - Copyright (c) 2011-2014 Yoran Heling + Copyright (c) 2011-2022 Yoran Heling Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the diff --git a/src/tth.c b/src/tth.c index 36d9c5b..ff64b6b 100644 --- a/src/tth.c +++ b/src/tth.c @@ -1,6 +1,6 @@ /* ncdc - NCurses Direct Connect client - Copyright (c) 2011-2014 Yoran Heling + Copyright (c) 2011-2022 Yoran Heling Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the diff --git a/src/ui.c b/src/ui.c index 312ccd4..32f8c52 100644 --- a/src/ui.c +++ b/src/ui.c @@ -1,6 +1,6 @@ /* ncdc - NCurses Direct Connect client - Copyright (c) 2011-2014 Yoran Heling + Copyright (c) 2011-2022 Yoran Heling Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the @@ -288,7 +288,7 @@ void ui_init(gboolean bracketed_paste) { static void ui_draw_status() { - if(fl_refresh_queue && fl_refresh_queue->head) + if(fl_is_refreshing()) mvaddstr(winrows-1, 0, "[Refreshing share]"); else if(fl_hash_queue && g_hash_table_size(fl_hash_queue)) mvprintw(winrows-1, 0, "[Hashing: %d / %s / %.2f MiB/s]", @@ -481,6 +481,14 @@ void ui_daychange(const char *day) { g_free(msg); } +// Get the extra flag used to annotate files which are present in the download +// queue (Q) or already shared (S). Currently used in file list and search tabs. +char ui_file_flag(const char *tth) { + return + fl_local_from_tth(tth) ? 'S' : + g_hash_table_lookup(dl_queue, tth) ? 'Q' : + ' '; +} void ui_input(guint64 key) { ui_tab_t *curtab = ui_tab_cur->data; diff --git a/src/ui_colors.c b/src/ui_colors.c index 081942e..c4eb0cf 100644 --- a/src/ui_colors.c +++ b/src/ui_colors.c @@ -1,6 +1,6 @@ /* ncdc - NCurses Direct Connect client - Copyright (c) 2011-2014 Yoran Heling + Copyright (c) 2011-2022 Yoran Heling Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the diff --git a/src/ui_listing.c b/src/ui_listing.c index f36583d..cb7a713 100644 --- a/src/ui_listing.c +++ b/src/ui_listing.c @@ -1,6 +1,6 @@ /* ncdc - NCurses Direct Connect client - Copyright (c) 2011-2014 Yoran Heling + Copyright (c) 2011-2022 Yoran Heling Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the diff --git a/src/ui_logwindow.c b/src/ui_logwindow.c index 13f00b3..759414a 100644 --- a/src/ui_logwindow.c +++ b/src/ui_logwindow.c @@ -1,6 +1,6 @@ /* ncdc - NCurses Direct Connect client - Copyright (c) 2011-2014 Yoran Heling + Copyright (c) 2011-2022 Yoran Heling Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the diff --git a/src/ui_textinput.c b/src/ui_textinput.c index 206921e..329519e 100644 --- a/src/ui_textinput.c +++ b/src/ui_textinput.c @@ -1,6 +1,6 @@ /* ncdc - NCurses Direct Connect client - Copyright (c) 2011-2014 Yoran Heling + Copyright (c) 2011-2022 Yoran Heling Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the diff --git a/src/uit_conn.c b/src/uit_conn.c index 4137971..28a0c81 100644 --- a/src/uit_conn.c +++ b/src/uit_conn.c @@ -1,6 +1,6 @@ /* ncdc - NCurses Direct Connect client - Copyright (c) 2011-2014 Yoran Heling + Copyright (c) 2011-2022 Yoran Heling Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the @@ -117,16 +117,8 @@ static void t_draw_row(ui_listing_t *list, GSequenceIter *iter, int row, void *d cc->state == CCS_IDLE ? 'I' : cc->dl ? 'D' : 'U'); mvaddch(row, 3, cc->tls ? 't' : ' '); - if(cc->nick) - mvaddnstr(row, 5, cc->nick, str_offset_from_columns(cc->nick, 15)); - else { - char tmp[30]; - strcpy(tmp, "IP:"); - strcat(tmp, cc->remoteaddr); - if(strchr(tmp+3, ':')) - *(strchr(tmp+3, ':')) = 0; - mvaddstr(row, 5, tmp); - } + char *tmp = cc->nick ? cc->nick : cc->remoteaddr; + mvaddnstr(row, 5, tmp, str_offset_from_columns(tmp, 15)); if(cc->hub) mvaddnstr(row, 21, cc->hub->tab->name, str_offset_from_columns(cc->hub->tab->name, 11)); @@ -336,6 +328,18 @@ static void t_key(ui_tab_t *tab, guint64 key) { } break; + case INPT_CHAR('b'): // b (/browse userlist) + case INPT_CHAR('B'): // B (force /browse userlist) + if(!cc) + ui_m(NULL, 0, "Nothing selected."); + else if(!cc->hub || !cc->uid) + ui_m(NULL, 0, "User or hub unknown."); + else if(!g_hash_table_lookup(hub_uids, &cc->uid)) + ui_m(NULL, 0, "User is not online."); + else + uit_fl_queue(cc->uid, key == INPT_CHAR('B'), NULL, tab, TRUE, FALSE); + break; + #define S(num, name)\ case INPT_CHAR('0'+num):\ t->s_##name = !t->s_##name;\ diff --git a/src/uit_dl.c b/src/uit_dl.c index 5c09e98..7483328 100644 --- a/src/uit_dl.c +++ b/src/uit_dl.c @@ -1,6 +1,6 @@ /* ncdc - NCurses Direct Connect client - Copyright (c) 2011-2014 Yoran Heling + Copyright (c) 2011-2022 Yoran Heling Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the @@ -54,7 +54,7 @@ static gint sort_func(gconstpointer da, gconstpointer db, gpointer dat) { // Note that we sort on username, uid. But we do not get a notification when a // user changes offline/online state, thus don't have the ability to keep the // list sorted reliably. This isn't a huge problem, though, the list is -// removed/recreated every time an other dl item is selected. This sorting is +// removed/recreated every time another dl item is selected. This sorting is // just better than having the users in completely random order all the time. static gint dud_sort_func(gconstpointer da, gconstpointer db, gpointer dat) { const dl_user_dl_t *a = da; @@ -190,7 +190,7 @@ static void dud_draw_row(ui_listing_t *list, GSequenceIter *iter, int row, void else if(dud->u->active == dud) mvaddstr(row, 36, "Downloading."); else if(dud->u->state == DLU_ACT) - mvaddstr(row, 36, "Downloading an other file."); + mvaddstr(row, 36, "Downloading another file."); else if(dud->u->state == DLU_EXP) mvaddstr(row, 36, "Connecting."); else @@ -224,10 +224,10 @@ static void t_draw(ui_tab_t *tab) { if(sel) { char hash[40] = {}; base32_encode(sel->hash, hash); - mvprintw(bottom, 0, hash); + mvaddstr(bottom, 0, hash); } else mvaddstr(bottom, 0, "Nothing selected."); - mvprintw(bottom, wincols-19, "%5d files - %3d%%", g_hash_table_size(dl_queue), pos); + mvprintw(bottom, wincols-19, "%5d file%c - %3d%%", g_hash_table_size(dl_queue), g_hash_table_size(dl_queue) == 1 ? ' ' : 's', pos); attroff(UIC(separator)); // error info diff --git a/src/uit_fl.c b/src/uit_fl.c index 3e7eb11..52ff6cc 100644 --- a/src/uit_fl.c +++ b/src/uit_fl.c @@ -1,6 +1,6 @@ /* ncdc - NCurses Direct Connect client - Copyright (c) 2011-2014 Yoran Heling + Copyright (c) 2011-2022 Yoran Heling Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the @@ -91,7 +91,7 @@ static void setdir(tab_t *t, fl_list_t *fl, fl_list_t *sel) { if(sel == g_ptr_array_index(fl->sub, i)) seli = iter; } - t->list = ui_listing_create(seq, NULL, NULL, get_name); + t->list = ui_listing_create(seq, NULL, t, get_name); if(seli) t->list->sel = seli; } @@ -329,13 +329,17 @@ static char *t_title(ui_tab_t *tab) { static void draw_row(ui_listing_t *list, GSequenceIter *iter, int row, void *dat) { fl_list_t *fl = g_sequence_get(iter); + tab_t *t = dat; attron(iter == list->sel ? UIC(list_select) : UIC(list_default)); mvhline(row, 0, ' ', wincols); if(iter == list->sel) mvaddstr(row, 0, ">"); - mvaddch(row, 2, fl->isfile && !fl->hastth ? 'H' :' '); + if(t->uid) // add shared/queued flags only while browsing others' lists. + mvaddch(row, 2, ui_file_flag(fl->tth)); + else + mvaddch(row, 2, fl->isfile && !fl->hastth ? 'H' :' '); mvaddstr(row, 4, str_formatsize(fl->size)); if(!fl->isfile) diff --git a/src/uit_hub.c b/src/uit_hub.c index 8cda11e..a669660 100644 --- a/src/uit_hub.c +++ b/src/uit_hub.c @@ -1,6 +1,6 @@ /* ncdc - NCurses Direct Connect client - Copyright (c) 2011-2014 Yoran Heling + Copyright (c) 2011-2022 Yoran Heling Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the @@ -127,8 +127,10 @@ static void t_draw(ui_tab_t *tab) { char *addr = var_get(tab->hub->id, VAR_hubaddr); char *conn = !listen_hub_active(tab->hub->id) ? g_strdup("[passive]") : g_strdup_printf("[active: %s]", hub_ip(tab->hub)); - char *tmp = g_strdup_printf("%s @ %s%s %s", tab->hub->nick, addr, - tab->hub->isop ? " (operator)" : tab->hub->isreg ? " (registered)" : "", conn); + char *proto = tab->hub->adc ? "[adc]" : "[nmdc]"; + char *tmp = g_strdup_printf("%s @ %s%s %s %s", tab->hub->nick, addr, + tab->hub->isop ? " (operator)" : tab->hub->isreg ? " (registered)" : "", + conn, proto); g_free(conn); mvaddstr(winrows-4, 0, tmp); g_free(tmp); diff --git a/src/uit_main.c b/src/uit_main.c index 33bc0bb..c11f2fc 100644 --- a/src/uit_main.c +++ b/src/uit_main.c @@ -1,6 +1,6 @@ /* ncdc - NCurses Direct Connect client - Copyright (c) 2011-2014 Yoran Heling + Copyright (c) 2011-2022 Yoran Heling Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the @@ -45,7 +45,7 @@ ui_tab_t *uit_main_create() { ui_mf(tab, 0, "Welcome to ncdc %s!", main_version); ui_m(tab, 0, "Check out the manual page for a general introduction to ncdc.\n" - "Make sure you always run the latest version available from http://dev.yorhel.nl/ncdc\n"); + "Make sure you always run the latest version available from https://dev.yorhel.nl/ncdc\n"); ui_mf(tab, 0, "Using working directory: %s", db_dir); return tab; diff --git a/src/uit_msg.c b/src/uit_msg.c index 4018391..0b220c1 100644 --- a/src/uit_msg.c +++ b/src/uit_msg.c @@ -1,6 +1,6 @@ /* ncdc - NCurses Direct Connect client - Copyright (c) 2011-2014 Yoran Heling + Copyright (c) 2011-2022 Yoran Heling Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the diff --git a/src/uit_search.c b/src/uit_search.c index fbb91e9..5a9d8a3 100644 --- a/src/uit_search.c +++ b/src/uit_search.c @@ -1,6 +1,6 @@ /* ncdc - NCurses Direct Connect client - Copyright (c) 2011-2014 Yoran Heling + Copyright (c) 2011-2022 Yoran Heling Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the @@ -191,7 +191,6 @@ static char *t_title(ui_tab_t *tab) { } -// TODO: mark already shared and queued files? static void draw_row(ui_listing_t *list, GSequenceIter *iter, int row, void *dat) { search_r_t *r = g_sequence_get(iter); tab_t *t = dat; @@ -221,12 +220,14 @@ static void draw_row(ui_listing_t *list, GSequenceIter *iter, int row, void *dat else mvaddstr(row, i+16, " -"); + mvaddch(row, i+20, ui_file_flag(r->tth)); + char *fn = strrchr(r->file, '/'); if(fn) fn++; else fn = r->file; - mvaddnstr(row, i+21, fn, str_offset_from_columns(fn, wincols-i-21)); + mvaddnstr(row, i+22, fn, str_offset_from_columns(fn, wincols-i-22)); attroff(iter == list->sel ? UIC(list_select) : UIC(list_default)); } @@ -243,7 +244,8 @@ static void t_draw(ui_tab_t *tab) { int i = t->hide_hub ? 22 : 36; mvaddstr(1, i, "Size"); mvaddstr(1, i+12, "Slots"); - mvaddstr(1, i+21, "File"); + mvaddstr(1, i+20, "F"); // short for "Flags" + mvaddstr(1, i+22, "File"); attroff(UIC(list_header)); int bottom = winrows-4; diff --git a/src/uit_userlist.c b/src/uit_userlist.c index 4a0c96b..788d164 100644 --- a/src/uit_userlist.c +++ b/src/uit_userlist.c @@ -1,6 +1,6 @@ /* ncdc - NCurses Direct Connect client - Copyright (c) 2011-2014 Yoran Heling + Copyright (c) 2011-2022 Yoran Heling Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the @@ -168,8 +168,8 @@ static void draw_row(ui_listing_t *list, GSequenceIter *iter, int row, void *dat int j = 6; const char *cc = - !ip4_isany(user->ip4) ? geoip_country4(ip4_unpack(user->ip4)) : - !ip6_isany(user->ip6) ? geoip_country6(ip6_unpack(user->ip6)) : NULL; + !ip4_isany(user->ip4) ? geoip_country(ip4_sockaddr(user->ip4, 0)) : + !ip6_isany(user->ip6) ? geoip_country(ip6_sockaddr(user->ip6, 0)) : NULL; DRAW_COL(row, j, t->cw_country, cc?cc:""); if(t->cw_user > 1) ui_listing_draw_match(list, iter, row, j, str_offset_from_columns(user->name, t->cw_user-1)); diff --git a/src/util.c b/src/util.c index 9ed3bf2..49c0e06 100644 --- a/src/util.c +++ b/src/util.c @@ -1,6 +1,6 @@ /* ncdc - NCurses Direct Connect client - Copyright (c) 2011-2014 Yoran Heling + Copyright (c) 2011-2022 Yoran Heling Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the @@ -101,6 +101,21 @@ void certificate_sha256(gnutls_datum_t cert, char *digest) { } +// Simple single-pass in-place AES-128-CBC encryption using a 16-byte zero'd IV. +void crypt_aes128cbc(gboolean encrypt, const char *key, size_t keylen, char *data, size_t len) { + gnutls_cipher_hd_t ciph; + char iv[16] = {}; + gnutls_datum_t ivd = { (unsigned char *)iv, 16 }; + gnutls_datum_t keyd = { (unsigned char *)key, keylen }; + gnutls_cipher_init(&ciph, GNUTLS_CIPHER_AES_128_CBC, &keyd, &ivd); + if(encrypt) + g_warn_if_fail(gnutls_cipher_encrypt(ciph, data, len) == 0); + else + g_warn_if_fail(gnutls_cipher_decrypt(ciph, data, len) == 0); + gnutls_cipher_deinit(ciph); +} + + // strftime()-like formatting for localtime. The returned string should be // g_free()'d. This function assumes that the resulting string always contains // at least one character. @@ -743,7 +758,7 @@ char *darray_get_dat(char *v, int *l) { #define RCC_MAX RCC_DOWN struct ratecalc_t { - GStaticMutex lock; // protects total, last, rate and burst + GMutex lock; // protects total, last, rate and burst gint64 total; gint64 last; int burst; @@ -752,13 +767,13 @@ struct ratecalc_t { }; #define ratecalc_reset(rc) do {\ - g_static_mutex_lock(&((rc)->lock));\ + g_mutex_lock(&((rc)->lock));\ (rc)->total = (rc)->last = (rc)->rate = (rc)->burst = 0;\ - g_static_mutex_unlock(&((rc)->lock));\ + g_mutex_unlock(&((rc)->lock));\ } while(0) #define ratecalc_init(rc) do {\ - g_static_mutex_init(&((rc)->lock));\ + g_mutex_init(&((rc)->lock));\ ratecalc_unregister(rc);\ ratecalc_reset(rc);\ } while(0) @@ -782,33 +797,33 @@ GSList *ratecalc_list = NULL; void ratecalc_add(ratecalc_t *rc, int b) { - g_static_mutex_lock(&rc->lock); + g_mutex_lock(&rc->lock); rc->total += b; rc->burst -= b; - g_static_mutex_unlock(&rc->lock); + g_mutex_unlock(&rc->lock); } int ratecalc_rate(ratecalc_t *rc) { - g_static_mutex_lock(&rc->lock); + g_mutex_lock(&rc->lock); int r = rc->rate; - g_static_mutex_unlock(&rc->lock); + g_mutex_unlock(&rc->lock); return r; } int ratecalc_burst(ratecalc_t *rc) { - g_static_mutex_lock(&rc->lock); + g_mutex_lock(&rc->lock); int r = rc->burst; - g_static_mutex_unlock(&rc->lock); + g_mutex_unlock(&rc->lock); return r; } gint64 ratecalc_total(ratecalc_t *rc) { - g_static_mutex_lock(&rc->lock); + g_mutex_lock(&rc->lock); gint64 r = rc->total; - g_static_mutex_unlock(&rc->lock); + g_mutex_unlock(&rc->lock); return r; } @@ -833,7 +848,7 @@ void ratecalc_calc() { // Pass one: calculate rc->rate, substract negative burst values from left[] and calculate nums[]. for(n=ratecalc_list; n; n=n->next) { ratecalc_t *rc = n->data; - g_static_mutex_lock(&rc->lock); + g_mutex_lock(&rc->lock); gint64 diff = rc->total - rc->last; rc->rate = diff + ((rc->rate - diff) / 2); rc->last = rc->total; @@ -846,7 +861,7 @@ void ratecalc_calc() { nums[rc->reg]++; else rc->burst = maxburst[rc->reg]; - g_static_mutex_unlock(&rc->lock); + g_mutex_unlock(&rc->lock); } //g_debug("Num: %d - %d - %d", nums[2], nums[3], nums[4]); @@ -870,12 +885,12 @@ void ratecalc_calc() { for(n=ratecalc_list; n; n=n->next) { ratecalc_t *rc = n->data; if(bwp[rc->reg] > 0) { - g_static_mutex_lock(&rc->lock); + g_mutex_lock(&rc->lock); int alloc = MIN(maxburst[rc->reg]-rc->burst, bwp[rc->reg]); //g_debug("Allocing class %d(num=%d), %d new bytes to %d", rc->reg, nums[rc->reg], alloc, rc->burst); rc->burst += alloc; left[rc->reg] -= alloc; - g_static_mutex_unlock(&rc->lock); + g_mutex_unlock(&rc->lock); if(alloc > 0 && alloc < bwp[rc->reg]) nums[rc->reg]--; } diff --git a/src/vars.c b/src/vars.c index dd2d5bb..9fa7878 100644 --- a/src/vars.c +++ b/src/vars.c @@ -1,6 +1,6 @@ /* ncdc - NCurses Direct Connect client - Copyright (c) 2011-2014 Yoran Heling + Copyright (c) 2011-2022 Yoran Heling Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the @@ -386,7 +386,7 @@ static char *i_cid_pid() { // Generate a random PID char pid[24]; - crypt_rnd(pid, 24); + g_warn_if_fail(gnutls_rnd(GNUTLS_RND_RANDOM, pid, 24) == 0); // now hash the PID so we have our CID char cid[24]; @@ -613,9 +613,8 @@ static char *i_ffc() { static gboolean s_geoip_cc(guint64 hub, const char *key, const char *val, GError **err) { #ifdef USE_GEOIP - int v = strcmp(key, "geoip_cc4") == 0 ? 4 : 6; db_vars_set(hub, key, val); - geoip_reinit(v); + geoip_reinit(); return TRUE; #else g_set_error(err, 1, 0, "This option can't be modified: %s.", "Ncdc has not been compiled with GeoIP support"); @@ -743,12 +742,14 @@ static char *p_sendfile(const char *val, GError **err) { #define VAR_TLSP_DISABLE 1 #define VAR_TLSP_ALLOW 2 #define VAR_TLSP_PREFER 4 +#define VAR_TLSP_FORCE 8 #endif static flag_option_t var_tls_policy_ops[] = { { VAR_TLSP_DISABLE, "disabled" }, { VAR_TLSP_ALLOW, "allow" }, { VAR_TLSP_PREFER, "prefer" }, + { VAR_TLSP_FORCE, "force" }, { 0 } }; @@ -962,14 +963,14 @@ struct var_t { V(download_exclude, 1,0, f_id, p_regex, su_old, NULL, NULL, NULL)\ V(download_rate, 1,0, f_speed, p_speed, NULL, NULL, NULL, NULL)\ V(download_segment, 1,0, f_download_segment,p_download_segment,NULL, NULL, NULL, g_strdup_printf("%"G_GUINT64_FORMAT, (guint64)DLFILE_CHUNKSIZE))\ + V(download_shared, 1,0, f_bool, p_bool, su_bool, NULL, NULL, "true")\ V(download_slots, 1,0, f_int, p_int, NULL, NULL, s_download_slots,"3")\ V(email, 1,1, f_id, p_id, su_old, NULL, s_hubinfo, NULL)\ V(encoding, 1,1, f_id, p_encoding, su_encoding, NULL, NULL, "UTF-8")\ V(filelist_maxage, 1,0, f_interval, p_interval, su_old, NULL, NULL, "604800")\ V(fl_done, 0,0, NULL, NULL, NULL, NULL, NULL, "false")\ V(flush_file_cache, 1,0, f_ffc, p_ffc, su_ffc, g_ffc, s_ffc, i_ffc())\ - V(geoip_cc4, 1,0, f_id, p_id, su_path, NULL, s_geoip_cc, NULL)\ - V(geoip_cc6, 1,0, f_id, p_id, su_path, NULL, s_geoip_cc, NULL)\ + V(geoip_cc, 1,0, f_id, p_id, su_path, NULL, s_geoip_cc, NULL)\ V(hash_rate, 1,0, f_speed, p_speed, NULL, NULL, NULL, NULL)\ V(hubaddr, 0,0, NULL, NULL, NULL, NULL, NULL, NULL)\ V(hubkp, 0,0, NULL, NULL, NULL, NULL, NULL, NULL)\ @@ -980,6 +981,7 @@ struct var_t { V(log_downloads, 1,0, f_bool, p_bool, su_bool, NULL, NULL, "true")\ V(log_hubchat, 1,1, f_bool, p_bool, su_bool, NULL, NULL, "true")\ V(log_uploads, 1,0, f_bool, p_bool, su_bool, NULL, NULL, "true")\ + V(max_ul_per_user, 1,1, f_int, p_int_ge1, NULL, NULL, NULL, "1")\ V(minislots, 1,0, f_int, p_int_ge1, NULL, NULL, NULL, "3")\ V(minislot_size, 1,0, f_minislot_size,p_minislot_size, NULL, NULL, NULL, "65536")\ V(nick, 1,1, f_id, p_nick, su_old, NULL, s_nick, i_nick())\ @@ -992,6 +994,7 @@ struct var_t { V(share_exclude, 1,0, f_id, p_regex, su_old, NULL, NULL, NULL)\ V(share_hidden, 1,0, f_bool, p_bool, su_bool, NULL, NULL, "false")\ V(share_symlinks, 1,0, f_bool, p_bool, su_bool, NULL, NULL, "false")\ + V(show_free_slots, 1,1, f_bool, p_bool, su_bool, NULL, s_hubinfo, "false")\ V(show_joinquit, 1,1, f_bool, p_bool, su_bool, NULL, NULL, "false")\ V(slots, 1,0, f_int, p_int_ge1, NULL, NULL, s_hubinfo, "10")\ V(sudp_policy, 1,0, f_sudp_policy, p_sudp_policy, su_sudp_policy,g_sudp_policy,s_sudp_policy, G_STRINGIFY(VAR_SUDPP_PREFER))\ diff --git a/static/build.sh b/static/build.sh deleted file mode 100755 index b7cb958..0000000 --- a/static/build.sh +++ /dev/null @@ -1,330 +0,0 @@ -#!/bin/sh - -# This script assumes the following: -# - You have the following installed on your machine: -# bash -# make -# GNU tar -# wget -# ncurses (with the 'tic' binary) -# perl -# python2 -# git -# (Anything else I forgot) -# - A checkout of the ncdc git repo can be found in "..", and the configure -# script exists (i.e. autoreconf has been run). -# - This script is run on an x86 machine -# -# Usage: -# ./build.sh $arch -# Where $arch = 'arm', 'mipsel', 'ppc', 'i486' or 'x86_64' -# -# TODO: -# - Cross-compile to platforms other than Linux? - - -MUSL_VERSION=1.1.5 -ZLIB_VERSION=1.2.8 -BZIP2_VERSION=1.0.6 -SQLITE_VERSION=3080702 -GMP_VERSION=6.0.0 -NETTLE_VERSION=3.1.1 -IDN_VERSION=1.30 -GNUTLS_VERSION=3.4.0 -NCURSES_VERSION=5.9 -GLIB_VERSION=2.43.1 -GEOIP_VERSION=1.6.3 - - -# We don't actually use pkg-config at all. Setting this variable to 'true' -# effectively tricks autoconf scripts into thinking that we have pkg-config -# installed, and that all packages it probes for exist. We handle the rest with -# manual CPPFLAGS/LDFLAGS. -export PKG_CONFIG=true - -export CFLAGS="-O3 -g -static" - -export HOST_CC=gcc - -# (The variables below are automatically set by the functions, they're defined -# here to make sure they have global scope and for documentation purposes.) - -# This is the arch we're compiling for, e.g. arm/mipsel. -TARGET= -# This is the name of the toolchain we're using, and thus the value we should -# pass to autoconf's --host argument. -HOST= -# Installation prefix. -PREFIX= -# Path of the extracted source code of the package we're currently building. -srcdir= - -mkdir -p tarballs - - -# "Fetch, Extract, Move" -fem() { # base-url name targerdir extractdir - echo "====== Fetching and extracting $1 $2" - cd tarballs - if [ -n "$4" ]; then - EDIR="$4" - else - EDIR=$(basename $(basename $(basename $2 .tar.bz2) .tar.gz) .tar.xz) - fi - if [ ! -e "$2" ]; then - wget "$1$2" || exit - fi - if [ ! -d "$3" ]; then - tar -xvf "$2" || exit - mv "$EDIR" "$3" - fi - cd .. -} - - -prebuild() { # dirname - if [ -e "$TARGET/$1/_built" ]; then - echo "====== Skipping build for $TARGET/$1 (assumed to be done)" - return 1 - fi - echo "====== Starting build for $TARGET/$1" - rm -rf "$TARGET/$1" - mkdir -p "$TARGET/$1" - cd "$TARGET/$1" - srcdir="../../tarballs/$1" - return 0 -} - - -postbuild() { - touch _built - cd ../.. -} - - -# Pre-built cross-compilation binaries for musl. Handy. :) -getmusl() { - # Order of $HOST is different than the tar/dir names, so we need this case. - case $TARGET in - ppc) DIR=powerpc-linux-musl ;; - arm) DIR=arm-linux-musleabi ;; - mipsel) DIR=mipsel-sf-linux-musl ;; - i486) DIR=i486-linux-musl ;; - x86_64) DIR=x86_64-linux-musl ;; - esac - fem https://googledrive.com/host/0BwnS5DMB0YQ6bDhPZkpOYVFhbk0/musl-$MUSL_VERSION/ crossx86-$DIR-$MUSL_VERSION.tar.xz "musl-$TARGET" $DIR - - # Configure scripts don't like 'mipsel-sf-musl-linux', so rename the links in - # the bin/ dir to remove the '-sf' flag. - find tarballs/musl-mipsel/bin -name '*-sf-*' -type l -execdir sh -c 'mv $0 `echo $0 | sed "s/mipsel-sf/mipsel/"`' {} + -} - - -getzlib() { - fem http://zlib.net/ zlib-$ZLIB_VERSION.tar.gz zlib - prebuild zlib || return - # zlib doesn't support out-of-source builds - cp -R $srcdir/* . - CC=$HOST-gcc ./configure --prefix=$PREFIX --static || exit - make install || exit - postbuild -} - - -getbzip2() { - fem http://bzip.org/$BZIP2_VERSION/ bzip2-$BZIP2_VERSION.tar.gz bzip2 - prebuild bzip2 || return - cp -R $srcdir/* . - make CC=$HOST-gcc AR=$HOST-ar RANLIB=$HOST-ranlib libbz2.a || exit - mkdir -p $PREFIX/lib $PREFIX/include - cp libbz2.a $PREFIX/lib - cp bzlib.h $PREFIX/include - postbuild -} - - -getsqlite() { - fem http://sqlite.org/2014/ sqlite-autoconf-$SQLITE_VERSION.tar.gz sqlite - prebuild sqlite || return - $srcdir/configure --prefix=$PREFIX --disable-readline --disable-dynamic-extensions\ - --disable-shared --enable-static --host=$HOST || exit - make install-includeHEADERS install-libLTLIBRARIES || exit - postbuild -} - - -getgmp() { - fem ftp://ftp.gmplib.org/pub/gmp-$GMP_VERSION/ gmp-$GMP_VERSION.tar.xz gmp - prebuild gmp || return - case $TARGET in - mipsel) ABI=o32 ;; - i486) ABI=32 ;; - esac - $srcdir/configure --host=$HOST ABI=$ABI --disable-shared --without-readline --enable-static --prefix=$PREFIX || exit - make install || exit - postbuild -} - - -getnettle() { - fem http://www.lysator.liu.se/~nisse/archive/ nettle-$NETTLE_VERSION.tar.gz nettle - prebuild nettle || return - $srcdir/configure --prefix=$PREFIX --disable-shared --host=$HOST\ - CPPFLAGS="-I$PREFIX/include" LDFLAGS="-L$PREFIX/lib" || exit - make install-headers install-static || exit - postbuild -} - - -getidn() { - fem http://ftp.gnu.org/gnu/libidn/ libidn-$IDN_VERSION.tar.gz idn - prebuild idn || return - $srcdir/configure --prefix=$PREFIX --disable-nls --disable-valgrind-tests --disable-shared\ - --enable-static --host=$HOST CPPFLAGS="-I$PREFIX/include" LDFLAGS="-L$PREFIX/lib" || exit - make install || exit - postbuild -} - - -getgnutls() { - fem ftp://ftp.gnutls.org/gcrypt/gnutls/v${GNUTLS_VERSION%.*}/ gnutls-$GNUTLS_VERSION.tar.xz gnutls - prebuild gnutls || return - # Patch some gnulib stuff to fix building against musl - if [ ! -e "$srcdir/patched" ]; then - for i in "$srcdir/gl/"{printf,fprintf,freadahead,closein,fseterr,vfprintf}.c ; do echo ''>$i; done - echo "#define __printf__ printf" >> "$srcdir/config.h.in" - echo "#define rpl_fprintf fprintf" >> "$srcdir/config.h.in" - touch "$srcdir/patched" - fi - $srcdir/configure --prefix=$PREFIX --disable-gtk-doc-html --disable-shared --disable-silent-rules\ - --enable-static --disable-cxx --disable-srp-authentication --disable-openssl-compatibility\ - --disable-guile --disable-crywrap --with-included-libtasn1 --without-p11-kit --with-nettle-mini\ - --host=$HOST CPPFLAGS="-I$PREFIX/include" LDFLAGS="-L$PREFIX/lib" || exit - make || exit - make -C gl install || exit - make -C lib install || exit - postbuild -} - - -getncurses() { - fem http://ftp.gnu.org/pub/gnu/ncurses/ ncurses-$NCURSES_VERSION.tar.gz ncurses - prebuild ncurses || return - $srcdir/configure --prefix=$PREFIX\ - --without-cxx --without-cxx-binding --without-ada --without-manpages --without-progs\ - --without-tests --without-curses-h --without-pkg-config --without-shared --without-debug\ - --without-gpm --without-sysmouse --enable-widec --with-default-terminfo-dir=/usr/share/terminfo\ - --with-terminfo-dirs=/usr/share/terminfo:/lib/terminfo:/usr/local/share/terminfo\ - --with-fallbacks="screen linux vt100 xterm" --host=$HOST\ - CPPFLAGS=-D_GNU_SOURCE || exit - make || exit - make install.libs || exit - postbuild -} - - -getglib() { - fem http://ftp.gnome.org/pub/gnome/sources/glib/${GLIB_VERSION%.*}/ glib-$GLIB_VERSION.tar.xz glib - prebuild glib || return - export glib_cv_stack_grows=no # Arch - export glib_cv_uscore=no # OS/Arch - export glib_cv_have_strlcpy=yes # libc - export ac_cv_func_posix_getpwuid_r=yes # libc - export ac_cv_func_posix_getgrgid_r=yes # libc - export ac_cv_alignof_guint32=4 # Arch, not mentioned in Glib docs... - export ac_cv_alignof_guint64=8 # Arch, ~ - case $TARGET in # Arch, ~ - x86_64) export ac_cv_alignof_unsigned_long=8 ;; - *) export ac_cv_alignof_unsigned_long=4 ;; - esac - $srcdir/configure --prefix=$PREFIX --enable-static --disable-shared\ - --disable-gtk-doc-html --disable-xattr --disable-fam --disable-dtrace\ - --disable-gcov --disable-modular-tests --with-pcre=internal --disable-silent-rules\ - --disable-compile-warnings --host=$HOST CPPFLAGS=-D_GNU_SOURCE || exit - perl -pi -e 's{(#define GLIB_LOCALE_DIR).+}{$1 "/usr/share/locale"}' config.h - make -C glib/libcharset install - make -C glib/gnulib install - make -C glib/pcre install - make -C glib install-libLTLIBRARIES install-data install-nodist_configexecincludeHEADERS || exit - make -C gthread install || exit - postbuild -} - - -getgeoip() { - fem https://github.com/maxmind/geoip-api-c/releases/download/v${GEOIP_VERSION}/ GeoIP-${GEOIP_VERSION}.tar.gz geoip - prebuild geoip || return - # Build fails on ARM without this check, because autoconf can't figure this - # out when cross-compiling. - export ac_cv_func_malloc_0_nonnull=yes - export ac_cv_func_realloc_0_nonnull=yes - $srcdir/configure --prefix=$PREFIX --host=$HOST --disable-shared || exit - make -C libGeoIP datadir=/usr/share install || exit - postbuild -} - - -getncdc() { - prebuild ncdc || return - srcdir=../../.. - $srcdir/configure --host=$HOST --disable-silent-rules --with-geoip\ - CPPFLAGS="-I$PREFIX/include -D_GNU_SOURCE" LDFLAGS="-static -L$PREFIX/lib -L$PREFIX/lib64 -lz -lbz2"\ - SQLITE_LIBS=-lsqlite3 GEOIP_LIBS=-lGeoIP GNUTLS_LIBS="-lgnutls -lz -lhogweed -lnettle -lgmp"\ - GLIB_LIBS="-pthread -lglib-2.0 -lgthread-2.0"\ - GLIB_CFLAGS="-I$PREFIX/include/glib-2.0 -I$PREFIX/lib/glib-2.0/include" || exit - # Make sure that the Makefile dependencies for makeheaders and gendoc are "up-to-date" - mkdir -p deps deps/.deps doc doc/.deps - touch deps/.dirstamp deps/.deps/.dirstamp deps/makeheaders.o doc/.dirstamp doc/.deps/.dirstamp doc/gendoc.o - gcc $srcdir/deps/makeheaders.c -o makeheaders || exit - gcc -I. -I$srcdir $srcdir/doc/gendoc.c -o gendoc || exit - make || exit - - VER=`cd '../../..' && git describe --abbrev=5 --dirty= | sed s/^v//` - tar -czf ../../ncdc-linux-$TARGET-$VER-unstripped.tar.gz ncdc - $HOST-strip ncdc - tar -czf ../../ncdc-linux-$TARGET-$VER.tar.gz ncdc - echo "====== ncdc-linux-$TARGET-$VER.tar.gz and -unstripped created." - - postbuild -} - - -allncdc() { - getzlib - getbzip2 - getsqlite - getgmp - getnettle - getidn - getgnutls - getncurses - getglib - getgeoip - getncdc -} - - -buildarch() { - TARGET=$1 - case $TARGET in - ppc) HOST=powerpc-musl-linux ;; - arm) HOST=arm-musl-linuxeabi ;; - mipsel) HOST=mipsel-musl-linux ;; - i486) HOST=i486-musl-linux ;; - x86_64) HOST=x86_64-musl-linux ;; - *) echo "Unknown target: $TARGET" ;; - esac - PREFIX="`pwd`/$TARGET/inst" - mkdir -p $TARGET $PREFIX - - getmusl - MBIN="`pwd`/tarballs/musl-$TARGET/bin" - OLDPATH="$PATH" - PATH="$PATH:$MBIN" - allncdc - PATH="$OLDPATH" -} - - -buildarch $1 -