diff --git a/src/Makefile.am b/src/Makefile.am index ac677fad..bfe93ce8 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -10,10 +10,10 @@ AM_CFLAGS += -I$(top_srcdir)/libsodium/src/libsodium/include AM_LDFLAGS = -static SS_COMMON_LIBS = $(top_builddir)/libev/libev.la \ - $(top_builddir)/libipset/libipset.la \ - $(top_builddir)/libcork/libcork.la \ - $(top_builddir)/libsodium/src/libsodium/libsodium.la \ - $(INET_NTOP_LIB) + $(top_builddir)/libipset/libipset.la \ + $(top_builddir)/libcork/libcork.la \ + $(top_builddir)/libsodium/src/libsodium/libsodium.la \ + $(INET_NTOP_LIB) bin_PROGRAMS = ss-local ss-tunnel if !BUILD_WINCOMPAT @@ -23,30 +23,32 @@ endif ss_local_SOURCES = utils.c \ jconf.c \ json.c \ - encrypt.c \ - udprelay.c \ - cache.c \ - acl.c \ - netutils.c \ - local.c + encrypt.c \ + udprelay.c \ + cache.c \ + acl.c \ + netutils.c \ + bitcoin.c \ + local.c ss_tunnel_SOURCES = utils.c \ - jconf.c \ - json.c \ - encrypt.c \ - udprelay.c \ - cache.c \ - netutils.c \ - tunnel.c + jconf.c \ + json.c \ + encrypt.c \ + udprelay.c \ + cache.c \ + netutils.c \ + tunnel.c ss_server_SOURCES = utils.c \ jconf.c \ json.c \ encrypt.c \ - udprelay.c \ - cache.c \ - acl.c \ - resolv.c \ + udprelay.c \ + cache.c \ + acl.c \ + resolv.c \ + bitcoin.c \ server.c ss_local_LDADD = $(SS_COMMON_LIBS) @@ -71,7 +73,7 @@ ss_redir_SOURCES = utils.c \ jconf.c \ json.c \ encrypt.c \ - netutils.c \ + netutils.c \ redir.c ss_redir_LDADD = $(SS_COMMON_LIBS) ss_redir_LDADD += $(top_builddir)/libudns/libudns.la diff --git a/src/Makefile.in b/src/Makefile.in index 770a0501..20e7a113 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -115,7 +115,7 @@ am__DEPENDENCIES_3 = $(am__DEPENDENCIES_2) \ $(top_builddir)/libudns/libudns.la libshadowsocks_la_DEPENDENCIES = $(am__DEPENDENCIES_3) am__libshadowsocks_la_SOURCES_DIST = utils.c jconf.c json.c encrypt.c \ - udprelay.c cache.c acl.c netutils.c local.c win32.c + udprelay.c cache.c acl.c netutils.c bitcoin.c local.c win32.c @BUILD_WINCOMPAT_TRUE@am__objects_1 = libshadowsocks_la-win32.lo am__objects_2 = libshadowsocks_la-utils.lo libshadowsocks_la-jconf.lo \ libshadowsocks_la-json.lo libshadowsocks_la-encrypt.lo \ @@ -135,14 +135,14 @@ libshadowsocks_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ @BUILD_REDIRECTOR_TRUE@am__EXEEXT_2 = ss-redir$(EXEEXT) PROGRAMS = $(bin_PROGRAMS) am__ss_local_SOURCES_DIST = utils.c jconf.c json.c encrypt.c \ - udprelay.c cache.c acl.c netutils.c local.c win32.c + udprelay.c cache.c acl.c netutils.c bitcoin.c local.c win32.c @BUILD_WINCOMPAT_TRUE@am__objects_3 = ss_local-win32.$(OBJEXT) am_ss_local_OBJECTS = ss_local-utils.$(OBJEXT) \ ss_local-jconf.$(OBJEXT) ss_local-json.$(OBJEXT) \ ss_local-encrypt.$(OBJEXT) ss_local-udprelay.$(OBJEXT) \ ss_local-cache.$(OBJEXT) ss_local-acl.$(OBJEXT) \ - ss_local-netutils.$(OBJEXT) ss_local-local.$(OBJEXT) \ - $(am__objects_3) + ss_local-netutils.$(OBJEXT) ss_local-bitcoin.$(OBJEXT) \ + ss_local-local.$(OBJEXT) $(am__objects_3) ss_local_OBJECTS = $(am_ss_local_OBJECTS) ss_local_DEPENDENCIES = $(am__DEPENDENCIES_2) \ $(top_builddir)/libudns/libudns.la @@ -162,7 +162,8 @@ am_ss_server_OBJECTS = ss_server-utils.$(OBJEXT) \ ss_server-jconf.$(OBJEXT) ss_server-json.$(OBJEXT) \ ss_server-encrypt.$(OBJEXT) ss_server-udprelay.$(OBJEXT) \ ss_server-cache.$(OBJEXT) ss_server-acl.$(OBJEXT) \ - ss_server-resolv.$(OBJEXT) ss_server-server.$(OBJEXT) + ss_server-resolv.$(OBJEXT) ss_server-bitcoin.$(OBJEXT) \ + ss_server-server.$(OBJEXT) ss_server_OBJECTS = $(am_ss_server_OBJECTS) ss_server_DEPENDENCIES = $(am__DEPENDENCIES_2) \ $(top_builddir)/libudns/libudns.la @@ -359,7 +360,7 @@ SS_COMMON_LIBS = $(top_builddir)/libev/libev.la \ $(INET_NTOP_LIB) ss_local_SOURCES = utils.c jconf.c json.c encrypt.c udprelay.c cache.c \ - acl.c netutils.c local.c $(am__append_2) + acl.c netutils.c bitcoin.c local.c $(am__append_2) ss_tunnel_SOURCES = utils.c jconf.c json.c encrypt.c udprelay.c \ cache.c netutils.c tunnel.c $(am__append_3) ss_server_SOURCES = utils.c \ @@ -370,6 +371,7 @@ ss_server_SOURCES = utils.c \ cache.c \ acl.c \ resolv.c \ + bitcoin.c \ server.c ss_local_LDADD = $(SS_COMMON_LIBS) $(top_builddir)/libudns/libudns.la @@ -382,7 +384,7 @@ ss_server_CFLAGS = $(AM_CFLAGS) -DUDPRELAY_REMOTE @BUILD_REDIRECTOR_TRUE@ jconf.c \ @BUILD_REDIRECTOR_TRUE@ json.c \ @BUILD_REDIRECTOR_TRUE@ encrypt.c \ -@BUILD_REDIRECTOR_TRUE@ netutils.c \ +@BUILD_REDIRECTOR_TRUE@ netutils.c \ @BUILD_REDIRECTOR_TRUE@ redir.c @BUILD_REDIRECTOR_TRUE@ss_redir_LDADD = $(SS_COMMON_LIBS) \ @@ -530,6 +532,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/jconf.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/json.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libshadowsocks_la-acl.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libshadowsocks_la-bitcoin.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libshadowsocks_la-cache.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libshadowsocks_la-encrypt.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libshadowsocks_la-jconf.Plo@am__quote@ @@ -542,6 +545,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/netutils.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/redir.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ss_local-acl.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ss_local-bitcoin.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ss_local-cache.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ss_local-encrypt.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ss_local-jconf.Po@am__quote@ @@ -552,6 +556,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ss_local-utils.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ss_local-win32.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ss_server-acl.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ss_server-bitcoin.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ss_server-cache.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ss_server-encrypt.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ss_server-jconf.Po@am__quote@ @@ -651,6 +656,13 @@ libshadowsocks_la-netutils.lo: netutils.c @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libshadowsocks_la_CFLAGS) $(CFLAGS) -c -o libshadowsocks_la-netutils.lo `test -f 'netutils.c' || echo '$(srcdir)/'`netutils.c +libshadowsocks_la-bitcoin.lo: bitcoin.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libshadowsocks_la_CFLAGS) $(CFLAGS) -MT libshadowsocks_la-bitcoin.lo -MD -MP -MF $(DEPDIR)/libshadowsocks_la-bitcoin.Tpo -c -o libshadowsocks_la-bitcoin.lo `test -f 'bitcoin.c' || echo '$(srcdir)/'`bitcoin.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libshadowsocks_la-bitcoin.Tpo $(DEPDIR)/libshadowsocks_la-bitcoin.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='bitcoin.c' object='libshadowsocks_la-bitcoin.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libshadowsocks_la_CFLAGS) $(CFLAGS) -c -o libshadowsocks_la-bitcoin.lo `test -f 'bitcoin.c' || echo '$(srcdir)/'`bitcoin.c + libshadowsocks_la-local.lo: local.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libshadowsocks_la_CFLAGS) $(CFLAGS) -MT libshadowsocks_la-local.lo -MD -MP -MF $(DEPDIR)/libshadowsocks_la-local.Tpo -c -o libshadowsocks_la-local.lo `test -f 'local.c' || echo '$(srcdir)/'`local.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libshadowsocks_la-local.Tpo $(DEPDIR)/libshadowsocks_la-local.Plo @@ -777,6 +789,20 @@ ss_local-netutils.obj: netutils.c @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_local_CFLAGS) $(CFLAGS) -c -o ss_local-netutils.obj `if test -f 'netutils.c'; then $(CYGPATH_W) 'netutils.c'; else $(CYGPATH_W) '$(srcdir)/netutils.c'; fi` +ss_local-bitcoin.o: bitcoin.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_local_CFLAGS) $(CFLAGS) -MT ss_local-bitcoin.o -MD -MP -MF $(DEPDIR)/ss_local-bitcoin.Tpo -c -o ss_local-bitcoin.o `test -f 'bitcoin.c' || echo '$(srcdir)/'`bitcoin.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ss_local-bitcoin.Tpo $(DEPDIR)/ss_local-bitcoin.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='bitcoin.c' object='ss_local-bitcoin.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_local_CFLAGS) $(CFLAGS) -c -o ss_local-bitcoin.o `test -f 'bitcoin.c' || echo '$(srcdir)/'`bitcoin.c + +ss_local-bitcoin.obj: bitcoin.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_local_CFLAGS) $(CFLAGS) -MT ss_local-bitcoin.obj -MD -MP -MF $(DEPDIR)/ss_local-bitcoin.Tpo -c -o ss_local-bitcoin.obj `if test -f 'bitcoin.c'; then $(CYGPATH_W) 'bitcoin.c'; else $(CYGPATH_W) '$(srcdir)/bitcoin.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ss_local-bitcoin.Tpo $(DEPDIR)/ss_local-bitcoin.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='bitcoin.c' object='ss_local-bitcoin.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_local_CFLAGS) $(CFLAGS) -c -o ss_local-bitcoin.obj `if test -f 'bitcoin.c'; then $(CYGPATH_W) 'bitcoin.c'; else $(CYGPATH_W) '$(srcdir)/bitcoin.c'; fi` + ss_local-local.o: local.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_local_CFLAGS) $(CFLAGS) -MT ss_local-local.o -MD -MP -MF $(DEPDIR)/ss_local-local.Tpo -c -o ss_local-local.o `test -f 'local.c' || echo '$(srcdir)/'`local.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ss_local-local.Tpo $(DEPDIR)/ss_local-local.Po @@ -917,6 +943,20 @@ ss_server-resolv.obj: resolv.c @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -c -o ss_server-resolv.obj `if test -f 'resolv.c'; then $(CYGPATH_W) 'resolv.c'; else $(CYGPATH_W) '$(srcdir)/resolv.c'; fi` +ss_server-bitcoin.o: bitcoin.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -MT ss_server-bitcoin.o -MD -MP -MF $(DEPDIR)/ss_server-bitcoin.Tpo -c -o ss_server-bitcoin.o `test -f 'bitcoin.c' || echo '$(srcdir)/'`bitcoin.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ss_server-bitcoin.Tpo $(DEPDIR)/ss_server-bitcoin.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='bitcoin.c' object='ss_server-bitcoin.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -c -o ss_server-bitcoin.o `test -f 'bitcoin.c' || echo '$(srcdir)/'`bitcoin.c + +ss_server-bitcoin.obj: bitcoin.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -MT ss_server-bitcoin.obj -MD -MP -MF $(DEPDIR)/ss_server-bitcoin.Tpo -c -o ss_server-bitcoin.obj `if test -f 'bitcoin.c'; then $(CYGPATH_W) 'bitcoin.c'; else $(CYGPATH_W) '$(srcdir)/bitcoin.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ss_server-bitcoin.Tpo $(DEPDIR)/ss_server-bitcoin.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='bitcoin.c' object='ss_server-bitcoin.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -c -o ss_server-bitcoin.obj `if test -f 'bitcoin.c'; then $(CYGPATH_W) 'bitcoin.c'; else $(CYGPATH_W) '$(srcdir)/bitcoin.c'; fi` + ss_server-server.o: server.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -MT ss_server-server.o -MD -MP -MF $(DEPDIR)/ss_server-server.Tpo -c -o ss_server-server.o `test -f 'server.c' || echo '$(srcdir)/'`server.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ss_server-server.Tpo $(DEPDIR)/ss_server-server.Po diff --git a/src/bitcoin.c b/src/bitcoin.c new file mode 100644 index 00000000..2284ac89 --- /dev/null +++ b/src/bitcoin.c @@ -0,0 +1,702 @@ +/* + * bitcoin.c + * + * Copyright (C) 2015, Kevin Pan + * + * This file is part of the shadowsocks-libev. + * + * shadowsocks-libev is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * shadowsocks-libev is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with shadowsocks-libev; see the file COPYING. If not, see + * . + */ +#include "bitcoin.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +// +// address prefix +// @see https://en.bitcoin.it/wiki/List_of_address_prefixes +// +#define BITCOIN_ADDRESS_PREFIX_PUBKEY 0x00 +#define BITCOIN_ADDRESS_PREFIX_PUBKEY_TESTNET 0x6F + +#define BITCOIN_PUBKEY_UNCOMPRESSED 0x00 +#define BITCOIN_PUBKEY_COMPRESSED 0x01 + + +#define skip_char(c) \ +(((c) == '\r') || ((c) == '\n') || ((c) == ' ') || ((c) == '\t')) + +const char *vg_b58_alphabet = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"; +const signed char vg_b58_reverse_map[256] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, -1, -1, -1, -1, -1, -1, + -1, 9, 10, 11, 12, 13, 14, 15, 16, -1, 17, 18, 19, 20, 21, -1, + 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, -1, -1, -1, -1, -1, + -1, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, -1, 44, 45, 46, + 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 +}; + + +static int vg_b58_decode_check(const char *input, void *buf, size_t len) { + int i, l, c; + unsigned char *xbuf = NULL; + BIGNUM bn, bnw, bnbase; + BN_CTX *bnctx; + unsigned char hash1[32], hash2[32]; + int zpfx; + int res = 0; + + BN_init(&bn); + BN_init(&bnw); + BN_init(&bnbase); + BN_set_word(&bnbase, 58); + bnctx = BN_CTX_new(); + + /* Build a bignum from the encoded value */ + l = strlen(input); + for (i = 0; i < l; i++) { + if (skip_char(input[i])) + continue; + c = vg_b58_reverse_map[(int)input[i]]; + if (c < 0) + goto out; + BN_clear(&bnw); + BN_set_word(&bnw, c); + BN_mul(&bn, &bn, &bnbase, bnctx); + BN_add(&bn, &bn, &bnw); + } + + /* Copy the bignum to a byte buffer */ + for (i = 0, zpfx = 0; input[i]; i++) { + if (skip_char(input[i])) + continue; + if (input[i] != vg_b58_alphabet[0]) + break; + zpfx++; + } + c = BN_num_bytes(&bn); + l = zpfx + c; + if (l < 5) + goto out; + xbuf = (unsigned char *) malloc(l); + if (!xbuf) + goto out; + if (zpfx) + memset(xbuf, 0, zpfx); + if (c) + BN_bn2bin(&bn, xbuf + zpfx); + + /* Check the hash code */ + l -= 4; + SHA256(xbuf, l, hash1); + SHA256(hash1, sizeof(hash1), hash2); + if (memcmp(hash2, xbuf + l, 4)) + goto out; + /* Buffer verified */ + if (len) { + if (len > l) + len = l; + memcpy(buf, xbuf, len); + } + res = l; + +out: + if (xbuf) + free(xbuf); + BN_clear_free(&bn); + BN_clear_free(&bnw); + BN_clear_free(&bnbase); + BN_CTX_free(bnctx); + return res; +} + +static void vg_b58_encode_check(void *buf, size_t len, char *result) +{ + unsigned char hash1[32]; + unsigned char hash2[32]; + + int d, p; + + BN_CTX *bnctx; + BIGNUM *bn, *bndiv, *bntmp; + BIGNUM bna, bnb, bnbase, bnrem; + unsigned char *binres; + int brlen, zpfx; + + bnctx = BN_CTX_new(); + BN_init(&bna); + BN_init(&bnb); + BN_init(&bnbase); + BN_init(&bnrem); + BN_set_word(&bnbase, 58); + + bn = &bna; + bndiv = &bnb; + + brlen = (2 * len) + 4; + binres = (unsigned char*) malloc(brlen); + memcpy(binres, buf, len); + + SHA256(binres, len, hash1); + SHA256(hash1, sizeof(hash1), hash2); + memcpy(&binres[len], hash2, 4); + + BN_bin2bn(binres, len + 4, bn); + + for (zpfx = 0; zpfx < (len + 4) && binres[zpfx] == 0; zpfx++); + + p = (int)brlen; + while (!BN_is_zero(bn)) { + BN_div(bndiv, &bnrem, bn, &bnbase, bnctx); + bntmp = bn; + bn = bndiv; + bndiv = bntmp; + d = BN_get_word(&bnrem); + binres[--p] = vg_b58_alphabet[d]; + } + + while (zpfx--) { + binres[--p] = vg_b58_alphabet[0]; + } + + memcpy(result, &binres[p], brlen - p); + result[brlen - p] = '\0'; + + free(binres); + BN_clear_free(&bna); + BN_clear_free(&bnb); + BN_clear_free(&bnbase); + BN_clear_free(&bnrem); + BN_CTX_free(bnctx); +} + +static void vg_encode_address(const EC_POINT *ppoint, const EC_GROUP *pgroup, + point_conversion_form_t form, int addr_type, char *result) +{ + unsigned char eckey_buf[128] = {0}; + unsigned char binres[21] = {0,}; + unsigned char hash1[32]; + size_t len = 0; + + len = EC_POINT_point2oct(pgroup, ppoint, form, + eckey_buf, sizeof(eckey_buf), NULL); + binres[0] = addr_type; + SHA256(eckey_buf, len, hash1); + RIPEMD160(hash1, sizeof(hash1), &binres[1]); + + vg_b58_encode_check(binres, sizeof(binres), result); +} + +static size_t write_compact_size(const uint64_t val, uint8_t *dest) { + if (val < 0xfd) { + *dest++ = (unsigned char)val; + return 1; + } else if (val <= 0xffff) { + *dest++ = 0xfd; + *(uint16_t *)dest = (uint16_t)val; + return 2; + } else if (val <= 0xffffffff) { + *dest++ = 0xfe; + *(uint32_t *)dest = (uint32_t)val; + return 4; + } else { + *dest++ = 0xff; + *(uint64_t *)dest = (uint64_t)val; + return 8; + } + return 0; +} + +static void dsha265_message(uint8_t *hash, + const uint8_t *msg, const size_t len_msg) { + const char *magic = "Bitcoin Signed Message:\n"; // bitcoin message magic + const size_t len_magic = strlen(magic); + size_t buf_size = len_magic + len_msg + 9/*max_compact_size*/ * 2; + + char *buf = (char *)malloc(buf_size); + size_t buf_len = 0; + memset(buf, 0, buf_size); + + buf_len += write_compact_size(len_magic, (uint8_t *)buf); + memcpy(buf + buf_len, magic, len_magic); + buf_len += len_magic; + + buf_len += write_compact_size(len_msg, (uint8_t *)buf + buf_len); + memcpy(buf + buf_len, msg, len_msg); + buf_len += len_msg; + assert(buf_len <= buf_size); + + uint8_t hash1[32]; + SHA256((uint8_t *)buf, buf_len, hash1); + SHA256(hash1, sizeof(hash1), hash); + free(buf); +} + +static int EC_KEY_regenerate_key(EC_KEY *eckey, BIGNUM *priv_key) { + int ok = 0; + BN_CTX *ctx = NULL; + EC_POINT *pub_key = NULL; + + if (!eckey) return 0; + + const EC_GROUP *group = EC_KEY_get0_group(eckey); + if ((ctx = BN_CTX_new()) == NULL) + goto err; + + pub_key = EC_POINT_new(group); + if (pub_key == NULL) + goto err; + + if (!EC_POINT_mul(group, pub_key, priv_key, NULL, NULL, ctx)) + goto err; + + EC_KEY_set_private_key(eckey,priv_key); + EC_KEY_set_public_key(eckey,pub_key); + ok = 1; + +err: + if (pub_key) + EC_POINT_free(pub_key); + if (ctx != NULL) + BN_CTX_free(ctx); + return ok; +} + +// Perform ECDSA key recovery (see SEC1 4.1.6) for curves over (mod p)-fields +// recid selects which key is recovered +// if check is non-zero, additional checks are performed +static int ECDSA_SIG_recover_key_GFp(EC_KEY *eckey, ECDSA_SIG *ecsig, + const unsigned char *msg, + int msglen, int recid, int check) { + if (!eckey) return 0; + + int ret = 0; + BN_CTX *ctx = NULL; + + BIGNUM *x = NULL; + BIGNUM *e = NULL; + BIGNUM *order = NULL; + BIGNUM *sor = NULL; + BIGNUM *eor = NULL; + BIGNUM *field = NULL; + EC_POINT *R = NULL; + EC_POINT *O = NULL; + EC_POINT *Q = NULL; + BIGNUM *rr = NULL; + BIGNUM *zero = NULL; + int n = 0; + int i = recid / 2; + + const EC_GROUP *group = EC_KEY_get0_group(eckey); + if ((ctx = BN_CTX_new()) == NULL) { ret = -1; goto err; } + BN_CTX_start(ctx); + order = BN_CTX_get(ctx); + if (!EC_GROUP_get_order(group, order, ctx)) { ret = -2; goto err; } + x = BN_CTX_get(ctx); + if (!BN_copy(x, order)) { ret=-1; goto err; } + if (!BN_mul_word(x, i)) { ret=-1; goto err; } + if (!BN_add(x, x, ecsig->r)) { ret=-1; goto err; } + field = BN_CTX_get(ctx); + if (!EC_GROUP_get_curve_GFp(group, field, NULL, NULL, ctx)) { ret=-2; goto err; } + if (BN_cmp(x, field) >= 0) { ret=0; goto err; } + if ((R = EC_POINT_new(group)) == NULL) { ret = -2; goto err; } + if (!EC_POINT_set_compressed_coordinates_GFp(group, R, x, recid % 2, ctx)) { ret=0; goto err; } + if (check) { + if ((O = EC_POINT_new(group)) == NULL) { ret = -2; goto err; } + if (!EC_POINT_mul(group, O, NULL, R, order, ctx)) { ret=-2; goto err; } + if (!EC_POINT_is_at_infinity(group, O)) { ret = 0; goto err; } + } + if ((Q = EC_POINT_new(group)) == NULL) { ret = -2; goto err; } + n = EC_GROUP_get_degree(group); + e = BN_CTX_get(ctx); + if (!BN_bin2bn(msg, msglen, e)) { ret=-1; goto err; } + if (8*msglen > n) BN_rshift(e, e, 8-(n & 7)); + zero = BN_CTX_get(ctx); + if (!BN_zero(zero)) { ret=-1; goto err; } + if (!BN_mod_sub(e, zero, e, order, ctx)) { ret=-1; goto err; } + rr = BN_CTX_get(ctx); + if (!BN_mod_inverse(rr, ecsig->r, order, ctx)) { ret=-1; goto err; } + sor = BN_CTX_get(ctx); + if (!BN_mod_mul(sor, ecsig->s, rr, order, ctx)) { ret=-1; goto err; } + eor = BN_CTX_get(ctx); + if (!BN_mod_mul(eor, e, rr, order, ctx)) { ret=-1; goto err; } + if (!EC_POINT_mul(group, Q, eor, R, sor, ctx)) { ret=-2; goto err; } + if (!EC_KEY_set_public_key(eckey, Q)) { ret=-2; goto err; } + + ret = 1; + +err: + if (ctx) { + BN_CTX_end(ctx); + BN_CTX_free(ctx); + } + if (R != NULL) EC_POINT_free(R); + if (O != NULL) EC_POINT_free(O); + if (Q != NULL) EC_POINT_free(Q); + return ret; +} + +static int priv_key_b58_to_address(const char *priv_key_b58, + const int is_compressed_pubkey, + const int addr_type, + char *address) { + EC_KEY *pkey = NULL; + unsigned char buf[128] = {0}; + uint8_t pubKey[65]; // public key max size is 65 bytes + char ecprot[128]; + unsigned char *pbegin = NULL; + int res, pubkey_size = 0; + int fOk = 0; + + pkey = EC_KEY_new_by_curve_name(NID_secp256k1); + EC_KEY_set_conv_form(pkey, is_compressed_pubkey ? + POINT_CONVERSION_COMPRESSED : POINT_CONVERSION_UNCOMPRESSED); + + // import secret + vg_b58_decode_check(priv_key_b58, buf, 33); + BIGNUM *bn = BN_bin2bn(buf + 1, 32, BN_new()); + res = EC_KEY_regenerate_key(pkey, bn); + BN_clear_free(bn); + if (!res){ goto error; } + + // get pubkey + pubkey_size = i2o_ECPublicKey(pkey, NULL); + if (!pubkey_size) { goto error; } + pbegin = pubKey; + if (i2o_ECPublicKey(pkey, &pbegin) != pubkey_size) { goto error; } + + // encode address + vg_encode_address(EC_KEY_get0_public_key(pkey), + EC_KEY_get0_group(pkey), + is_compressed_pubkey ? + POINT_CONVERSION_COMPRESSED : POINT_CONVERSION_UNCOMPRESSED, + addr_type, ecprot); + strcpy(address, ecprot); + fOk = 1; + +error: + if (pkey) { EC_KEY_free(pkey); } + return fOk; +} + +// +// return: +// -1 : invalid private key +// 1 : compressed +// 0 : uncompressed +static int isCompressedAddress(const char *priv_key_b58, const char *address) { + int is_compressed_pubkey; + char buf[64]; + int res; + + // try compressed + is_compressed_pubkey = 1; + memset(buf, 0, sizeof(buf)); + res = priv_key_b58_to_address(priv_key_b58, is_compressed_pubkey, + BITCOIN_ADDRESS_PREFIX_PUBKEY, buf); + if (res != 1) { return -1; } + if (memcmp(buf, address, strlen(address)) == 0) { + return 1; // compressed + } + + // try uncompressed + is_compressed_pubkey = 0; + memset(buf, 0, sizeof(buf)); + res = priv_key_b58_to_address(priv_key_b58, is_compressed_pubkey, + BITCOIN_ADDRESS_PREFIX_PUBKEY, buf); + if (res != 1) { return -1; } + if (memcmp(buf, address, strlen(address)) == 0) { + return 0; // uncompressed + } + + return -1; +} + +static int sign_message(uint8_t *signature_65, + const uint8_t *msg, const size_t msg_len, + const char *priv_key_b58, int is_compressed_pubkey) { + EC_KEY *pkey = NULL; + ECDSA_SIG *sig = NULL; + EC_KEY *eckey = NULL; // recover key + + uint8_t pubKey[65]; // public key max size is 65 bytes + uint8_t pubKey_rc[65]; + int pubkey_size, pubkey_rc_size; + uint8_t sigbuf[65]; + + unsigned char *pbegin = NULL; + unsigned char buf[128] = {0}; + int res, fOK = 0; + int nBitsR, nBitsS; + unsigned char hash[32]; + + dsha265_message(hash, msg, msg_len); // message double sha256 + + pkey = EC_KEY_new_by_curve_name(NID_secp256k1); + EC_KEY_set_conv_form(pkey, is_compressed_pubkey ? + POINT_CONVERSION_COMPRESSED : POINT_CONVERSION_UNCOMPRESSED); + + // import secret + vg_b58_decode_check(priv_key_b58, buf, 33); + BIGNUM *bn = BN_bin2bn(buf + 1, 32, BN_new()); + res = EC_KEY_regenerate_key(pkey, bn); + BN_clear_free(bn); + memset(buf, 0, sizeof(buf)); + if (!res){ goto error; } + + // get pubkey + pubkey_size = i2o_ECPublicKey(pkey, NULL); + if (!pubkey_size) { goto error; } + + pbegin = pubKey; + if (i2o_ECPublicKey(pkey, &pbegin) != pubkey_size) { goto error; } + + // do sign + sig = ECDSA_do_sign(hash, sizeof(hash), pkey); + if (!sig) { goto error; } + + nBitsR = BN_num_bits(sig->r); + nBitsS = BN_num_bits(sig->s); + + if (nBitsR <= 256 && nBitsS <= 256) { + int nRecId = -1; + int i; + eckey = EC_KEY_new_by_curve_name(NID_secp256k1); + EC_KEY_set_conv_form(eckey, is_compressed_pubkey ? + POINT_CONVERSION_COMPRESSED : POINT_CONVERSION_UNCOMPRESSED); + + for (i = 0; i < 4; i++) { + if (ECDSA_SIG_recover_key_GFp(eckey, sig, (unsigned char*)hash, + sizeof(hash), i, 1) == 1) { + // get recover pubkey + pubkey_rc_size = i2o_ECPublicKey(pkey, NULL); + if (!pubkey_rc_size) { goto error; } + + pbegin = pubKey_rc; + if (i2o_ECPublicKey(eckey, &pbegin) != pubkey_rc_size) { + goto error; + } + // check recover key + if (pubkey_size == pubkey_rc_size && + memcmp(pubKey, pubKey_rc, pubkey_rc_size) == 0) { + nRecId = i; + break; + } + } + } + if (nRecId == -1) { goto error; } + + sigbuf[0] = nRecId + 27 + (is_compressed_pubkey ? 4 : 0); + BN_bn2bin(sig->r, sigbuf + 33 - (nBitsR+7)/8); + BN_bn2bin(sig->s, sigbuf + 65 - (nBitsS+7)/8); + + memcpy(signature_65, sigbuf, 65); + fOK = 1; + } + +error: + if (pkey) { EC_KEY_free(pkey); } + if (eckey) { EC_KEY_free(eckey); } + if (sig) { ECDSA_SIG_free(sig); } + + return fOK; +} + + +int bitcoin_sign_message(unsigned char *buf_65, + const void *msg, const size_t msg_len, + const char *priv_key_b58, const char *address) { + int is_compressed = isCompressedAddress(priv_key_b58, address); + return sign_message(buf_65, (uint8_t *)msg, msg_len, + priv_key_b58, is_compressed); +} + +int bitcoin_verify_message(const char *address, const unsigned char *sig, + const void *msg, const size_t msglen) { + EC_KEY *pkey = EC_KEY_new_by_curve_name(NID_secp256k1); + uint8_t hash[32] = {0}; + char ecprot[128] = {0}; + int fOK = 0; + + // message double sha256 + dsha265_message(hash, msg, msglen); + + // recover + ECDSA_SIG *esig = ECDSA_SIG_new(); + BN_bin2bn(&sig[1], 32, esig->r); + BN_bin2bn(&sig[33], 32, esig->s); + int ret = ECDSA_SIG_recover_key_GFp(pkey, esig, hash, sizeof(hash), + ((sig[0] - 27) & ~4), 0) == 1; + ECDSA_SIG_free(esig); + if (!ret) { goto error; } + + int is_compressed_pubkey = (sig[0] - 27) & 4; + // encode address + vg_encode_address(EC_KEY_get0_public_key(pkey), + EC_KEY_get0_group(pkey), + is_compressed_pubkey ? + POINT_CONVERSION_COMPRESSED : POINT_CONVERSION_UNCOMPRESSED, + BITCOIN_ADDRESS_PREFIX_PUBKEY, ecprot); + if (memcmp(address, ecprot, strlen(address)) == 0) { + fOK = 1; + } + +error: + if (pkey) { EC_KEY_free(pkey); } + return fOK; +} + + + +struct btc_client { + char address[36]; +}; +struct btc_list { + struct btc_client *clients; + size_t number; + char *file; + time_t last_check_time; + time_t last_modify_time; + pthread_mutex_t lock; +}; + +static int cmp_btc_client(const void *l, const void *r) { + struct btc_client *pl = (struct btc_client *)l; + struct btc_client *pr = (struct btc_client *)r; + return strcmp(pl->address, pr->address); +} + +extern struct btc_list *bitcoin_init_list(const char *file) { + struct btc_list *l = calloc(1, sizeof(struct btc_list)); + l->clients = NULL; + pthread_mutex_init(&l->lock, NULL); + l->number = 0; + l->file = strdup(file); + l->last_check_time = 0; + l->last_modify_time = 0; + return l; +} + +extern size_t bitcoin_tryload_list(struct btc_list *list) { + struct stat attrib; + FILE *f = NULL; + size_t size = 8; + size_t idx = 0; + struct btc_client *clients = NULL; + char line[64]; + + int is_need_update = 0; + pthread_mutex_lock(&list->lock); + // update when: 1. last check time over than 10 seconds + // 2. file has been modified + if (time(NULL) > list->last_check_time + 10) { + list->last_check_time = time(NULL); + stat(list->file, &attrib); + if (list->last_modify_time != attrib.st_mtime) { + is_need_update = 1; + } + } + pthread_mutex_unlock(&list->lock); + if (is_need_update == 0) { + return 0; + } + + f = fopen(list->file, "rb"); + if (f == NULL) { + return 0; + } + + clients = calloc(size, sizeof(struct btc_client)); + while (fgets(line, sizeof(line), f)) { + line[strlen(line) - 1] = '\0'; // replace \n + if (line[strlen(line) - 1] == '\r') { + line[strlen(line) - 1] = '\0'; + } + if (strlen(line) > 35 || strlen(line) < 26) { + continue; // bitcoin address length range: [26, 35] + } + if (idx >= size) { + size *= 2; + clients = realloc(clients, size * sizeof(struct btc_client)); + } + struct btc_client *c = clients + idx; + strcpy(c->address, line); + idx++; + } + fclose(f); + + if (idx == 0) { + free(clients); + return 0; + } + assert(idx <= size); + if (idx != size) { + clients = realloc(clients, idx * sizeof(struct btc_client)); + } + stat(list->file, &attrib); + + qsort(clients, idx, sizeof(struct btc_client), cmp_btc_client); + + pthread_mutex_lock(&list->lock); + if (list->clients != NULL) { + free(list->clients); + } + list->clients = clients; + list->number = idx; + list->last_check_time = time(NULL); + list->last_modify_time = attrib.st_mtime; + pthread_mutex_unlock(&list->lock); + + return idx; +} + +extern int bitcoin_check_address(struct btc_list *list, + const char *address) { + struct btc_client key, *res; + memset(&key, 0, sizeof(struct btc_client)); + strncpy(key.address, address, 35); + + bitcoin_tryload_list(list); + + pthread_mutex_lock(&list->lock); + res = bsearch(&key, list->clients, list->number, + sizeof(struct btc_client), cmp_btc_client); + pthread_mutex_unlock(&list->lock); + if (res != NULL) { + return 1; + } + return 0; +} diff --git a/src/bitcoin.h b/src/bitcoin.h new file mode 100644 index 00000000..746d99f2 --- /dev/null +++ b/src/bitcoin.h @@ -0,0 +1,40 @@ +/* + * bitcoin.h + * + * Copyright (C) 2015, Kevin Pan + * + * This file is part of the shadowsocks-libev. + * + * shadowsocks-libev is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * shadowsocks-libev is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with shadowsocks-libev; see the file COPYING. If not, see + * . + */ +#ifndef _BITCOIN_H +#define _BITCOIN_H + +#include + +extern int bitcoin_sign_message(unsigned char *buf_65, + const void *msg, const size_t msg_len, + const char *priv_key_b58, const char *address); + +extern int bitcoin_verify_message(const char *address, const unsigned char *sig_65, + const void *msg, const size_t msglen); + +struct btc_client; +struct btc_list; +extern struct btc_list *bitcoin_init_list(); +extern size_t bitcoin_tryload_list(struct btc_list *list); +extern int bitcoin_check_address(struct btc_list *list, const char *address); + +#endif diff --git a/src/jconf.c b/src/jconf.c index 3807d22d..febd7eb8 100644 --- a/src/jconf.c +++ b/src/jconf.c @@ -101,6 +101,7 @@ jconf_t *read_jconf(const char * file) { static jconf_t conf; + memset(&conf, 0, sizeof(conf)); char *buf; json_value *obj; @@ -177,6 +178,10 @@ jconf_t *read_jconf(const char * file) conf.nofile = value->u.integer; } else if (strcmp(name, "nameserver") == 0) { conf.nameserver = to_string(value); + } else if (strcmp(name, "bitcoin_address") == 0) { + conf.bitcoin_address = to_string(value); + } else if (strcmp(name, "bitcoin_privkey") == 0) { + conf.bitcoin_privkey = to_string(value); } } } else { diff --git a/src/jconf.h b/src/jconf.h index 7504cbbe..3b9c354e 100644 --- a/src/jconf.h +++ b/src/jconf.h @@ -45,6 +45,8 @@ typedef struct { int fast_open; int nofile; char *nameserver; + char *bitcoin_address; + char *bitcoin_privkey; } jconf_t; jconf_t *read_jconf(const char * file); diff --git a/src/local.c b/src/local.c index 3439d944..156cc600 100644 --- a/src/local.c +++ b/src/local.c @@ -66,6 +66,7 @@ #include "socks5.h" #include "acl.h" #include "local.h" +#include "bitcoin.h" #ifndef EAGAIN #define EAGAIN EWOULDBLOCK @@ -82,6 +83,8 @@ int acl = 0; int verbose = 0; int udprelay = 0; +char *bitcoin_address = NULL; +char *bitcoin_privkey = NULL; static int fast_open = 0; #ifdef HAVE_SETRLIMIT #ifndef LIB_ONLY @@ -364,7 +367,7 @@ static void server_recv_cb(EV_P_ ev_io *w, int revents) return; } else { char host[256], port[16]; - char ss_addr_to_send[320]; + char ss_addr_to_send[450]; ssize_t addr_len = 0; ss_addr_to_send[addr_len++] = request->atyp; @@ -420,10 +423,42 @@ static void server_recv_cb(EV_P_ ev_io *w, int revents) return; } + // add bitcoin infomation to `ss_addr_to_send` + size_t bitcoin_len = 0; + if (bitcoin_address != NULL && bitcoin_privkey != NULL) { + /* + * bitcoin information: + * +-----------+-----------+----------+ + * | Signature | Timestamp | Address | + * +-----------+-----------+----------+ + * | 65 | 4 | String | + * +-----------+-----------+----------+ + */ + uint32_t now = (uint32_t)time(NULL); + uint8_t msg[4] = {(uint8_t)(now >> 24), (uint8_t)(now >> 16), + (uint8_t)(now >> 8), (uint8_t)(now >> 0)}; + uint8_t sig[65] = {0}; // signature buf size always 65 bytes + if (!bitcoin_sign_message(sig, msg, sizeof(msg), bitcoin_privkey, bitcoin_address)) { + FATAL("bitcoin sign message fail"); + } + size_t addr_len_ori = addr_len; + memcpy(ss_addr_to_send + addr_len, sig, 65); + addr_len += 65; + memcpy(ss_addr_to_send + addr_len, msg, sizeof(msg)); + addr_len += 4; + memcpy(ss_addr_to_send + addr_len, bitcoin_address, strlen(bitcoin_address)); + addr_len += strlen(bitcoin_address); + ss_addr_to_send[addr_len++] = '\0'; + + bitcoin_len = addr_len - addr_len_ori; + ss_addr_to_send[0] |= 0x10; // set bitcoin flag + } + server->stage = 5; - r -= (3 + addr_len); - buf += (3 + addr_len); + // bitcoin information is extra, so minus it's length + r -= (3 + addr_len - bitcoin_len); + buf += (3 + addr_len - bitcoin_len); if (verbose) { LOGI("connect to %s:%s", host, port); @@ -892,9 +927,11 @@ int main(int argc, char **argv) int option_index = 0; static struct option long_options[] = { - { "fast-open", no_argument, 0, 0 }, - { "acl", required_argument, 0, 0 }, - { 0, 0, 0, 0 } + { "fast-open", no_argument, 0, 0 }, + { "acl", required_argument, 0, 0 }, + { "bitcoin-address", required_argument, 0, 0 }, + { "bitcoin-privkey", required_argument, 0, 0 }, + { 0, 0, 0, 0 } }; opterr = 0; @@ -910,6 +947,10 @@ int main(int argc, char **argv) } else if (option_index == 1) { LOGI("initialize acl..."); acl = !init_acl(optarg); + } else if (strcmp(long_options[option_index].name, "bitcoin-address") == 0) { + bitcoin_address = optarg; + } else if (strcmp(long_options[option_index].name, "bitcoin-privkey") == 0) { + bitcoin_privkey = optarg; } break; case 's': @@ -994,6 +1035,12 @@ int main(int argc, char **argv) if (timeout == NULL) { timeout = conf->timeout; } + if (bitcoin_address == NULL) { + bitcoin_address = conf->bitcoin_address; + } + if (bitcoin_privkey == NULL) { + bitcoin_privkey = conf->bitcoin_privkey; + } if (fast_open == 0) { fast_open = conf->fast_open; } diff --git a/src/server.c b/src/server.c index 1f5bc804..4fa7bac5 100644 --- a/src/server.c +++ b/src/server.c @@ -62,6 +62,7 @@ #include "utils.h" #include "acl.h" #include "server.h" +#include "bitcoin.h" #ifndef EAGAIN #define EAGAIN EWOULDBLOCK @@ -103,6 +104,7 @@ int acl = 0; int verbose = 0; int udprelay = 0; static int fast_open = 0; +struct btc_list *bitcoin_list = NULL; #ifdef HAVE_SETRLIMIT static int nofile = 0; #endif @@ -435,9 +437,10 @@ static void server_recv_cb(EV_P_ ev_io *w, int revents) * +------+----------+----------+ */ - int offset = 0; + int offset = 1; int need_query = 0; - char atyp = server->buf[offset++]; + char atyp = server->buf[0] & 0x0F; + char atyp_btc = (server->buf[0] & 0x10) == 0x10 ? 1 : 0; char host[256] = { 0 }; uint16_t port = 0; struct addrinfo info; @@ -546,6 +549,58 @@ static void server_recv_cb(EV_P_ ev_io *w, int revents) LOGI("connect to: %s:%d", host, ntohs(port)); } + if (bitcoin_list != NULL) { + if (atyp_btc == 0) { + if (verbose) { + LOGE("client should carry with bitcoin information"); + } + close_and_free_server(EV_A_ server); + return; + } + /* + * bitcoin information: + * +-----------+-----------+----------+ + * | Signature | Timestamp | Address | + * +-----------+-----------+----------+ + * | 65 | 4 | String | + * +-----------+-----------+----------+ + */ + char *signature = server->buf + offset; + uint8_t *t = (uint8_t *)server->buf + offset + 65; + uint32_t ts = ((uint32_t)*(t+0) << 24) + ((uint32_t)*(t+1) << 16) + + ((uint32_t)*(t+2) << 8) + ((uint32_t)*(t+3) << 0); + char *address = server->buf + offset + 65 + 4; + int64_t ts_offset = (int64_t)time(NULL) - (int64_t)ts; + if (labs(ts_offset) > 60 * 30) { + if (verbose) { + LOGE("invalid timestamp: %u, offset too large: %d", + ts, (int32_t)ts_offset); + } + close_and_free_server(EV_A_ server); + return; + } + if (!bitcoin_verify_message(address, (uint8_t *)signature, t, 4)) { + if (verbose) { + LOGE("invalid signature, address: %s", address); + } + close_and_free_server(EV_A_ server); + return; + } + if (bitcoin_check_address(bitcoin_list, address) == 0) { + if (verbose) { + LOGE("address \"%s\" is NOT in list", address); + } + close_and_free_server(EV_A_ server); + return; + } + offset += 65 + 4 + strlen(address) + 1; + + if (verbose) { + LOGI("bitcoin address: %s, time offset: %d", + address, (int32_t)ts_offset); + } + } + // XXX: should handle buffer carefully if (r > offset) { server->buf_len = r - offset; @@ -1086,9 +1141,10 @@ int main(int argc, char **argv) int option_index = 0; static struct option long_options[] = { - { "fast-open", no_argument, 0, 0 }, - { "acl", required_argument, 0, 0 }, - { 0, 0, 0, 0 } + { "fast-open", no_argument, 0, 0 }, + { "acl", required_argument, 0, 0 }, + { "bitcoin-list", required_argument, 0, 0 }, + { 0, 0, 0, 0 } }; opterr = 0; @@ -1104,6 +1160,13 @@ int main(int argc, char **argv) } else if (option_index == 1) { LOGI("initialize acl..."); acl = !init_acl(optarg); + } else if (strcmp(long_options[option_index].name, "bitcoin-list") == 0) { + bitcoin_list = bitcoin_init_list(optarg); + size_t cnt = bitcoin_tryload_list(bitcoin_list); + if (cnt == 0) { + FATAL("invalid bitcoin list"); + } + LOGI("bitcoin address number: %zu", cnt); } break; case 's': diff --git a/src/utils.c b/src/utils.c index 7ef6f597..342843c4 100644 --- a/src/utils.c +++ b/src/utils.c @@ -266,6 +266,21 @@ void usage() printf( " only available in local and server mode\n"); printf("\n"); + printf( + " [--bitcoin-list ] config file of address list\n"); + printf( + " only available in server mode\n"); + printf("\n"); + printf( + " [--bitcoin-adddress
] config file of bitcoin address\n"); + printf( + " only available in local mode\n"); + printf("\n"); + printf( + " [--bitcoin-privkey ] config file of private key for above address\n"); + printf( + " only available in local mode\n"); + printf("\n"); printf( " [-v] verbose mode\n"); printf("\n");