libgo: Update to weekly.2011-12-02.

From-SVN: r182295
This commit is contained in:
Ian Lance Taylor 2011-12-13 19:16:27 +00:00
parent 36cfbee133
commit 7b1c3dd9e6
234 changed files with 6653 additions and 4704 deletions

View File

@ -20047,11 +20047,10 @@ var gettysburg = " Four score and seven years ago our fathers brought forth on\
"\n" +
"Abraham Lincoln, November 19, 1863, Gettysburg, Pennsylvania\n"
func main() {
m := md5.New()
io.WriteString(m, data)
hash := fmt.Sprintf("%x", m.Sum())
hash := fmt.Sprintf("%x", m.Sum(nil))
if hash != "525f06bc62a65017cd2217d7584e5920" {
println("BUG a", hash)
return
@ -20059,7 +20058,7 @@ func main() {
m = md5.New()
io.WriteString(m, gettysburg)
hash = fmt.Sprintf("%x", m.Sum())
hash = fmt.Sprintf("%x", m.Sum(nil))
if hash != "d7ec5d9d47a4d166091e8d9ebd7ea0aa" {
println("BUG gettysburg", hash)
println(len(gettysburg))

View File

@ -19,9 +19,8 @@ func f() {
func init() {
go f()
time.Nanoseconds()
time.Now()
}
func main() {
}

View File

@ -1,4 +1,4 @@
b4a91b693374
0beb796b4ef8
The first line of this file holds the Mercurial revision number of the
last merge done from the master library sources.

View File

@ -233,7 +233,6 @@ toolexeclibgoexpdir = $(toolexeclibgodir)/exp
toolexeclibgoexp_DATA = \
exp/ebnf.gox \
exp/gui.gox \
$(exp_inotify_gox) \
exp/norm.gox \
exp/spdy.gox \
@ -242,11 +241,6 @@ toolexeclibgoexp_DATA = \
exp/terminal.gox \
exp/types.gox
toolexeclibgoexpguidir = $(toolexeclibgoexpdir)/gui
toolexeclibgoexpgui_DATA = \
exp/gui/x11.gox
toolexeclibgoexpsqldir = $(toolexeclibgoexpdir)/sql
toolexeclibgoexpsql_DATA = \
@ -447,6 +441,7 @@ runtime_files = \
runtime/go-map-len.c \
runtime/go-map-range.c \
runtime/go-nanotime.c \
runtime/go-now.c \
runtime/go-new-map.c \
runtime/go-new.c \
runtime/go-panic.c \
@ -576,6 +571,7 @@ go_hash_files = \
go_html_files = \
go/html/const.go \
go/html/doc.go \
go/html/doctype.go \
go/html/entity.go \
go/html/escape.go \
go/html/node.go \
@ -888,7 +884,7 @@ go_time_files = \
go/time/sys_unix.go \
go/time/tick.go \
go/time/time.go \
go/time/zoneinfo_posix.go \
go/time/zoneinfo.go \
go/time/zoneinfo_unix.go
go_unicode_files = \
@ -1038,6 +1034,7 @@ go_crypto_twofish_files = \
go_crypto_x509_files = \
go/crypto/x509/cert_pool.go \
go/crypto/x509/pkcs1.go \
go/crypto/x509/pkcs8.go \
go/crypto/x509/verify.go \
go/crypto/x509/x509.go
go_crypto_xtea_files = \
@ -1135,8 +1132,6 @@ go_encoding_xml_files = \
go_exp_ebnf_files = \
go/exp/ebnf/ebnf.go \
go/exp/ebnf/parser.go
go_exp_gui_files = \
go/exp/gui/gui.go
go_exp_inotify_files = \
go/exp/inotify/inotify_linux.go
go_exp_norm_files = \
@ -1178,10 +1173,6 @@ go_exp_types_files = \
go/exp/types/types.go \
go/exp/types/universe.go
go_exp_gui_x11_files = \
go/exp/gui/x11/auth.go \
go/exp/gui/x11/conn.go
go_exp_sql_driver_files = \
go/exp/sql/driver/driver.go \
go/exp/sql/driver/types.go
@ -1415,13 +1406,11 @@ go_text_template_files = \
go/text/template/exec.go \
go/text/template/funcs.go \
go/text/template/helper.go \
go/text/template/parse.go \
go/text/template/set.go
go/text/template/template.go
go_text_template_parse_files = \
go/text/template/parse/lex.go \
go/text/template/parse/node.go \
go/text/template/parse/parse.go \
go/text/template/parse/set.go
go/text/template/parse/parse.go
go_sync_atomic_files = \
go/sync/atomic/doc.go
@ -1725,14 +1714,12 @@ libgo_go_objs = \
encoding/pem.lo \
encoding/xml.lo \
exp/ebnf.lo \
exp/gui.lo \
exp/norm.lo \
exp/spdy.lo \
exp/sql.lo \
exp/ssh.lo \
exp/terminal.lo \
exp/types.lo \
exp/gui/x11.lo \
exp/sql/driver.lo \
html/template.lo \
go/ast.lo \
@ -2784,16 +2771,6 @@ exp/ebnf/check: $(CHECK_DEPS)
@$(CHECK)
.PHONY: exp/ebnf/check
@go_include@ exp/gui.lo.dep
exp/gui.lo.dep: $(go_exp_gui_files)
$(BUILDDEPS)
exp/gui.lo: $(go_exp_gui_files)
$(BUILDPACKAGE)
exp/gui/check: $(CHECK_DEPS)
@$(MKDIR_P) exp/gui
@$(CHECK)
.PHONY: exp/gui/check
@go_include@ exp/norm.lo.dep
exp/norm.lo.dep: $(go_exp_norm_files)
$(BUILDDEPS)
@ -2854,16 +2831,6 @@ exp/types/check: $(CHECK_DEPS)
@$(CHECK)
.PHONY: exp/types/check
@go_include@ exp/gui/x11.lo.dep
exp/gui/x11.lo.dep: $(go_exp_gui_x11_files)
$(BUILDDEPS)
exp/gui/x11.lo: $(go_exp_gui_x11_files)
$(BUILDPACKAGE)
exp/gui/x11/check: $(CHECK_DEPS)
@$(MKDIR_P) exp/gui/x11
@$(CHECK)
.PHONY: exp/gui/x11/check
@go_include@ exp/inotify.lo.dep
exp/inotify.lo.dep: $(go_exp_inotify_files)
$(BUILDDEPS)
@ -3686,8 +3653,6 @@ encoding/xml.gox: encoding/xml.lo
exp/ebnf.gox: exp/ebnf.lo
$(BUILDGOX)
exp/gui.gox: exp/gui.lo
$(BUILDGOX)
exp/inotify.gox: exp/inotify.lo
$(BUILDGOX)
exp/norm.gox: exp/norm.lo
@ -3703,9 +3668,6 @@ exp/terminal.gox: exp/terminal.lo
exp/types.gox: exp/types.lo
$(BUILDGOX)
exp/gui/x11.gox: exp/gui/x11.lo
$(BUILDGOX)
exp/sql/driver.gox: exp/sql/driver.lo
$(BUILDGOX)
@ -3950,6 +3912,7 @@ TEST_PACKAGES = \
html/template/check \
go/ast/check \
$(go_build_check_omitted_since_it_calls_6g) \
go/doc/check \
go/parser/check \
go/printer/check \
go/scanner/check \

View File

@ -102,7 +102,6 @@ am__installdirs = "$(DESTDIR)$(toolexeclibdir)" \
"$(DESTDIR)$(toolexeclibgodebugdir)" \
"$(DESTDIR)$(toolexeclibgoencodingdir)" \
"$(DESTDIR)$(toolexeclibgoexpdir)" \
"$(DESTDIR)$(toolexeclibgoexpguidir)" \
"$(DESTDIR)$(toolexeclibgoexpsqldir)" \
"$(DESTDIR)$(toolexeclibgogodir)" \
"$(DESTDIR)$(toolexeclibgohashdir)" \
@ -161,15 +160,15 @@ am__DEPENDENCIES_2 = bufio/bufio.lo bytes/bytes.lo bytes/index.lo \
encoding/base64.lo encoding/binary.lo encoding/csv.lo \
encoding/git85.lo encoding/gob.lo encoding/hex.lo \
encoding/json.lo encoding/pem.lo encoding/xml.lo exp/ebnf.lo \
exp/gui.lo exp/norm.lo exp/spdy.lo exp/sql.lo exp/ssh.lo \
exp/terminal.lo exp/types.lo exp/gui/x11.lo exp/sql/driver.lo \
html/template.lo go/ast.lo go/build.lo go/doc.lo go/parser.lo \
go/printer.lo go/scanner.lo go/token.lo hash/adler32.lo \
hash/crc32.lo hash/crc64.lo hash/fnv.lo net/http/cgi.lo \
net/http/fcgi.lo net/http/httptest.lo net/http/httputil.lo \
net/http/pprof.lo image/bmp.lo image/color.lo image/draw.lo \
image/gif.lo image/jpeg.lo image/png.lo image/tiff.lo \
image/ycbcr.lo index/suffixarray.lo io/ioutil.lo log/syslog.lo \
exp/norm.lo exp/spdy.lo exp/sql.lo exp/ssh.lo exp/terminal.lo \
exp/types.lo exp/sql/driver.lo html/template.lo go/ast.lo \
go/build.lo go/doc.lo go/parser.lo go/printer.lo go/scanner.lo \
go/token.lo hash/adler32.lo hash/crc32.lo hash/crc64.lo \
hash/fnv.lo net/http/cgi.lo net/http/fcgi.lo \
net/http/httptest.lo net/http/httputil.lo net/http/pprof.lo \
image/bmp.lo image/color.lo image/draw.lo image/gif.lo \
image/jpeg.lo image/png.lo image/tiff.lo image/ycbcr.lo \
index/suffixarray.lo io/ioutil.lo log/syslog.lo \
log/syslog/syslog_c.lo math/big.lo math/cmplx.lo math/rand.lo \
mime/mime.lo mime/multipart.lo net/dict.lo net/http.lo \
net/mail.lo net/rpc.lo net/smtp.lo net/textproto.lo net/url.lo \
@ -200,12 +199,12 @@ am__libgo_la_SOURCES_DIST = runtime/go-append.c runtime/go-assert.c \
runtime/go-interface-val-compare.c runtime/go-make-slice.c \
runtime/go-map-delete.c runtime/go-map-index.c \
runtime/go-map-len.c runtime/go-map-range.c \
runtime/go-nanotime.c runtime/go-new-map.c runtime/go-new.c \
runtime/go-panic.c runtime/go-print.c runtime/go-recover.c \
runtime/go-reflect.c runtime/go-reflect-call.c \
runtime/go-reflect-map.c runtime/go-rune.c \
runtime/go-runtime-error.c runtime/go-setenv.c \
runtime/go-signal.c runtime/go-strcmp.c \
runtime/go-nanotime.c runtime/go-now.c runtime/go-new-map.c \
runtime/go-new.c runtime/go-panic.c runtime/go-print.c \
runtime/go-recover.c runtime/go-reflect.c \
runtime/go-reflect-call.c runtime/go-reflect-map.c \
runtime/go-rune.c runtime/go-runtime-error.c \
runtime/go-setenv.c runtime/go-signal.c runtime/go-strcmp.c \
runtime/go-string-to-byte-array.c \
runtime/go-string-to-int-array.c runtime/go-strplus.c \
runtime/go-strslice.c runtime/go-trampoline.c \
@ -238,20 +237,20 @@ am__objects_4 = go-append.lo go-assert.lo go-assert-interface.lo \
go-interface-compare.lo go-interface-eface-compare.lo \
go-interface-val-compare.lo go-make-slice.lo go-map-delete.lo \
go-map-index.lo go-map-len.lo go-map-range.lo go-nanotime.lo \
go-new-map.lo go-new.lo go-panic.lo go-print.lo go-recover.lo \
go-reflect.lo go-reflect-call.lo go-reflect-map.lo go-rune.lo \
go-runtime-error.lo go-setenv.lo go-signal.lo go-strcmp.lo \
go-string-to-byte-array.lo go-string-to-int-array.lo \
go-strplus.lo go-strslice.lo go-trampoline.lo go-type-eface.lo \
go-type-error.lo go-type-identity.lo go-type-interface.lo \
go-type-string.lo go-typedesc-equal.lo go-typestring.lo \
go-unreflect.lo go-unsafe-new.lo go-unsafe-newarray.lo \
go-unsafe-pointer.lo go-unwind.lo chan.lo cpuprof.lo \
$(am__objects_1) mcache.lo mcentral.lo $(am__objects_2) \
mfinal.lo mfixalloc.lo mgc0.lo mheap.lo msize.lo proc.lo \
runtime.lo thread.lo yield.lo $(am__objects_3) iface.lo \
malloc.lo map.lo mprof.lo reflect.lo runtime1.lo sema.lo \
sigqueue.lo string.lo time.lo
go-now.lo go-new-map.lo go-new.lo go-panic.lo go-print.lo \
go-recover.lo go-reflect.lo go-reflect-call.lo \
go-reflect-map.lo go-rune.lo go-runtime-error.lo go-setenv.lo \
go-signal.lo go-strcmp.lo go-string-to-byte-array.lo \
go-string-to-int-array.lo go-strplus.lo go-strslice.lo \
go-trampoline.lo go-type-eface.lo go-type-error.lo \
go-type-identity.lo go-type-interface.lo go-type-string.lo \
go-typedesc-equal.lo go-typestring.lo go-unreflect.lo \
go-unsafe-new.lo go-unsafe-newarray.lo go-unsafe-pointer.lo \
go-unwind.lo chan.lo cpuprof.lo $(am__objects_1) mcache.lo \
mcentral.lo $(am__objects_2) mfinal.lo mfixalloc.lo mgc0.lo \
mheap.lo msize.lo proc.lo runtime.lo thread.lo yield.lo \
$(am__objects_3) iface.lo malloc.lo map.lo mprof.lo reflect.lo \
runtime1.lo sema.lo sigqueue.lo string.lo time.lo
am_libgo_la_OBJECTS = $(am__objects_4)
libgo_la_OBJECTS = $(am_libgo_la_OBJECTS)
libgo_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
@ -290,18 +289,18 @@ DATA = $(toolexeclibgo_DATA) $(toolexeclibgoarchive_DATA) \
$(toolexeclibgocrypto_DATA) $(toolexeclibgocryptoopenpgp_DATA) \
$(toolexeclibgocryptox509_DATA) $(toolexeclibgodebug_DATA) \
$(toolexeclibgoencoding_DATA) $(toolexeclibgoexp_DATA) \
$(toolexeclibgoexpgui_DATA) $(toolexeclibgoexpsql_DATA) \
$(toolexeclibgogo_DATA) $(toolexeclibgohash_DATA) \
$(toolexeclibgohtml_DATA) $(toolexeclibgoimage_DATA) \
$(toolexeclibgoindex_DATA) $(toolexeclibgoio_DATA) \
$(toolexeclibgolog_DATA) $(toolexeclibgomath_DATA) \
$(toolexeclibgomime_DATA) $(toolexeclibgonet_DATA) \
$(toolexeclibgonethttp_DATA) $(toolexeclibgonetrpc_DATA) \
$(toolexeclibgoold_DATA) $(toolexeclibgoos_DATA) \
$(toolexeclibgopath_DATA) $(toolexeclibgoregexp_DATA) \
$(toolexeclibgoruntime_DATA) $(toolexeclibgosync_DATA) \
$(toolexeclibgotesting_DATA) $(toolexeclibgotext_DATA) \
$(toolexeclibgotexttemplate_DATA) $(toolexeclibgounicode_DATA)
$(toolexeclibgoexpsql_DATA) $(toolexeclibgogo_DATA) \
$(toolexeclibgohash_DATA) $(toolexeclibgohtml_DATA) \
$(toolexeclibgoimage_DATA) $(toolexeclibgoindex_DATA) \
$(toolexeclibgoio_DATA) $(toolexeclibgolog_DATA) \
$(toolexeclibgomath_DATA) $(toolexeclibgomime_DATA) \
$(toolexeclibgonet_DATA) $(toolexeclibgonethttp_DATA) \
$(toolexeclibgonetrpc_DATA) $(toolexeclibgoold_DATA) \
$(toolexeclibgoos_DATA) $(toolexeclibgopath_DATA) \
$(toolexeclibgoregexp_DATA) $(toolexeclibgoruntime_DATA) \
$(toolexeclibgosync_DATA) $(toolexeclibgotesting_DATA) \
$(toolexeclibgotext_DATA) $(toolexeclibgotexttemplate_DATA) \
$(toolexeclibgounicode_DATA)
RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \
distclean-recursive maintainer-clean-recursive
AM_RECURSIVE_TARGETS = $(RECURSIVE_TARGETS:-recursive=) \
@ -690,7 +689,6 @@ toolexeclibgoencoding_DATA = \
toolexeclibgoexpdir = $(toolexeclibgodir)/exp
toolexeclibgoexp_DATA = \
exp/ebnf.gox \
exp/gui.gox \
$(exp_inotify_gox) \
exp/norm.gox \
exp/spdy.gox \
@ -699,10 +697,6 @@ toolexeclibgoexp_DATA = \
exp/terminal.gox \
exp/types.gox
toolexeclibgoexpguidir = $(toolexeclibgoexpdir)/gui
toolexeclibgoexpgui_DATA = \
exp/gui/x11.gox
toolexeclibgoexpsqldir = $(toolexeclibgoexpdir)/sql
toolexeclibgoexpsql_DATA = \
exp/sql/driver.gox
@ -868,6 +862,7 @@ runtime_files = \
runtime/go-map-len.c \
runtime/go-map-range.c \
runtime/go-nanotime.c \
runtime/go-now.c \
runtime/go-new-map.c \
runtime/go-new.c \
runtime/go-panic.c \
@ -960,6 +955,7 @@ go_hash_files = \
go_html_files = \
go/html/const.go \
go/html/doc.go \
go/html/doctype.go \
go/html/entity.go \
go/html/escape.go \
go/html/node.go \
@ -1204,7 +1200,7 @@ go_time_files = \
go/time/sys_unix.go \
go/time/tick.go \
go/time/time.go \
go/time/zoneinfo_posix.go \
go/time/zoneinfo.go \
go/time/zoneinfo_unix.go
go_unicode_files = \
@ -1377,6 +1373,7 @@ go_crypto_twofish_files = \
go_crypto_x509_files = \
go/crypto/x509/cert_pool.go \
go/crypto/x509/pkcs1.go \
go/crypto/x509/pkcs8.go \
go/crypto/x509/verify.go \
go/crypto/x509/x509.go
@ -1495,9 +1492,6 @@ go_exp_ebnf_files = \
go/exp/ebnf/ebnf.go \
go/exp/ebnf/parser.go
go_exp_gui_files = \
go/exp/gui/gui.go
go_exp_inotify_files = \
go/exp/inotify/inotify_linux.go
@ -1545,10 +1539,6 @@ go_exp_types_files = \
go/exp/types/types.go \
go/exp/types/universe.go
go_exp_gui_x11_files = \
go/exp/gui/x11/auth.go \
go/exp/gui/x11/conn.go
go_exp_sql_driver_files = \
go/exp/sql/driver/driver.go \
go/exp/sql/driver/types.go
@ -1805,14 +1795,12 @@ go_text_template_files = \
go/text/template/exec.go \
go/text/template/funcs.go \
go/text/template/helper.go \
go/text/template/parse.go \
go/text/template/set.go
go/text/template/template.go
go_text_template_parse_files = \
go/text/template/parse/lex.go \
go/text/template/parse/node.go \
go/text/template/parse/parse.go \
go/text/template/parse/set.go
go/text/template/parse/parse.go
go_sync_atomic_files = \
go/sync/atomic/doc.go
@ -2016,14 +2004,12 @@ libgo_go_objs = \
encoding/pem.lo \
encoding/xml.lo \
exp/ebnf.lo \
exp/gui.lo \
exp/norm.lo \
exp/spdy.lo \
exp/sql.lo \
exp/ssh.lo \
exp/terminal.lo \
exp/types.lo \
exp/gui/x11.lo \
exp/sql/driver.lo \
html/template.lo \
go/ast.lo \
@ -2295,6 +2281,7 @@ TEST_PACKAGES = \
html/template/check \
go/ast/check \
$(go_build_check_omitted_since_it_calls_6g) \
go/doc/check \
go/parser/check \
go/printer/check \
go/scanner/check \
@ -2511,6 +2498,7 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-nanotime.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-new-map.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-new.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-now.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-panic.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-print.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-recover.Plo@am__quote@
@ -2799,6 +2787,13 @@ go-nanotime.lo: runtime/go-nanotime.c
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o go-nanotime.lo `test -f 'runtime/go-nanotime.c' || echo '$(srcdir)/'`runtime/go-nanotime.c
go-now.lo: runtime/go-now.c
@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT go-now.lo -MD -MP -MF $(DEPDIR)/go-now.Tpo -c -o go-now.lo `test -f 'runtime/go-now.c' || echo '$(srcdir)/'`runtime/go-now.c
@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/go-now.Tpo $(DEPDIR)/go-now.Plo
@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='runtime/go-now.c' object='go-now.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o go-now.lo `test -f 'runtime/go-now.c' || echo '$(srcdir)/'`runtime/go-now.c
go-new-map.lo: runtime/go-new-map.c
@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT go-new-map.lo -MD -MP -MF $(DEPDIR)/go-new-map.Tpo -c -o go-new-map.lo `test -f 'runtime/go-new-map.c' || echo '$(srcdir)/'`runtime/go-new-map.c
@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/go-new-map.Tpo $(DEPDIR)/go-new-map.Plo
@ -3374,26 +3369,6 @@ uninstall-toolexeclibgoexpDATA:
test -n "$$files" || exit 0; \
echo " ( cd '$(DESTDIR)$(toolexeclibgoexpdir)' && rm -f" $$files ")"; \
cd "$(DESTDIR)$(toolexeclibgoexpdir)" && rm -f $$files
install-toolexeclibgoexpguiDATA: $(toolexeclibgoexpgui_DATA)
@$(NORMAL_INSTALL)
test -z "$(toolexeclibgoexpguidir)" || $(MKDIR_P) "$(DESTDIR)$(toolexeclibgoexpguidir)"
@list='$(toolexeclibgoexpgui_DATA)'; test -n "$(toolexeclibgoexpguidir)" || list=; \
for p in $$list; do \
if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
echo "$$d$$p"; \
done | $(am__base_list) | \
while read files; do \
echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(toolexeclibgoexpguidir)'"; \
$(INSTALL_DATA) $$files "$(DESTDIR)$(toolexeclibgoexpguidir)" || exit $$?; \
done
uninstall-toolexeclibgoexpguiDATA:
@$(NORMAL_UNINSTALL)
@list='$(toolexeclibgoexpgui_DATA)'; test -n "$(toolexeclibgoexpguidir)" || list=; \
files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
test -n "$$files" || exit 0; \
echo " ( cd '$(DESTDIR)$(toolexeclibgoexpguidir)' && rm -f" $$files ")"; \
cd "$(DESTDIR)$(toolexeclibgoexpguidir)" && rm -f $$files
install-toolexeclibgoexpsqlDATA: $(toolexeclibgoexpsql_DATA)
@$(NORMAL_INSTALL)
test -z "$(toolexeclibgoexpsqldir)" || $(MKDIR_P) "$(DESTDIR)$(toolexeclibgoexpsqldir)"
@ -4171,7 +4146,7 @@ all-am: Makefile $(LIBRARIES) $(LTLIBRARIES) all-multi $(DATA) \
config.h
installdirs: installdirs-recursive
installdirs-am:
for dir in "$(DESTDIR)$(toolexeclibdir)" "$(DESTDIR)$(toolexeclibdir)" "$(DESTDIR)$(toolexeclibgodir)" "$(DESTDIR)$(toolexeclibgoarchivedir)" "$(DESTDIR)$(toolexeclibgocompressdir)" "$(DESTDIR)$(toolexeclibgocontainerdir)" "$(DESTDIR)$(toolexeclibgocryptodir)" "$(DESTDIR)$(toolexeclibgocryptoopenpgpdir)" "$(DESTDIR)$(toolexeclibgocryptox509dir)" "$(DESTDIR)$(toolexeclibgodebugdir)" "$(DESTDIR)$(toolexeclibgoencodingdir)" "$(DESTDIR)$(toolexeclibgoexpdir)" "$(DESTDIR)$(toolexeclibgoexpguidir)" "$(DESTDIR)$(toolexeclibgoexpsqldir)" "$(DESTDIR)$(toolexeclibgogodir)" "$(DESTDIR)$(toolexeclibgohashdir)" "$(DESTDIR)$(toolexeclibgohtmldir)" "$(DESTDIR)$(toolexeclibgoimagedir)" "$(DESTDIR)$(toolexeclibgoindexdir)" "$(DESTDIR)$(toolexeclibgoiodir)" "$(DESTDIR)$(toolexeclibgologdir)" "$(DESTDIR)$(toolexeclibgomathdir)" "$(DESTDIR)$(toolexeclibgomimedir)" "$(DESTDIR)$(toolexeclibgonetdir)" "$(DESTDIR)$(toolexeclibgonethttpdir)" "$(DESTDIR)$(toolexeclibgonetrpcdir)" "$(DESTDIR)$(toolexeclibgoolddir)" "$(DESTDIR)$(toolexeclibgoosdir)" "$(DESTDIR)$(toolexeclibgopathdir)" "$(DESTDIR)$(toolexeclibgoregexpdir)" "$(DESTDIR)$(toolexeclibgoruntimedir)" "$(DESTDIR)$(toolexeclibgosyncdir)" "$(DESTDIR)$(toolexeclibgotestingdir)" "$(DESTDIR)$(toolexeclibgotextdir)" "$(DESTDIR)$(toolexeclibgotexttemplatedir)" "$(DESTDIR)$(toolexeclibgounicodedir)"; do \
for dir in "$(DESTDIR)$(toolexeclibdir)" "$(DESTDIR)$(toolexeclibdir)" "$(DESTDIR)$(toolexeclibgodir)" "$(DESTDIR)$(toolexeclibgoarchivedir)" "$(DESTDIR)$(toolexeclibgocompressdir)" "$(DESTDIR)$(toolexeclibgocontainerdir)" "$(DESTDIR)$(toolexeclibgocryptodir)" "$(DESTDIR)$(toolexeclibgocryptoopenpgpdir)" "$(DESTDIR)$(toolexeclibgocryptox509dir)" "$(DESTDIR)$(toolexeclibgodebugdir)" "$(DESTDIR)$(toolexeclibgoencodingdir)" "$(DESTDIR)$(toolexeclibgoexpdir)" "$(DESTDIR)$(toolexeclibgoexpsqldir)" "$(DESTDIR)$(toolexeclibgogodir)" "$(DESTDIR)$(toolexeclibgohashdir)" "$(DESTDIR)$(toolexeclibgohtmldir)" "$(DESTDIR)$(toolexeclibgoimagedir)" "$(DESTDIR)$(toolexeclibgoindexdir)" "$(DESTDIR)$(toolexeclibgoiodir)" "$(DESTDIR)$(toolexeclibgologdir)" "$(DESTDIR)$(toolexeclibgomathdir)" "$(DESTDIR)$(toolexeclibgomimedir)" "$(DESTDIR)$(toolexeclibgonetdir)" "$(DESTDIR)$(toolexeclibgonethttpdir)" "$(DESTDIR)$(toolexeclibgonetrpcdir)" "$(DESTDIR)$(toolexeclibgoolddir)" "$(DESTDIR)$(toolexeclibgoosdir)" "$(DESTDIR)$(toolexeclibgopathdir)" "$(DESTDIR)$(toolexeclibgoregexpdir)" "$(DESTDIR)$(toolexeclibgoruntimedir)" "$(DESTDIR)$(toolexeclibgosyncdir)" "$(DESTDIR)$(toolexeclibgotestingdir)" "$(DESTDIR)$(toolexeclibgotextdir)" "$(DESTDIR)$(toolexeclibgotexttemplatedir)" "$(DESTDIR)$(toolexeclibgounicodedir)"; do \
test -z "$$dir" || $(MKDIR_P) "$$dir"; \
done
install: install-recursive
@ -4241,7 +4216,6 @@ install-exec-am: install-multi install-toolexeclibLIBRARIES \
install-toolexeclibgocryptox509DATA \
install-toolexeclibgodebugDATA \
install-toolexeclibgoencodingDATA install-toolexeclibgoexpDATA \
install-toolexeclibgoexpguiDATA \
install-toolexeclibgoexpsqlDATA install-toolexeclibgogoDATA \
install-toolexeclibgohashDATA install-toolexeclibgohtmlDATA \
install-toolexeclibgoimageDATA install-toolexeclibgoindexDATA \
@ -4307,7 +4281,6 @@ uninstall-am: uninstall-toolexeclibLIBRARIES \
uninstall-toolexeclibgodebugDATA \
uninstall-toolexeclibgoencodingDATA \
uninstall-toolexeclibgoexpDATA \
uninstall-toolexeclibgoexpguiDATA \
uninstall-toolexeclibgoexpsqlDATA \
uninstall-toolexeclibgogoDATA uninstall-toolexeclibgohashDATA \
uninstall-toolexeclibgohtmlDATA \
@ -4355,7 +4328,6 @@ uninstall-am: uninstall-toolexeclibLIBRARIES \
install-toolexeclibgocryptox509DATA \
install-toolexeclibgodebugDATA \
install-toolexeclibgoencodingDATA install-toolexeclibgoexpDATA \
install-toolexeclibgoexpguiDATA \
install-toolexeclibgoexpsqlDATA install-toolexeclibgogoDATA \
install-toolexeclibgohashDATA install-toolexeclibgohtmlDATA \
install-toolexeclibgoimageDATA install-toolexeclibgoindexDATA \
@ -4385,7 +4357,6 @@ uninstall-am: uninstall-toolexeclibLIBRARIES \
uninstall-toolexeclibgodebugDATA \
uninstall-toolexeclibgoencodingDATA \
uninstall-toolexeclibgoexpDATA \
uninstall-toolexeclibgoexpguiDATA \
uninstall-toolexeclibgoexpsqlDATA \
uninstall-toolexeclibgogoDATA uninstall-toolexeclibgohashDATA \
uninstall-toolexeclibgohtmlDATA \
@ -5383,16 +5354,6 @@ exp/ebnf/check: $(CHECK_DEPS)
@$(CHECK)
.PHONY: exp/ebnf/check
@go_include@ exp/gui.lo.dep
exp/gui.lo.dep: $(go_exp_gui_files)
$(BUILDDEPS)
exp/gui.lo: $(go_exp_gui_files)
$(BUILDPACKAGE)
exp/gui/check: $(CHECK_DEPS)
@$(MKDIR_P) exp/gui
@$(CHECK)
.PHONY: exp/gui/check
@go_include@ exp/norm.lo.dep
exp/norm.lo.dep: $(go_exp_norm_files)
$(BUILDDEPS)
@ -5453,16 +5414,6 @@ exp/types/check: $(CHECK_DEPS)
@$(CHECK)
.PHONY: exp/types/check
@go_include@ exp/gui/x11.lo.dep
exp/gui/x11.lo.dep: $(go_exp_gui_x11_files)
$(BUILDDEPS)
exp/gui/x11.lo: $(go_exp_gui_x11_files)
$(BUILDPACKAGE)
exp/gui/x11/check: $(CHECK_DEPS)
@$(MKDIR_P) exp/gui/x11
@$(CHECK)
.PHONY: exp/gui/x11/check
@go_include@ exp/inotify.lo.dep
exp/inotify.lo.dep: $(go_exp_inotify_files)
$(BUILDDEPS)
@ -6280,8 +6231,6 @@ encoding/xml.gox: encoding/xml.lo
exp/ebnf.gox: exp/ebnf.lo
$(BUILDGOX)
exp/gui.gox: exp/gui.lo
$(BUILDGOX)
exp/inotify.gox: exp/inotify.lo
$(BUILDGOX)
exp/norm.gox: exp/norm.lo
@ -6297,9 +6246,6 @@ exp/terminal.gox: exp/terminal.lo
exp/types.gox: exp/types.lo
$(BUILDGOX)
exp/gui/x11.gox: exp/gui/x11.lo
$(BUILDGOX)
exp/sql/driver.gox: exp/sql/driver.lo
$(BUILDGOX)

View File

@ -11,41 +11,42 @@
// http://www.gnu.org/software/tar/manual/html_node/Standard.html
package tar
import "time"
const (
blockSize = 512
// Types
TypeReg = '0' // regular file.
TypeRegA = '\x00' // regular file.
TypeLink = '1' // hard link.
TypeSymlink = '2' // symbolic link.
TypeChar = '3' // character device node.
TypeBlock = '4' // block device node.
TypeDir = '5' // directory.
TypeFifo = '6' // fifo node.
TypeCont = '7' // reserved.
TypeXHeader = 'x' // extended header.
TypeXGlobalHeader = 'g' // global extended header.
TypeReg = '0' // regular file
TypeRegA = '\x00' // regular file
TypeLink = '1' // hard link
TypeSymlink = '2' // symbolic link
TypeChar = '3' // character device node
TypeBlock = '4' // block device node
TypeDir = '5' // directory
TypeFifo = '6' // fifo node
TypeCont = '7' // reserved
TypeXHeader = 'x' // extended header
TypeXGlobalHeader = 'g' // global extended header
)
// A Header represents a single header in a tar archive.
// Some fields may not be populated.
type Header struct {
Name string // name of header file entry.
Mode int64 // permission and mode bits.
Uid int // user id of owner.
Gid int // group id of owner.
Size int64 // length in bytes.
Mtime int64 // modified time; seconds since epoch.
Typeflag byte // type of header entry.
Linkname string // target name of link.
Uname string // user name of owner.
Gname string // group name of owner.
Devmajor int64 // major number of character or block device.
Devminor int64 // minor number of character or block device.
Atime int64 // access time; seconds since epoch.
Ctime int64 // status change time; seconds since epoch.
Name string // name of header file entry
Mode int64 // permission and mode bits
Uid int // user id of owner
Gid int // group id of owner
Size int64 // length in bytes
ModTime time.Time // modified time
Typeflag byte // type of header entry
Linkname string // target name of link
Uname string // user name of owner
Gname string // group name of owner
Devmajor int64 // major number of character or block device
Devminor int64 // minor number of character or block device
AccessTime time.Time // access time
ChangeTime time.Time // status change time
}
var zeroBlock = make([]byte, blockSize)

View File

@ -14,6 +14,7 @@ import (
"io/ioutil"
"os"
"strconv"
"time"
)
var (
@ -141,7 +142,7 @@ func (tr *Reader) readHeader() *Header {
hdr.Uid = int(tr.octal(s.next(8)))
hdr.Gid = int(tr.octal(s.next(8)))
hdr.Size = tr.octal(s.next(12))
hdr.Mtime = tr.octal(s.next(12))
hdr.ModTime = time.Unix(tr.octal(s.next(12)), 0)
s.next(8) // chksum
hdr.Typeflag = s.next(1)[0]
hdr.Linkname = cString(s.next(100))
@ -178,8 +179,8 @@ func (tr *Reader) readHeader() *Header {
prefix = cString(s.next(155))
case "star":
prefix = cString(s.next(131))
hdr.Atime = tr.octal(s.next(12))
hdr.Ctime = tr.octal(s.next(12))
hdr.AccessTime = time.Unix(tr.octal(s.next(12)), 0)
hdr.ChangeTime = time.Unix(tr.octal(s.next(12)), 0)
}
if len(prefix) > 0 {
hdr.Name = prefix + "/" + hdr.Name

View File

@ -12,6 +12,7 @@ import (
"os"
"reflect"
"testing"
"time"
)
type untarTest struct {
@ -29,7 +30,7 @@ var gnuTarTest = &untarTest{
Uid: 73025,
Gid: 5000,
Size: 5,
Mtime: 1244428340,
ModTime: time.Unix(1244428340, 0),
Typeflag: '0',
Uname: "dsymonds",
Gname: "eng",
@ -40,7 +41,7 @@ var gnuTarTest = &untarTest{
Uid: 73025,
Gid: 5000,
Size: 11,
Mtime: 1244436044,
ModTime: time.Unix(1244436044, 0),
Typeflag: '0',
Uname: "dsymonds",
Gname: "eng",
@ -58,30 +59,30 @@ var untarTests = []*untarTest{
file: "testdata/star.tar",
headers: []*Header{
&Header{
Name: "small.txt",
Mode: 0640,
Uid: 73025,
Gid: 5000,
Size: 5,
Mtime: 1244592783,
Typeflag: '0',
Uname: "dsymonds",
Gname: "eng",
Atime: 1244592783,
Ctime: 1244592783,
Name: "small.txt",
Mode: 0640,
Uid: 73025,
Gid: 5000,
Size: 5,
ModTime: time.Unix(1244592783, 0),
Typeflag: '0',
Uname: "dsymonds",
Gname: "eng",
AccessTime: time.Unix(1244592783, 0),
ChangeTime: time.Unix(1244592783, 0),
},
&Header{
Name: "small2.txt",
Mode: 0640,
Uid: 73025,
Gid: 5000,
Size: 11,
Mtime: 1244592783,
Typeflag: '0',
Uname: "dsymonds",
Gname: "eng",
Atime: 1244592783,
Ctime: 1244592783,
Name: "small2.txt",
Mode: 0640,
Uid: 73025,
Gid: 5000,
Size: 11,
ModTime: time.Unix(1244592783, 0),
Typeflag: '0',
Uname: "dsymonds",
Gname: "eng",
AccessTime: time.Unix(1244592783, 0),
ChangeTime: time.Unix(1244592783, 0),
},
},
},
@ -94,7 +95,7 @@ var untarTests = []*untarTest{
Uid: 73025,
Gid: 5000,
Size: 5,
Mtime: 1244593104,
ModTime: time.Unix(1244593104, 0),
Typeflag: '\x00',
},
&Header{
@ -103,7 +104,7 @@ var untarTests = []*untarTest{
Uid: 73025,
Gid: 5000,
Size: 11,
Mtime: 1244593104,
ModTime: time.Unix(1244593104, 0),
Typeflag: '\x00',
},
},
@ -221,7 +222,7 @@ func TestIncrementalRead(t *testing.T) {
h.Write(rdbuf[0:nr])
}
// verify checksum
have := fmt.Sprintf("%x", h.Sum())
have := fmt.Sprintf("%x", h.Sum(nil))
want := cksums[nread]
if want != have {
t.Errorf("Bad checksum on file %s:\nhave %+v\nwant %+v", hdr.Name, have, want)

View File

@ -127,19 +127,19 @@ func (tw *Writer) WriteHeader(hdr *Header) error {
// TODO(dsymonds): handle names longer than 100 chars
copy(s.next(100), []byte(hdr.Name))
tw.octal(s.next(8), hdr.Mode) // 100:108
tw.numeric(s.next(8), int64(hdr.Uid)) // 108:116
tw.numeric(s.next(8), int64(hdr.Gid)) // 116:124
tw.numeric(s.next(12), hdr.Size) // 124:136
tw.numeric(s.next(12), hdr.Mtime) // 136:148
s.next(8) // chksum (148:156)
s.next(1)[0] = hdr.Typeflag // 156:157
tw.cString(s.next(100), hdr.Linkname) // linkname (157:257)
copy(s.next(8), []byte("ustar\x0000")) // 257:265
tw.cString(s.next(32), hdr.Uname) // 265:297
tw.cString(s.next(32), hdr.Gname) // 297:329
tw.numeric(s.next(8), hdr.Devmajor) // 329:337
tw.numeric(s.next(8), hdr.Devminor) // 337:345
tw.octal(s.next(8), hdr.Mode) // 100:108
tw.numeric(s.next(8), int64(hdr.Uid)) // 108:116
tw.numeric(s.next(8), int64(hdr.Gid)) // 116:124
tw.numeric(s.next(12), hdr.Size) // 124:136
tw.numeric(s.next(12), hdr.ModTime.Unix()) // 136:148
s.next(8) // chksum (148:156)
s.next(1)[0] = hdr.Typeflag // 156:157
tw.cString(s.next(100), hdr.Linkname) // linkname (157:257)
copy(s.next(8), []byte("ustar\x0000")) // 257:265
tw.cString(s.next(32), hdr.Uname) // 265:297
tw.cString(s.next(32), hdr.Gname) // 297:329
tw.numeric(s.next(8), hdr.Devmajor) // 329:337
tw.numeric(s.next(8), hdr.Devminor) // 337:345
// Use the GNU magic instead of POSIX magic if we used any GNU extensions.
if tw.usedBinary {

View File

@ -11,6 +11,7 @@ import (
"io/ioutil"
"testing"
"testing/iotest"
"time"
)
type writerTestEntry struct {
@ -38,7 +39,7 @@ var writerTests = []*writerTest{
Uid: 73025,
Gid: 5000,
Size: 5,
Mtime: 1246508266,
ModTime: time.Unix(1246508266, 0),
Typeflag: '0',
Uname: "dsymonds",
Gname: "eng",
@ -52,7 +53,7 @@ var writerTests = []*writerTest{
Uid: 73025,
Gid: 5000,
Size: 11,
Mtime: 1245217492,
ModTime: time.Unix(1245217492, 0),
Typeflag: '0',
Uname: "dsymonds",
Gname: "eng",
@ -66,7 +67,7 @@ var writerTests = []*writerTest{
Uid: 1000,
Gid: 1000,
Size: 0,
Mtime: 1314603082,
ModTime: time.Unix(1314603082, 0),
Typeflag: '2',
Linkname: "small.txt",
Uname: "strings",
@ -89,7 +90,7 @@ var writerTests = []*writerTest{
Uid: 73025,
Gid: 5000,
Size: 16 << 30,
Mtime: 1254699560,
ModTime: time.Unix(1254699560, 0),
Typeflag: '0',
Uname: "dsymonds",
Gname: "eng",

View File

@ -56,7 +56,7 @@ func OpenReader(name string) (*ReadCloser, error) {
return nil, err
}
r := new(ReadCloser)
if err := r.init(f, fi.Size); err != nil {
if err := r.init(f, fi.Size()); err != nil {
f.Close()
return nil, err
}

View File

@ -164,8 +164,8 @@ func readTestFile(t *testing.T, ft ZipTestFile, f *File) {
t.Error(err)
return
}
if got, want := f.Mtime_ns()/1e9, mtime.Seconds(); got != want {
t.Errorf("%s: mtime=%s (%d); want %s (%d)", f.Name, time.SecondsToUTC(got), got, mtime, want)
if ft := f.ModTime(); !ft.Equal(mtime) {
t.Errorf("%s: mtime=%s, want %s", f.Name, ft, mtime)
}
testFileMode(t, f, ft.Mode)

View File

@ -11,8 +11,10 @@ This package does not support ZIP64 or disk spanning.
*/
package zip
import "errors"
import "time"
import (
"errors"
"time"
)
// Compression methods.
const (
@ -74,24 +76,26 @@ func recoverError(errp *error) {
// The resolution is 2s.
// See: http://msdn.microsoft.com/en-us/library/ms724247(v=VS.85).aspx
func msDosTimeToTime(dosDate, dosTime uint16) time.Time {
return time.Time{
return time.Date(
// date bits 0-4: day of month; 5-8: month; 9-15: years since 1980
Year: int64(dosDate>>9 + 1980),
Month: int(dosDate >> 5 & 0xf),
Day: int(dosDate & 0x1f),
int(dosDate>>9+1980),
time.Month(dosDate>>5&0xf),
int(dosDate&0x1f),
// time bits 0-4: second/2; 5-10: minute; 11-15: hour
Hour: int(dosTime >> 11),
Minute: int(dosTime >> 5 & 0x3f),
Second: int(dosTime & 0x1f * 2),
}
int(dosTime>>11),
int(dosTime>>5&0x3f),
int(dosTime&0x1f*2),
0, // nanoseconds
time.UTC,
)
}
// Mtime_ns returns the modified time in ns since epoch.
// ModTime returns the modification time.
// The resolution is 2s.
func (h *FileHeader) Mtime_ns() int64 {
t := msDosTimeToTime(h.ModifiedDate, h.ModifiedTime)
return t.Seconds() * 1e9
func (h *FileHeader) ModTime() time.Time {
return msDosTimeToTime(h.ModifiedDate, h.ModifiedTime)
}
// Mode returns the permission and mode bits for the FileHeader.

View File

@ -702,7 +702,7 @@ func TestTrim(t *testing.T) {
case "TrimRight":
f = TrimRight
default:
t.Error("Undefined trim function %s", name)
t.Errorf("Undefined trim function %s", name)
}
actual := string(f([]byte(tc.in), tc.cutset))
if actual != tc.out {

View File

@ -13,6 +13,7 @@ import (
"hash"
"hash/crc32"
"io"
"time"
)
// BUG(nigeltao): Comments and Names don't properly map UTF-8 character codes outside of
@ -42,11 +43,11 @@ var ChecksumError = errors.New("gzip checksum error")
// The gzip file stores a header giving metadata about the compressed file.
// That header is exposed as the fields of the Compressor and Decompressor structs.
type Header struct {
Comment string // comment
Extra []byte // "extra data"
Mtime uint32 // modification time (seconds since January 1, 1970)
Name string // file name
OS byte // operating system type
Comment string // comment
Extra []byte // "extra data"
ModTime time.Time // modification time
Name string // file name
OS byte // operating system type
}
// An Decompressor is an io.Reader that can be read to retrieve
@ -130,7 +131,7 @@ func (z *Decompressor) readHeader(save bool) error {
}
z.flg = z.buf[3]
if save {
z.Mtime = get4(z.buf[4:8])
z.ModTime = time.Unix(int64(get4(z.buf[4:8])), 0)
// z.buf[8] is xfl, ignored
z.OS = z.buf[9]
}

View File

@ -122,7 +122,7 @@ func (z *Compressor) Write(p []byte) (int, error) {
if z.Comment != "" {
z.buf[3] |= 0x10
}
put4(z.buf[4:8], z.Mtime)
put4(z.buf[4:8], uint32(z.ModTime.Unix()))
if z.level == BestCompression {
z.buf[8] = 2
} else if z.level == BestSpeed {

View File

@ -8,6 +8,7 @@ import (
"io"
"io/ioutil"
"testing"
"time"
)
// pipe creates two ends of a pipe that gzip and gunzip, and runs dfunc at the
@ -53,7 +54,7 @@ func TestWriter(t *testing.T) {
func(compressor *Compressor) {
compressor.Comment = "comment"
compressor.Extra = []byte("extra")
compressor.Mtime = 1e8
compressor.ModTime = time.Unix(1e8, 0)
compressor.Name = "name"
_, err := compressor.Write([]byte("payload"))
if err != nil {
@ -74,8 +75,8 @@ func TestWriter(t *testing.T) {
if string(decompressor.Extra) != "extra" {
t.Fatalf("extra is %q, want %q", decompressor.Extra, "extra")
}
if decompressor.Mtime != 1e8 {
t.Fatalf("mtime is %d, want %d", decompressor.Mtime, uint32(1e8))
if decompressor.ModTime.Unix() != 1e8 {
t.Fatalf("mtime is %d, want %d", decompressor.ModTime.Unix(), uint32(1e8))
}
if decompressor.Name != "name" {
t.Fatalf("name is %q, want %q", decompressor.Name, "name")

View File

@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package bcrypt implements Provos and Mazières's bcrypt adapative hashing
// Package bcrypt implements Provos and Mazières's bcrypt adaptive hashing
// algorithm. See http://www.usenix.org/event/usenix99/provos/provos.pdf
package bcrypt

View File

@ -214,7 +214,7 @@ func TestVectors(t *testing.T) {
msg, _ := hex.DecodeString(test.msg)
sha.Reset()
sha.Write(msg)
hashed := sha.Sum()
hashed := sha.Sum(nil)
r := fromHex(test.r)
s := fromHex(test.s)
if Verify(&pub, hashed, r, s) != test.ok {

View File

@ -48,15 +48,15 @@ func (h *hmac) tmpPad(xor byte) {
}
}
func (h *hmac) Sum() []byte {
sum := h.inner.Sum()
func (h *hmac) Sum(in []byte) []byte {
sum := h.inner.Sum(nil)
h.tmpPad(0x5c)
for i, b := range sum {
h.tmp[padSize+i] = b
}
h.outer.Reset()
h.outer.Write(h.tmp)
return h.outer.Sum()
return h.outer.Sum(in)
}
func (h *hmac) Write(p []byte) (n int, err error) {
@ -81,7 +81,7 @@ func New(h func() hash.Hash, key []byte) hash.Hash {
if len(key) > padSize {
// If key is too big, hash it.
hm.outer.Write(key)
key = hm.outer.Sum()
key = hm.outer.Sum(nil)
}
hm.key = make([]byte, len(key))
copy(hm.key, key)

View File

@ -192,7 +192,7 @@ func TestHMAC(t *testing.T) {
// Repetitive Sum() calls should return the same value
for k := 0; k < 2; k++ {
sum := fmt.Sprintf("%x", h.Sum())
sum := fmt.Sprintf("%x", h.Sum(nil))
if sum != tt.out {
t.Errorf("test %d.%d.%d: have %s want %s\n", i, j, k, sum, tt.out)
}

View File

@ -77,7 +77,7 @@ func (d *digest) Write(p []byte) (nn int, err error) {
return
}
func (d0 *digest) Sum() []byte {
func (d0 *digest) Sum(in []byte) []byte {
// Make a copy of d0, so that caller can keep writing and summing.
d := new(digest)
*d = *d0
@ -103,14 +103,11 @@ func (d0 *digest) Sum() []byte {
panic("d.nx != 0")
}
p := make([]byte, 16)
j := 0
for _, s := range d.s {
p[j+0] = byte(s >> 0)
p[j+1] = byte(s >> 8)
p[j+2] = byte(s >> 16)
p[j+3] = byte(s >> 24)
j += 4
in = append(in, byte(s>>0))
in = append(in, byte(s>>8))
in = append(in, byte(s>>16))
in = append(in, byte(s>>24))
}
return p
return in
}

View File

@ -58,10 +58,10 @@ func TestGolden(t *testing.T) {
io.WriteString(c, g.in)
} else {
io.WriteString(c, g.in[0:len(g.in)/2])
c.Sum()
c.Sum(nil)
io.WriteString(c, g.in[len(g.in)/2:])
}
s := fmt.Sprintf("%x", c.Sum())
s := fmt.Sprintf("%x", c.Sum(nil))
if s != g.out {
t.Fatalf("md4[%d](%s) = %s want %s", j, g.in, s, g.out)
}

View File

@ -77,7 +77,7 @@ func (d *digest) Write(p []byte) (nn int, err error) {
return
}
func (d0 *digest) Sum() []byte {
func (d0 *digest) Sum(in []byte) []byte {
// Make a copy of d0 so that caller can keep writing and summing.
d := new(digest)
*d = *d0
@ -103,14 +103,11 @@ func (d0 *digest) Sum() []byte {
panic("d.nx != 0")
}
p := make([]byte, 16)
j := 0
for _, s := range d.s {
p[j+0] = byte(s >> 0)
p[j+1] = byte(s >> 8)
p[j+2] = byte(s >> 16)
p[j+3] = byte(s >> 24)
j += 4
in = append(in, byte(s>>0))
in = append(in, byte(s>>8))
in = append(in, byte(s>>16))
in = append(in, byte(s>>24))
}
return p
return in
}

View File

@ -58,10 +58,10 @@ func TestGolden(t *testing.T) {
io.WriteString(c, g.in)
} else {
io.WriteString(c, g.in[0:len(g.in)/2])
c.Sum()
c.Sum(nil)
io.WriteString(c, g.in[len(g.in)/2:])
}
s := fmt.Sprintf("%x", c.Sum())
s := fmt.Sprintf("%x", c.Sum(nil))
if s != g.out {
t.Fatalf("md5[%d](%s) = %s want %s", j, g.in, s, g.out)
}

View File

@ -61,7 +61,7 @@ type responseData struct {
Version int `asn1:"optional,default:1,explicit,tag:0"`
RequestorName pkix.RDNSequence `asn1:"optional,explicit,tag:1"`
KeyHash []byte `asn1:"optional,explicit,tag:2"`
ProducedAt *time.Time
ProducedAt time.Time
Responses []singleResponse
}
@ -70,12 +70,12 @@ type singleResponse struct {
Good asn1.Flag `asn1:"explicit,tag:0,optional"`
Revoked revokedInfo `asn1:"explicit,tag:1,optional"`
Unknown asn1.Flag `asn1:"explicit,tag:2,optional"`
ThisUpdate *time.Time
NextUpdate *time.Time `asn1:"explicit,tag:0,optional"`
ThisUpdate time.Time
NextUpdate time.Time `asn1:"explicit,tag:0,optional"`
}
type revokedInfo struct {
RevocationTime *time.Time
RevocationTime time.Time
Reason int `asn1:"explicit,tag:0,optional"`
}
@ -97,7 +97,7 @@ type Response struct {
// Status is one of {Good, Revoked, Unknown, ServerFailed}
Status int
SerialNumber []byte
ProducedAt, ThisUpdate, NextUpdate, RevokedAt *time.Time
ProducedAt, ThisUpdate, NextUpdate, RevokedAt time.Time
RevocationReason int
Certificate *x509.Certificate
}
@ -161,7 +161,7 @@ func ParseResponse(bytes []byte) (*Response, error) {
pub := ret.Certificate.PublicKey.(*rsa.PublicKey)
h.Write(basicResp.TBSResponseData.Raw)
digest := h.Sum()
digest := h.Sum(nil)
signature := basicResp.Signature.RightAlign()
if rsa.VerifyPKCS1v15(pub, hashType, digest, signature) != nil {

View File

@ -15,7 +15,13 @@ func TestOCSPDecode(t *testing.T) {
t.Error(err)
}
expected := Response{Status: 0, SerialNumber: []byte{0x1, 0xd0, 0xfa}, RevocationReason: 0, ThisUpdate: &time.Time{Year: 2010, Month: 7, Day: 7, Hour: 15, Minute: 1, Second: 5, ZoneOffset: 0, Zone: "UTC"}, NextUpdate: &time.Time{Year: 2010, Month: 7, Day: 7, Hour: 18, Minute: 35, Second: 17, ZoneOffset: 0, Zone: "UTC"}}
expected := Response{
Status: 0,
SerialNumber: []byte{0x1, 0xd0, 0xfa},
RevocationReason: 0,
ThisUpdate: time.Date(2010, 7, 7, 15, 1, 5, 0, time.UTC),
NextUpdate: time.Date(2010, 7, 7, 18, 35, 17, 0, time.UTC),
}
if !reflect.DeepEqual(resp.ThisUpdate, resp.ThisUpdate) {
t.Errorf("resp.ThisUpdate: got %d, want %d", resp.ThisUpdate, expected.ThisUpdate)

View File

@ -41,8 +41,8 @@ func (cth *canonicalTextHash) Write(buf []byte) (int, error) {
return len(buf), nil
}
func (cth *canonicalTextHash) Sum() []byte {
return cth.h.Sum()
func (cth *canonicalTextHash) Sum(in []byte) []byte {
return cth.h.Sum(in)
}
func (cth *canonicalTextHash) Reset() {

View File

@ -17,8 +17,8 @@ func (r recordingHash) Write(b []byte) (n int, err error) {
return r.buf.Write(b)
}
func (r recordingHash) Sum() []byte {
return r.buf.Bytes()
func (r recordingHash) Sum(in []byte) []byte {
return append(in, r.buf.Bytes()...)
}
func (r recordingHash) Reset() {
@ -33,7 +33,7 @@ func testCanonicalText(t *testing.T, input, expected string) {
r := recordingHash{bytes.NewBuffer(nil)}
c := NewCanonicalTextHash(r)
c.Write([]byte(input))
result := c.Sum()
result := c.Sum(nil)
if expected != string(result) {
t.Errorf("input: %x got: %x want: %x", input, result, expected)
}

View File

@ -381,7 +381,7 @@ const defaultRSAKeyBits = 2048
// NewEntity returns an Entity that contains a fresh RSA/RSA keypair with a
// single identity composed of the given full name, comment and email, any of
// which may be empty but must not contain any of "()<>\x00".
func NewEntity(rand io.Reader, currentTimeSecs int64, name, comment, email string) (*Entity, error) {
func NewEntity(rand io.Reader, currentTime time.Time, name, comment, email string) (*Entity, error) {
uid := packet.NewUserId(name, comment, email)
if uid == nil {
return nil, error_.InvalidArgumentError("user id field contained invalid characters")
@ -395,11 +395,9 @@ func NewEntity(rand io.Reader, currentTimeSecs int64, name, comment, email strin
return nil, err
}
t := uint32(currentTimeSecs)
e := &Entity{
PrimaryKey: packet.NewRSAPublicKey(t, &signingPriv.PublicKey, false /* not a subkey */ ),
PrivateKey: packet.NewRSAPrivateKey(t, signingPriv, false /* not a subkey */ ),
PrimaryKey: packet.NewRSAPublicKey(currentTime, &signingPriv.PublicKey, false /* not a subkey */ ),
PrivateKey: packet.NewRSAPrivateKey(currentTime, signingPriv, false /* not a subkey */ ),
Identities: make(map[string]*Identity),
}
isPrimaryId := true
@ -407,7 +405,7 @@ func NewEntity(rand io.Reader, currentTimeSecs int64, name, comment, email strin
Name: uid.Name,
UserId: uid,
SelfSignature: &packet.Signature{
CreationTime: t,
CreationTime: currentTime,
SigType: packet.SigTypePositiveCert,
PubKeyAlgo: packet.PubKeyAlgoRSA,
Hash: crypto.SHA256,
@ -421,10 +419,10 @@ func NewEntity(rand io.Reader, currentTimeSecs int64, name, comment, email strin
e.Subkeys = make([]Subkey, 1)
e.Subkeys[0] = Subkey{
PublicKey: packet.NewRSAPublicKey(t, &encryptingPriv.PublicKey, true /* is a subkey */ ),
PrivateKey: packet.NewRSAPrivateKey(t, encryptingPriv, true /* is a subkey */ ),
PublicKey: packet.NewRSAPublicKey(currentTime, &encryptingPriv.PublicKey, true /* is a subkey */ ),
PrivateKey: packet.NewRSAPrivateKey(currentTime, encryptingPriv, true /* is a subkey */ ),
Sig: &packet.Signature{
CreationTime: t,
CreationTime: currentTime,
SigType: packet.SigTypeSubkeyBinding,
PubKeyAlgo: packet.PubKeyAlgoRSA,
Hash: crypto.SHA256,
@ -533,7 +531,7 @@ func (e *Entity) SignIdentity(identity string, signer *Entity) error {
SigType: packet.SigTypeGenericCert,
PubKeyAlgo: signer.PrivateKey.PubKeyAlgo,
Hash: crypto.SHA256,
CreationTime: uint32(time.Seconds()),
CreationTime: time.Now(),
IssuerKeyId: &signer.PrivateKey.KeyId,
}
if err := sig.SignKey(e.PrimaryKey, signer.PrivateKey); err != nil {

View File

@ -17,6 +17,7 @@ import (
"io/ioutil"
"math/big"
"strconv"
"time"
)
// PrivateKey represents a possibly encrypted private key. See RFC 4880,
@ -32,9 +33,9 @@ type PrivateKey struct {
iv []byte
}
func NewRSAPrivateKey(currentTimeSecs uint32, priv *rsa.PrivateKey, isSubkey bool) *PrivateKey {
func NewRSAPrivateKey(currentTime time.Time, priv *rsa.PrivateKey, isSubkey bool) *PrivateKey {
pk := new(PrivateKey)
pk.PublicKey = *NewRSAPublicKey(currentTimeSecs, &priv.PublicKey, isSubkey)
pk.PublicKey = *NewRSAPublicKey(currentTime, &priv.PublicKey, isSubkey)
pk.PrivateKey = priv
return pk
}
@ -99,13 +100,9 @@ func (pk *PrivateKey) parse(r io.Reader) (err error) {
}
func mod64kHash(d []byte) uint16 {
h := uint16(0)
for i := 0; i < len(d); i += 2 {
v := uint16(d[i]) << 8
if i+1 < len(d) {
v += uint16(d[i+1])
}
h += v
var h uint16
for _, b := range d {
h += uint16(b)
}
return h
}
@ -195,7 +192,7 @@ func (pk *PrivateKey) Decrypt(passphrase []byte) error {
}
h := sha1.New()
h.Write(data[:len(data)-sha1.Size])
sum := h.Sum()
sum := h.Sum(nil)
if !bytes.Equal(sum, data[len(data)-sha1.Size:]) {
return error_.StructuralError("private key checksum failure")
}

View File

@ -6,19 +6,20 @@ package packet
import (
"testing"
"time"
)
var privateKeyTests = []struct {
privateKeyHex string
creationTime uint32
creationTime time.Time
}{
{
privKeyRSAHex,
0x4cc349a8,
time.Unix(0x4cc349a8, 0),
},
{
privKeyElGamalHex,
0x4df9ee1a,
time.Unix(0x4df9ee1a, 0),
},
}
@ -43,7 +44,7 @@ func TestPrivateKeyRead(t *testing.T) {
continue
}
if privKey.CreationTime != test.creationTime || privKey.Encrypted {
if !privKey.CreationTime.Equal(test.creationTime) || privKey.Encrypted {
t.Errorf("#%d: bad result, got: %#v", i, privKey)
}
}

View File

@ -16,11 +16,12 @@ import (
"io"
"math/big"
"strconv"
"time"
)
// PublicKey represents an OpenPGP public key. See RFC 4880, section 5.5.2.
type PublicKey struct {
CreationTime uint32 // seconds since the epoch
CreationTime time.Time
PubKeyAlgo PublicKeyAlgorithm
PublicKey interface{} // Either a *rsa.PublicKey or *dsa.PublicKey
Fingerprint [20]byte
@ -38,9 +39,9 @@ func fromBig(n *big.Int) parsedMPI {
}
// NewRSAPublicKey returns a PublicKey that wraps the given rsa.PublicKey.
func NewRSAPublicKey(creationTimeSecs uint32, pub *rsa.PublicKey, isSubkey bool) *PublicKey {
func NewRSAPublicKey(creationTime time.Time, pub *rsa.PublicKey, isSubkey bool) *PublicKey {
pk := &PublicKey{
CreationTime: creationTimeSecs,
CreationTime: creationTime,
PubKeyAlgo: PubKeyAlgoRSA,
PublicKey: pub,
IsSubkey: isSubkey,
@ -62,7 +63,7 @@ func (pk *PublicKey) parse(r io.Reader) (err error) {
if buf[0] != 4 {
return error_.UnsupportedError("public key version")
}
pk.CreationTime = uint32(buf[1])<<24 | uint32(buf[2])<<16 | uint32(buf[3])<<8 | uint32(buf[4])
pk.CreationTime = time.Unix(int64(uint32(buf[1])<<24|uint32(buf[2])<<16|uint32(buf[3])<<8|uint32(buf[4])), 0)
pk.PubKeyAlgo = PublicKeyAlgorithm(buf[5])
switch pk.PubKeyAlgo {
case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly, PubKeyAlgoRSASignOnly:
@ -87,7 +88,7 @@ func (pk *PublicKey) setFingerPrintAndKeyId() {
fingerPrint := sha1.New()
pk.SerializeSignaturePrefix(fingerPrint)
pk.serializeWithoutHeaders(fingerPrint)
copy(pk.Fingerprint[:], fingerPrint.Sum())
copy(pk.Fingerprint[:], fingerPrint.Sum(nil))
pk.KeyId = binary.BigEndian.Uint64(pk.Fingerprint[12:20])
}
@ -234,10 +235,11 @@ func (pk *PublicKey) Serialize(w io.Writer) (err error) {
func (pk *PublicKey) serializeWithoutHeaders(w io.Writer) (err error) {
var buf [6]byte
buf[0] = 4
buf[1] = byte(pk.CreationTime >> 24)
buf[2] = byte(pk.CreationTime >> 16)
buf[3] = byte(pk.CreationTime >> 8)
buf[4] = byte(pk.CreationTime)
t := uint32(pk.CreationTime.Unix())
buf[1] = byte(t >> 24)
buf[2] = byte(t >> 16)
buf[3] = byte(t >> 8)
buf[4] = byte(t)
buf[5] = byte(pk.PubKeyAlgo)
_, err = w.Write(buf[:])
@ -269,7 +271,7 @@ func (pk *PublicKey) VerifySignature(signed hash.Hash, sig *Signature) (err erro
}
signed.Write(sig.HashSuffix)
hashBytes := signed.Sum()
hashBytes := signed.Sum(nil)
if hashBytes[0] != sig.HashTag[0] || hashBytes[1] != sig.HashTag[1] {
return error_.SignatureError("hash tag doesn't match")

View File

@ -8,19 +8,20 @@ import (
"bytes"
"encoding/hex"
"testing"
"time"
)
var pubKeyTests = []struct {
hexData string
hexFingerprint string
creationTime uint32
creationTime time.Time
pubKeyAlgo PublicKeyAlgorithm
keyId uint64
keyIdString string
keyIdShort string
}{
{rsaPkDataHex, rsaFingerprintHex, 0x4d3c5c10, PubKeyAlgoRSA, 0xa34d7e18c20c31bb, "A34D7E18C20C31BB", "C20C31BB"},
{dsaPkDataHex, dsaFingerprintHex, 0x4d432f89, PubKeyAlgoDSA, 0x8e8fbe54062f19ed, "8E8FBE54062F19ED", "062F19ED"},
{rsaPkDataHex, rsaFingerprintHex, time.Unix(0x4d3c5c10, 0), PubKeyAlgoRSA, 0xa34d7e18c20c31bb, "A34D7E18C20C31BB", "C20C31BB"},
{dsaPkDataHex, dsaFingerprintHex, time.Unix(0x4d432f89, 0), PubKeyAlgoDSA, 0x8e8fbe54062f19ed, "8E8FBE54062F19ED", "062F19ED"},
}
func TestPublicKeyRead(t *testing.T) {
@ -38,8 +39,8 @@ func TestPublicKeyRead(t *testing.T) {
if pk.PubKeyAlgo != test.pubKeyAlgo {
t.Errorf("#%d: bad public key algorithm got:%x want:%x", i, pk.PubKeyAlgo, test.pubKeyAlgo)
}
if pk.CreationTime != test.creationTime {
t.Errorf("#%d: bad creation time got:%x want:%x", i, pk.CreationTime, test.creationTime)
if !pk.CreationTime.Equal(test.creationTime) {
t.Errorf("#%d: bad creation time got:%v want:%v", i, pk.CreationTime, test.creationTime)
}
expectedFingerprint, _ := hex.DecodeString(test.hexFingerprint)
if !bytes.Equal(expectedFingerprint, pk.Fingerprint[:]) {

View File

@ -15,6 +15,7 @@ import (
"hash"
"io"
"strconv"
"time"
)
// Signature represents a signature. See RFC 4880, section 5.2.
@ -28,7 +29,7 @@ type Signature struct {
// HashTag contains the first two bytes of the hash for fast rejection
// of bad signed data.
HashTag [2]byte
CreationTime uint32 // Unix epoch time
CreationTime time.Time
RSASignature parsedMPI
DSASigR, DSASigS parsedMPI
@ -151,7 +152,7 @@ func parseSignatureSubpackets(sig *Signature, subpackets []byte, isHashed bool)
}
}
if sig.CreationTime == 0 {
if sig.CreationTime.IsZero() {
err = error_.StructuralError("no creation time in signature")
}
@ -223,7 +224,12 @@ func parseSignatureSubpacket(sig *Signature, subpacket []byte, isHashed bool) (r
err = error_.StructuralError("signature creation time not four bytes")
return
}
sig.CreationTime = binary.BigEndian.Uint32(subpacket)
t := binary.BigEndian.Uint32(subpacket)
if t == 0 {
sig.CreationTime = time.Time{}
} else {
sig.CreationTime = time.Unix(int64(t), 0)
}
case signatureExpirationSubpacket:
// Signature expiration time, section 5.2.3.10
if !isHashed {
@ -417,7 +423,7 @@ func (sig *Signature) signPrepareHash(h hash.Hash) (digest []byte, err error) {
}
h.Write(sig.HashSuffix)
digest = h.Sum()
digest = h.Sum(nil)
copy(sig.HashTag[:], digest)
return
}
@ -541,10 +547,7 @@ type outputSubpacket struct {
func (sig *Signature) buildSubpackets() (subpackets []outputSubpacket) {
creationTime := make([]byte, 4)
creationTime[0] = byte(sig.CreationTime >> 24)
creationTime[1] = byte(sig.CreationTime >> 16)
creationTime[2] = byte(sig.CreationTime >> 8)
creationTime[3] = byte(sig.CreationTime)
binary.BigEndian.PutUint32(creationTime, uint32(sig.CreationTime.Unix()))
subpackets = append(subpackets, outputSubpacket{true, creationTimeSubpacket, false, creationTime})
if sig.IssuerKeyId != nil {

View File

@ -201,7 +201,7 @@ func (ser *seMDCReader) Close() error {
}
ser.h.Write(ser.trailer[:2])
final := ser.h.Sum()
final := ser.h.Sum(nil)
if subtle.ConstantTimeCompare(final, ser.trailer[2:]) != 1 {
return error_.SignatureError("hash mismatch")
}
@ -227,7 +227,7 @@ func (w *seMDCWriter) Close() (err error) {
buf[0] = mdcPacketTagByte
buf[1] = sha1.Size
w.h.Write(buf[:2])
digest := w.h.Sum()
digest := w.h.Sum(nil)
copy(buf[2:], digest)
_, err = w.w.Write(buf[:])

View File

@ -34,7 +34,7 @@ func Salted(out []byte, h hash.Hash, in []byte, salt []byte) {
}
h.Write(salt)
h.Write(in)
n := copy(out[done:], h.Sum())
n := copy(out[done:], h.Sum(nil))
done += n
}
}
@ -68,7 +68,7 @@ func Iterated(out []byte, h hash.Hash, in []byte, salt []byte, count int) {
written += len(combined)
}
}
n := copy(out[done:], h.Sum())
n := copy(out[done:], h.Sum(nil))
done += n
}
}

View File

@ -68,7 +68,7 @@ func detachSign(w io.Writer, signer *Entity, message io.Reader, sigType packet.S
sig.SigType = sigType
sig.PubKeyAlgo = signer.PrivateKey.PubKeyAlgo
sig.Hash = crypto.SHA256
sig.CreationTime = uint32(time.Seconds())
sig.CreationTime = time.Now()
sig.IssuerKeyId = &signer.PrivateKey.KeyId
h, wrappedHash, err := hashForSignature(sig.Hash, sig.SigType)
@ -95,8 +95,8 @@ type FileHints struct {
// file should not be written to disk. It may be equal to "_CONSOLE" to
// suggest the data should not be written to disk.
FileName string
// EpochSeconds contains the modification time of the file, or 0 if not applicable.
EpochSeconds uint32
// ModTime contains the modification time of the file, or the zero time if not applicable.
ModTime time.Time
}
// SymmetricallyEncrypt acts like gpg -c: it encrypts a file with a passphrase.
@ -115,7 +115,11 @@ func SymmetricallyEncrypt(ciphertext io.Writer, passphrase []byte, hints *FileHi
if err != nil {
return
}
return packet.SerializeLiteral(w, hints.IsBinary, hints.FileName, hints.EpochSeconds)
var epochSeconds uint32
if !hints.ModTime.IsZero() {
epochSeconds = uint32(hints.ModTime.Unix())
}
return packet.SerializeLiteral(w, hints.IsBinary, hints.FileName, epochSeconds)
}
// intersectPreferences mutates and returns a prefix of a that contains only
@ -243,7 +247,11 @@ func Encrypt(ciphertext io.Writer, to []*Entity, signed *Entity, hints *FileHint
w = noOpCloser{encryptedData}
}
literalData, err := packet.SerializeLiteral(w, hints.IsBinary, hints.FileName, hints.EpochSeconds)
var epochSeconds uint32
if !hints.ModTime.IsZero() {
epochSeconds = uint32(hints.ModTime.Unix())
}
literalData, err := packet.SerializeLiteral(w, hints.IsBinary, hints.FileName, epochSeconds)
if err != nil {
return nil, err
}
@ -275,7 +283,7 @@ func (s signatureWriter) Close() error {
SigType: packet.SigTypeBinary,
PubKeyAlgo: s.signer.PubKeyAlgo,
Hash: s.hashType,
CreationTime: uint32(time.Seconds()),
CreationTime: time.Now(),
IssuerKeyId: &s.signer.KeyId,
}

View File

@ -54,7 +54,7 @@ func TestNewEntity(t *testing.T) {
return
}
e, err := NewEntity(rand.Reader, time.Seconds(), "Test User", "test", "test@example.com")
e, err := NewEntity(rand.Reader, time.Now(), "Test User", "test", "test@example.com")
if err != nil {
t.Errorf("failed to create entity: %s", err)
return

View File

@ -100,7 +100,7 @@ func (r *reader) Read(b []byte) (n int, err error) {
// t = encrypt(time)
// dst = encrypt(t^seed)
// seed = encrypt(t^dst)
ns := time.Nanoseconds()
ns := time.Now().UnixNano()
r.time[0] = byte(ns >> 56)
r.time[1] = byte(ns >> 48)
r.time[2] = byte(ns >> 40)

View File

@ -81,7 +81,7 @@ func (d *digest) Write(p []byte) (nn int, err error) {
return
}
func (d0 *digest) Sum() []byte {
func (d0 *digest) Sum(in []byte) []byte {
// Make a copy of d0 so that caller can keep writing and summing.
d := new(digest)
*d = *d0
@ -107,11 +107,11 @@ func (d0 *digest) Sum() []byte {
panic("d.nx != 0")
}
p := make([]byte, 20)
j := 0
for _, s := range d.s {
p[j], p[j+1], p[j+2], p[j+3] = byte(s), byte(s>>8), byte(s>>16), byte(s>>24)
j += 4
in = append(in, byte(s))
in = append(in, byte(s>>8))
in = append(in, byte(s>>16))
in = append(in, byte(s>>24))
}
return p
return in
}

View File

@ -38,10 +38,10 @@ func TestVectors(t *testing.T) {
io.WriteString(md, tv.in)
} else {
io.WriteString(md, tv.in[0:len(tv.in)/2])
md.Sum()
md.Sum(nil)
io.WriteString(md, tv.in[len(tv.in)/2:])
}
s := fmt.Sprintf("%x", md.Sum())
s := fmt.Sprintf("%x", md.Sum(nil))
if s != tv.out {
t.Fatalf("RIPEMD-160[%d](%s) = %s, expected %s", j, tv.in, s, tv.out)
}
@ -56,7 +56,7 @@ func TestMillionA(t *testing.T) {
io.WriteString(md, "aaaaaaaaaa")
}
out := "52783243c1697bdbe16d37f97f68f08325dc1528"
s := fmt.Sprintf("%x", md.Sum())
s := fmt.Sprintf("%x", md.Sum(nil))
if s != out {
t.Fatalf("RIPEMD-160 (1 million 'a') = %s, expected %s", s, out)
}

View File

@ -168,7 +168,7 @@ func TestSignPKCS1v15(t *testing.T) {
for i, test := range signPKCS1v15Tests {
h := sha1.New()
h.Write([]byte(test.in))
digest := h.Sum()
digest := h.Sum(nil)
s, err := SignPKCS1v15(nil, rsaPrivateKey, crypto.SHA1, digest)
if err != nil {
@ -186,7 +186,7 @@ func TestVerifyPKCS1v15(t *testing.T) {
for i, test := range signPKCS1v15Tests {
h := sha1.New()
h.Write([]byte(test.in))
digest := h.Sum()
digest := h.Sum(nil)
sig, _ := hex.DecodeString(test.out)

View File

@ -194,7 +194,7 @@ func mgf1XOR(out []byte, hash hash.Hash, seed []byte) {
for done < len(out) {
hash.Write(seed)
hash.Write(counter[0:4])
digest := hash.Sum()
digest := hash.Sum(nil)
hash.Reset()
for i := 0; i < len(digest) && done < len(out); i++ {
@ -231,7 +231,7 @@ func EncryptOAEP(hash hash.Hash, random io.Reader, pub *PublicKey, msg []byte, l
}
hash.Write(label)
lHash := hash.Sum()
lHash := hash.Sum(nil)
hash.Reset()
em := make([]byte, k)
@ -428,7 +428,7 @@ func DecryptOAEP(hash hash.Hash, random io.Reader, priv *PrivateKey, ciphertext
}
hash.Write(label)
lHash := hash.Sum()
lHash := hash.Sum(nil)
hash.Reset()
// Converting the plaintext number to bytes will strip any

View File

@ -79,7 +79,7 @@ func (d *digest) Write(p []byte) (nn int, err error) {
return
}
func (d0 *digest) Sum() []byte {
func (d0 *digest) Sum(in []byte) []byte {
// Make a copy of d0 so that caller can keep writing and summing.
d := new(digest)
*d = *d0
@ -105,14 +105,11 @@ func (d0 *digest) Sum() []byte {
panic("d.nx != 0")
}
p := make([]byte, 20)
j := 0
for _, s := range d.h {
p[j+0] = byte(s >> 24)
p[j+1] = byte(s >> 16)
p[j+2] = byte(s >> 8)
p[j+3] = byte(s >> 0)
j += 4
in = append(in, byte(s>>24))
in = append(in, byte(s>>16))
in = append(in, byte(s>>8))
in = append(in, byte(s))
}
return p
return in
}

View File

@ -60,10 +60,10 @@ func TestGolden(t *testing.T) {
io.WriteString(c, g.in)
} else {
io.WriteString(c, g.in[0:len(g.in)/2])
c.Sum()
c.Sum(nil)
io.WriteString(c, g.in[len(g.in)/2:])
}
s := fmt.Sprintf("%x", c.Sum())
s := fmt.Sprintf("%x", c.Sum(nil))
if s != g.out {
t.Fatalf("sha1[%d](%s) = %s want %s", j, g.in, s, g.out)
}

View File

@ -123,7 +123,7 @@ func (d *digest) Write(p []byte) (nn int, err error) {
return
}
func (d0 *digest) Sum() []byte {
func (d0 *digest) Sum(in []byte) []byte {
// Make a copy of d0 so that caller can keep writing and summing.
d := new(digest)
*d = *d0
@ -149,17 +149,15 @@ func (d0 *digest) Sum() []byte {
panic("d.nx != 0")
}
p := make([]byte, 32)
j := 0
for _, s := range d.h {
p[j+0] = byte(s >> 24)
p[j+1] = byte(s >> 16)
p[j+2] = byte(s >> 8)
p[j+3] = byte(s >> 0)
j += 4
}
h := d.h[:]
if d.is224 {
return p[0:28]
h = d.h[:7]
}
return p
for _, s := range h {
in = append(in, byte(s>>24))
in = append(in, byte(s>>16))
in = append(in, byte(s>>8))
in = append(in, byte(s))
}
return in
}

View File

@ -94,10 +94,10 @@ func TestGolden(t *testing.T) {
io.WriteString(c, g.in)
} else {
io.WriteString(c, g.in[0:len(g.in)/2])
c.Sum()
c.Sum(nil)
io.WriteString(c, g.in[len(g.in)/2:])
}
s := fmt.Sprintf("%x", c.Sum())
s := fmt.Sprintf("%x", c.Sum(nil))
if s != g.out {
t.Fatalf("sha256[%d](%s) = %s want %s", j, g.in, s, g.out)
}
@ -112,10 +112,10 @@ func TestGolden(t *testing.T) {
io.WriteString(c, g.in)
} else {
io.WriteString(c, g.in[0:len(g.in)/2])
c.Sum()
c.Sum(nil)
io.WriteString(c, g.in[len(g.in)/2:])
}
s := fmt.Sprintf("%x", c.Sum())
s := fmt.Sprintf("%x", c.Sum(nil))
if s != g.out {
t.Fatalf("sha224[%d](%s) = %s want %s", j, g.in, s, g.out)
}

View File

@ -123,7 +123,7 @@ func (d *digest) Write(p []byte) (nn int, err error) {
return
}
func (d0 *digest) Sum() []byte {
func (d0 *digest) Sum(in []byte) []byte {
// Make a copy of d0 so that caller can keep writing and summing.
d := new(digest)
*d = *d0
@ -149,21 +149,19 @@ func (d0 *digest) Sum() []byte {
panic("d.nx != 0")
}
p := make([]byte, 64)
j := 0
for _, s := range d.h {
p[j+0] = byte(s >> 56)
p[j+1] = byte(s >> 48)
p[j+2] = byte(s >> 40)
p[j+3] = byte(s >> 32)
p[j+4] = byte(s >> 24)
p[j+5] = byte(s >> 16)
p[j+6] = byte(s >> 8)
p[j+7] = byte(s >> 0)
j += 8
}
h := d.h[:]
if d.is384 {
return p[0:48]
h = d.h[:6]
}
return p
for _, s := range h {
in = append(in, byte(s>>56))
in = append(in, byte(s>>48))
in = append(in, byte(s>>40))
in = append(in, byte(s>>32))
in = append(in, byte(s>>24))
in = append(in, byte(s>>16))
in = append(in, byte(s>>8))
in = append(in, byte(s))
}
return in
}

View File

@ -94,10 +94,10 @@ func TestGolden(t *testing.T) {
io.WriteString(c, g.in)
} else {
io.WriteString(c, g.in[0:len(g.in)/2])
c.Sum()
c.Sum(nil)
io.WriteString(c, g.in[len(g.in)/2:])
}
s := fmt.Sprintf("%x", c.Sum())
s := fmt.Sprintf("%x", c.Sum(nil))
if s != g.out {
t.Fatalf("sha512[%d](%s) = %s want %s", j, g.in, s, g.out)
}
@ -112,10 +112,10 @@ func TestGolden(t *testing.T) {
io.WriteString(c, g.in)
} else {
io.WriteString(c, g.in[0:len(g.in)/2])
c.Sum()
c.Sum(nil)
io.WriteString(c, g.in[len(g.in)/2:])
}
s := fmt.Sprintf("%x", c.Sum())
s := fmt.Sprintf("%x", c.Sum(nil))
if s != g.out {
t.Fatalf("sha384[%d](%s) = %s want %s", j, g.in, s, g.out)
}

View File

@ -37,6 +37,7 @@ type keyAgreement interface {
// A cipherSuite is a specific combination of key agreement, cipher and MAC
// function. All cipher suites currently assume RSA key agreement.
type cipherSuite struct {
id uint16
// the lengths, in bytes, of the key material needed for each component.
keyLen int
macLen int
@ -50,13 +51,13 @@ type cipherSuite struct {
mac func(version uint16, macKey []byte) macFunction
}
var cipherSuites = map[uint16]*cipherSuite{
TLS_RSA_WITH_RC4_128_SHA: &cipherSuite{16, 20, 0, rsaKA, false, cipherRC4, macSHA1},
TLS_RSA_WITH_3DES_EDE_CBC_SHA: &cipherSuite{24, 20, 8, rsaKA, false, cipher3DES, macSHA1},
TLS_RSA_WITH_AES_128_CBC_SHA: &cipherSuite{16, 20, 16, rsaKA, false, cipherAES, macSHA1},
TLS_ECDHE_RSA_WITH_RC4_128_SHA: &cipherSuite{16, 20, 0, ecdheRSAKA, true, cipherRC4, macSHA1},
TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA: &cipherSuite{24, 20, 8, ecdheRSAKA, true, cipher3DES, macSHA1},
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA: &cipherSuite{16, 20, 16, ecdheRSAKA, true, cipherAES, macSHA1},
var cipherSuites = []*cipherSuite{
&cipherSuite{TLS_RSA_WITH_RC4_128_SHA, 16, 20, 0, rsaKA, false, cipherRC4, macSHA1},
&cipherSuite{TLS_RSA_WITH_3DES_EDE_CBC_SHA, 24, 20, 8, rsaKA, false, cipher3DES, macSHA1},
&cipherSuite{TLS_RSA_WITH_AES_128_CBC_SHA, 16, 20, 16, rsaKA, false, cipherAES, macSHA1},
&cipherSuite{TLS_ECDHE_RSA_WITH_RC4_128_SHA, 16, 20, 0, ecdheRSAKA, true, cipherRC4, macSHA1},
&cipherSuite{TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, 24, 20, 8, ecdheRSAKA, true, cipher3DES, macSHA1},
&cipherSuite{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, 16, 20, 16, ecdheRSAKA, true, cipherAES, macSHA1},
}
func cipherRC4(key, iv []byte, isRead bool) interface{} {
@ -126,13 +127,13 @@ func (s ssl30MAC) MAC(seq, record []byte) []byte {
s.h.Write(record[:1])
s.h.Write(record[3:5])
s.h.Write(record[recordHeaderLen:])
digest := s.h.Sum()
digest := s.h.Sum(nil)
s.h.Reset()
s.h.Write(s.key)
s.h.Write(ssl30Pad2[:padLength])
s.h.Write(digest)
return s.h.Sum()
return s.h.Sum(nil)
}
// tls10MAC implements the TLS 1.0 MAC function. RFC 2246, section 6.2.3.
@ -148,7 +149,7 @@ func (s tls10MAC) MAC(seq, record []byte) []byte {
s.h.Reset()
s.h.Write(seq)
s.h.Write(record)
return s.h.Sum()
return s.h.Sum(nil)
}
func rsaKA() keyAgreement {
@ -159,15 +160,20 @@ func ecdheRSAKA() keyAgreement {
return new(ecdheRSAKeyAgreement)
}
// mutualCipherSuite returns a cipherSuite and its id given a list of supported
// mutualCipherSuite returns a cipherSuite given a list of supported
// ciphersuites and the id requested by the peer.
func mutualCipherSuite(have []uint16, want uint16) (suite *cipherSuite, id uint16) {
func mutualCipherSuite(have []uint16, want uint16) *cipherSuite {
for _, id := range have {
if id == want {
return cipherSuites[id], id
for _, suite := range cipherSuites {
if suite.id == want {
return suite
}
}
return nil
}
}
return
return nil
}
// A list of the possible cipher suite ids. Taken from

View File

@ -121,7 +121,7 @@ type Config struct {
// Time returns the current time as the number of seconds since the epoch.
// If Time is nil, TLS uses the system time.Seconds.
Time func() int64
Time func() time.Time
// Certificates contains one or more certificate chains
// to present to the other side of the connection.
@ -175,10 +175,10 @@ func (c *Config) rand() io.Reader {
return r
}
func (c *Config) time() int64 {
func (c *Config) time() time.Time {
t := c.Time
if t == nil {
t = time.Seconds
t = time.Now
}
return t()
}
@ -315,9 +315,7 @@ var (
func initDefaultCipherSuites() {
varDefaultCipherSuites = make([]uint16, len(cipherSuites))
i := 0
for id := range cipherSuites {
varDefaultCipherSuites[i] = id
i++
for i, suite := range cipherSuites {
varDefaultCipherSuites[i] = suite.id
}
}

View File

@ -32,7 +32,7 @@ func (c *Conn) clientHandshake() error {
nextProtoNeg: len(c.config.NextProtos) > 0,
}
t := uint32(c.config.time())
t := uint32(c.config.time().Unix())
hello.random[0] = byte(t >> 24)
hello.random[1] = byte(t >> 16)
hello.random[2] = byte(t >> 8)
@ -72,7 +72,7 @@ func (c *Conn) clientHandshake() error {
return errors.New("server advertised unrequested NPN")
}
suite, suiteId := mutualCipherSuite(c.config.cipherSuites(), serverHello.cipherSuite)
suite := mutualCipherSuite(c.config.cipherSuites(), serverHello.cipherSuite)
if suite == nil {
return c.sendAlert(alertHandshakeFailure)
}
@ -232,8 +232,8 @@ func (c *Conn) clientHandshake() error {
if cert != nil {
certVerify := new(certificateVerifyMsg)
var digest [36]byte
copy(digest[0:16], finishedHash.serverMD5.Sum())
copy(digest[16:36], finishedHash.serverSHA1.Sum())
copy(digest[0:16], finishedHash.serverMD5.Sum(nil))
copy(digest[16:36], finishedHash.serverSHA1.Sum(nil))
signed, err := rsa.SignPKCS1v15(c.config.rand(), c.config.Certificates[0].PrivateKey, crypto.MD5SHA1, digest[0:])
if err != nil {
return c.sendAlert(alertInternalError)
@ -292,7 +292,7 @@ func (c *Conn) clientHandshake() error {
}
c.handshakeComplete = true
c.cipherSuite = suiteId
c.cipherSuite = suite.id
return nil
}

View File

@ -56,18 +56,25 @@ Curves:
ellipticOk := supportedCurve && supportedPointFormat
var suite *cipherSuite
var suiteId uint16
FindCipherSuite:
for _, id := range clientHello.cipherSuites {
for _, supported := range config.cipherSuites() {
if id == supported {
suite = cipherSuites[id]
suite = nil
for _, s := range cipherSuites {
if s.id == id {
suite = s
break
}
}
if suite == nil {
continue
}
// Don't select a ciphersuite which we can't
// support for this client.
if suite.elliptic && !ellipticOk {
continue
}
suiteId = id
break FindCipherSuite
}
}
@ -87,8 +94,8 @@ FindCipherSuite:
}
hello.vers = vers
hello.cipherSuite = suiteId
t := uint32(config.time())
hello.cipherSuite = suite.id
t := uint32(config.time().Unix())
hello.random = make([]byte, 32)
hello.random[0] = byte(t >> 24)
hello.random[1] = byte(t >> 16)
@ -228,8 +235,8 @@ FindCipherSuite:
}
digest := make([]byte, 36)
copy(digest[0:16], finishedHash.serverMD5.Sum())
copy(digest[16:36], finishedHash.serverSHA1.Sum())
copy(digest[0:16], finishedHash.serverMD5.Sum(nil))
copy(digest[16:36], finishedHash.serverSHA1.Sum(nil))
err = rsa.VerifyPKCS1v15(pub, crypto.MD5SHA1, digest, certVerify.signature)
if err != nil {
c.sendAlert(alertBadCertificate)
@ -296,7 +303,7 @@ FindCipherSuite:
c.writeRecord(recordTypeHandshake, finished.marshal())
c.handshakeComplete = true
c.cipherSuite = suiteId
c.cipherSuite = suite.id
return nil
}

View File

@ -15,6 +15,7 @@ import (
"strconv"
"strings"
"testing"
"time"
)
type zeroSource struct{}
@ -31,7 +32,7 @@ var testConfig *Config
func init() {
testConfig = new(Config)
testConfig.Time = func() int64 { return 0 }
testConfig.Time = func() time.Time { return time.Unix(0, 0) }
testConfig.Rand = zeroSource{}
testConfig.Certificates = make([]Certificate, 1)
testConfig.Certificates[0].Certificate = [][]byte{testCertificate}

View File

@ -90,13 +90,13 @@ func md5SHA1Hash(slices ...[]byte) []byte {
for _, slice := range slices {
hmd5.Write(slice)
}
copy(md5sha1, hmd5.Sum())
copy(md5sha1, hmd5.Sum(nil))
hsha1 := sha1.New()
for _, slice := range slices {
hsha1.Write(slice)
}
copy(md5sha1[md5.Size:], hsha1.Sum())
copy(md5sha1[md5.Size:], hsha1.Sum(nil))
return md5sha1
}

View File

@ -22,14 +22,14 @@ func splitPreMasterSecret(secret []byte) (s1, s2 []byte) {
func pHash(result, secret, seed []byte, hash func() hash.Hash) {
h := hmac.New(hash, secret)
h.Write(seed)
a := h.Sum()
a := h.Sum(nil)
j := 0
for j < len(result) {
h.Reset()
h.Write(a)
h.Write(seed)
b := h.Sum()
b := h.Sum(nil)
todo := len(b)
if j+todo > len(result) {
todo = len(result) - j
@ -39,7 +39,7 @@ func pHash(result, secret, seed []byte, hash func() hash.Hash) {
h.Reset()
h.Write(a)
a = h.Sum()
a = h.Sum(nil)
}
}
@ -84,13 +84,13 @@ func pRF30(result, secret, label, seed []byte) {
hashSHA1.Write(b[:i+1])
hashSHA1.Write(secret)
hashSHA1.Write(seed)
digest := hashSHA1.Sum()
digest := hashSHA1.Sum(nil)
hashMD5.Reset()
hashMD5.Write(secret)
hashMD5.Write(digest)
done += copy(result[done:], hashMD5.Sum())
done += copy(result[done:], hashMD5.Sum(nil))
i++
}
}
@ -182,24 +182,24 @@ func finishedSum30(md5, sha1 hash.Hash, masterSecret []byte, magic [4]byte) []by
md5.Write(magic[:])
md5.Write(masterSecret)
md5.Write(ssl30Pad1[:])
md5Digest := md5.Sum()
md5Digest := md5.Sum(nil)
md5.Reset()
md5.Write(masterSecret)
md5.Write(ssl30Pad2[:])
md5.Write(md5Digest)
md5Digest = md5.Sum()
md5Digest = md5.Sum(nil)
sha1.Write(magic[:])
sha1.Write(masterSecret)
sha1.Write(ssl30Pad1[:40])
sha1Digest := sha1.Sum()
sha1Digest := sha1.Sum(nil)
sha1.Reset()
sha1.Write(masterSecret)
sha1.Write(ssl30Pad2[:40])
sha1.Write(sha1Digest)
sha1Digest = sha1.Sum()
sha1Digest = sha1.Sum(nil)
ret := make([]byte, len(md5Digest)+len(sha1Digest))
copy(ret, md5Digest)
@ -217,8 +217,8 @@ func (h finishedHash) clientSum(masterSecret []byte) []byte {
return finishedSum30(h.clientMD5, h.clientSHA1, masterSecret, ssl3ClientFinishedMagic)
}
md5 := h.clientMD5.Sum()
sha1 := h.clientSHA1.Sum()
md5 := h.clientMD5.Sum(nil)
sha1 := h.clientSHA1.Sum(nil)
return finishedSum10(md5, sha1, clientFinishedLabel, masterSecret)
}
@ -229,7 +229,7 @@ func (h finishedHash) serverSum(masterSecret []byte) []byte {
return finishedSum30(h.serverMD5, h.serverSHA1, masterSecret, ssl3ServerFinishedMagic)
}
md5 := h.serverMD5.Sum()
sha1 := h.serverSHA1.Sum()
md5 := h.serverMD5.Sum(nil)
sha1 := h.serverSHA1.Sum(nil)
return finishedSum10(md5, sha1, serverFinishedLabel, masterSecret)
}

View File

@ -14,6 +14,7 @@ var certFiles = []string{
"/etc/ssl/certs/ca-certificates.crt", // Linux etc
"/etc/pki/tls/certs/ca-bundle.crt", // Fedora/RHEL
"/etc/ssl/ca-bundle.pem", // OpenSUSE
"/etc/ssl/cert.pem", // OpenBSD
}
func initDefaultRoots() {

View File

@ -6,7 +6,6 @@ package tls
import (
"crypto/x509"
"reflect"
"syscall"
"unsafe"
)
@ -16,29 +15,23 @@ func loadStore(roots *x509.CertPool, name string) {
if err != nil {
return
}
defer syscall.CertCloseStore(store, 0)
var cert *syscall.CertContext
for {
cert = syscall.CertEnumCertificatesInStore(store, cert)
if cert == nil {
break
cert, err = syscall.CertEnumCertificatesInStore(store, cert)
if err != nil {
return
}
var asn1Slice []byte
hdrp := (*reflect.SliceHeader)(unsafe.Pointer(&asn1Slice))
hdrp.Data = cert.EncodedCert
hdrp.Len = int(cert.Length)
hdrp.Cap = int(cert.Length)
buf := make([]byte, len(asn1Slice))
copy(buf, asn1Slice)
if cert, err := x509.ParseCertificate(buf); err == nil {
roots.AddCert(cert)
buf := (*[1 << 20]byte)(unsafe.Pointer(cert.EncodedCert))[:]
// ParseCertificate requires its own copy of certificate data to keep.
buf2 := make([]byte, cert.Length)
copy(buf2, buf)
if c, err := x509.ParseCertificate(buf2); err == nil {
roots.AddCert(c)
}
}
syscall.CertCloseStore(store, 0)
}
func initDefaultRoots() {

View File

@ -157,10 +157,21 @@ func X509KeyPair(certPEMBlock, keyPEMBlock []byte) (cert Certificate, err error)
return
}
key, err := x509.ParsePKCS1PrivateKey(keyDERBlock.Bytes)
if err != nil {
err = errors.New("crypto/tls: failed to parse key: " + err.Error())
return
// OpenSSL 0.9.8 generates PKCS#1 private keys by default, while
// OpenSSL 1.0.0 generates PKCS#8 keys. We try both.
var key *rsa.PrivateKey
if key, err = x509.ParsePKCS1PrivateKey(keyDERBlock.Bytes); err != nil {
var privKey interface{}
if privKey, err = x509.ParsePKCS8PrivateKey(keyDERBlock.Bytes); err != nil {
err = errors.New("crypto/tls: failed to parse key: " + err.Error())
return
}
var ok bool
if key, ok = privKey.(*rsa.PrivateKey); !ok {
err = errors.New("crypto/tls: found non-RSA private key in PKCS#8 wrapping")
return
}
}
cert.PrivateKey = key

View File

@ -8,7 +8,7 @@ import (
"encoding/pem"
)
// Roots is a set of certificates.
// CertPool is a set of certificates.
type CertPool struct {
bySubjectKeyId map[string][]int
byName map[string][]int
@ -70,11 +70,11 @@ func (s *CertPool) AddCert(cert *Certificate) {
s.byName[name] = append(s.byName[name], n)
}
// AppendCertsFromPEM attempts to parse a series of PEM encoded root
// certificates. It appends any certificates found to s and returns true if any
// certificates were successfully parsed.
// AppendCertsFromPEM attempts to parse a series of PEM encoded certificates.
// It appends any certificates found to s and returns true if any certificates
// were successfully parsed.
//
// On many Linux systems, /etc/ssl/cert.pem will contains the system wide set
// On many Linux systems, /etc/ssl/cert.pem will contain the system wide set
// of root CAs in a format suitable for this function.
func (s *CertPool) AppendCertsFromPEM(pemCerts []byte) (ok bool) {
for len(pemCerts) > 0 {

View File

@ -0,0 +1,42 @@
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package x509
import (
"crypto/x509/pkix"
"encoding/asn1"
"errors"
"fmt"
)
// pkcs8 reflects an ASN.1, PKCS#8 PrivateKey. See
// ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-8/pkcs-8v1_2.asn.
type pkcs8 struct {
Version int
Algo pkix.AlgorithmIdentifier
PrivateKey []byte
// optional attributes omitted.
}
// ParsePKCS8PrivateKey parses an unencrypted, PKCS#8 private key. See
// http://www.rsa.com/rsalabs/node.asp?id=2130
func ParsePKCS8PrivateKey(der []byte) (key interface{}, err error) {
var privKey pkcs8
if _, err := asn1.Unmarshal(der, &privKey); err != nil {
return nil, err
}
switch {
case privKey.Algo.Algorithm.Equal(oidRSA):
key, err = ParsePKCS1PrivateKey(privKey.PrivateKey)
if err != nil {
return nil, errors.New("crypto/x509: failed to parse RSA private key embedded in PKCS#8: " + err.Error())
}
return key, nil
default:
return nil, fmt.Errorf("crypto/x509: PKCS#8 wrapping contained private key with unknown algorithm: %v", privKey.Algo.Algorithm)
}
panic("unreachable")
}

View File

@ -0,0 +1,20 @@
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package x509
import (
"encoding/hex"
"testing"
)
var pkcs8PrivateKeyHex = `30820278020100300d06092a864886f70d0101010500048202623082025e02010002818100cfb1b5bf9685ffa97b4f99df4ff122b70e59ac9b992f3bc2b3dde17d53c1a34928719b02e8fd17839499bfbd515bd6ef99c7a1c47a239718fe36bfd824c0d96060084b5f67f0273443007a24dfaf5634f7772c9346e10eb294c2306671a5a5e719ae24b4de467291bc571014b0e02dec04534d66a9bb171d644b66b091780e8d020301000102818100b595778383c4afdbab95d2bfed12b3f93bb0a73a7ad952f44d7185fd9ec6c34de8f03a48770f2009c8580bcd275e9632714e9a5e3f32f29dc55474b2329ff0ebc08b3ffcb35bc96e6516b483df80a4a59cceb71918cbabf91564e64a39d7e35dce21cb3031824fdbc845dba6458852ec16af5dddf51a8397a8797ae0337b1439024100ea0eb1b914158c70db39031dd8904d6f18f408c85fbbc592d7d20dee7986969efbda081fdf8bc40e1b1336d6b638110c836bfdc3f314560d2e49cd4fbde1e20b024100e32a4e793b574c9c4a94c8803db5152141e72d03de64e54ef2c8ed104988ca780cd11397bc359630d01b97ebd87067c5451ba777cf045ca23f5912f1031308c702406dfcdbbd5a57c9f85abc4edf9e9e29153507b07ce0a7ef6f52e60dcfebe1b8341babd8b789a837485da6c8d55b29bbb142ace3c24a1f5b54b454d01b51e2ad03024100bd6a2b60dee01e1b3bfcef6a2f09ed027c273cdbbaf6ba55a80f6dcc64e4509ee560f84b4f3e076bd03b11e42fe71a3fdd2dffe7e0902c8584f8cad877cdc945024100aa512fa4ada69881f1d8bb8ad6614f192b83200aef5edf4811313d5ef30a86cbd0a90f7b025c71ea06ec6b34db6306c86b1040670fd8654ad7291d066d06d031`
func TestPKCS8(t *testing.T) {
derBytes, _ := hex.DecodeString(pkcs8PrivateKeyHex)
_, err := ParsePKCS8PrivateKey(derBytes)
if err != nil {
t.Errorf("failed to decode PKCS8 key: %s", err)
}
}

View File

@ -142,10 +142,9 @@ type CertificateList struct {
SignatureValue asn1.BitString
}
// HasExpired returns true iff currentTimeSeconds is past the expiry time of
// certList.
func (certList *CertificateList) HasExpired(currentTimeSeconds int64) bool {
return certList.TBSCertList.NextUpdate.Seconds() <= currentTimeSeconds
// HasExpired returns true iff now is past the expiry time of certList.
func (certList *CertificateList) HasExpired(now time.Time) bool {
return now.After(certList.TBSCertList.NextUpdate)
}
// TBSCertificateList represents the ASN.1 structure of the same name. See RFC
@ -155,8 +154,8 @@ type TBSCertificateList struct {
Version int `asn1:"optional,default:2"`
Signature AlgorithmIdentifier
Issuer RDNSequence
ThisUpdate *time.Time
NextUpdate *time.Time
ThisUpdate time.Time
NextUpdate time.Time
RevokedCertificates []RevokedCertificate `asn1:"optional"`
Extensions []Extension `asn1:"tag:0,optional,explicit"`
}
@ -165,6 +164,6 @@ type TBSCertificateList struct {
// 5280, section 5.1.
type RevokedCertificate struct {
SerialNumber *big.Int
RevocationTime *time.Time
RevocationTime time.Time
Extensions []Extension `asn1:"optional"`
}

View File

@ -76,7 +76,7 @@ type VerifyOptions struct {
DNSName string
Intermediates *CertPool
Roots *CertPool
CurrentTime int64 // if 0, the current system time is used.
CurrentTime time.Time // if zero, the current time is used
}
const (
@ -87,8 +87,11 @@ const (
// isValid performs validity checks on the c.
func (c *Certificate) isValid(certType int, opts *VerifyOptions) error {
if opts.CurrentTime < c.NotBefore.Seconds() ||
opts.CurrentTime > c.NotAfter.Seconds() {
now := opts.CurrentTime
if now.IsZero() {
now = time.Now()
}
if now.Before(c.NotBefore) || now.After(c.NotAfter) {
return CertificateInvalidError{c, Expired}
}
@ -136,9 +139,6 @@ func (c *Certificate) isValid(certType int, opts *VerifyOptions) error {
//
// WARNING: this doesn't do any revocation checking.
func (c *Certificate) Verify(opts VerifyOptions) (chains [][]*Certificate, err error) {
if opts.CurrentTime == 0 {
opts.CurrentTime = time.Seconds()
}
err = c.isValid(leafCertificate, &opts)
if err != nil {
return

View File

@ -10,6 +10,7 @@ import (
"errors"
"strings"
"testing"
"time"
)
type verifyTest struct {
@ -133,7 +134,7 @@ func TestVerify(t *testing.T) {
Roots: NewCertPool(),
Intermediates: NewCertPool(),
DNSName: test.dnsName,
CurrentTime: test.currentTime,
CurrentTime: time.Unix(test.currentTime, 0),
}
for j, root := range test.roots {

View File

@ -107,7 +107,7 @@ type dsaSignature struct {
}
type validity struct {
NotBefore, NotAfter *time.Time
NotBefore, NotAfter time.Time
}
type publicKeyInfo struct {
@ -303,7 +303,7 @@ type Certificate struct {
SerialNumber *big.Int
Issuer pkix.Name
Subject pkix.Name
NotBefore, NotAfter *time.Time // Validity bounds.
NotBefore, NotAfter time.Time // Validity bounds.
KeyUsage KeyUsage
ExtKeyUsage []ExtKeyUsage // Sequence of extended key usages.
@ -398,7 +398,7 @@ func (c *Certificate) CheckSignature(algo SignatureAlgorithm, signed, signature
}
h.Write(signed)
digest := h.Sum()
digest := h.Sum(nil)
switch pub := c.PublicKey.(type) {
case *rsa.PublicKey:
@ -899,11 +899,10 @@ var (
oidRSA = []int{1, 2, 840, 113549, 1, 1, 1}
)
// CreateSelfSignedCertificate creates a new certificate based on
// a template. The following members of template are used: SerialNumber,
// Subject, NotBefore, NotAfter, KeyUsage, BasicConstraintsValid, IsCA,
// MaxPathLen, SubjectKeyId, DNSNames, PermittedDNSDomainsCritical,
// PermittedDNSDomains.
// CreateCertificate creates a new certificate based on a template. The
// following members of template are used: SerialNumber, Subject, NotBefore,
// NotAfter, KeyUsage, BasicConstraintsValid, IsCA, MaxPathLen, SubjectKeyId,
// DNSNames, PermittedDNSDomainsCritical, PermittedDNSDomains.
//
// The certificate is signed by parent. If parent is equal to template then the
// certificate is self-signed. The parameter pub is the public key of the
@ -958,7 +957,7 @@ func CreateCertificate(rand io.Reader, template, parent *Certificate, pub *rsa.P
h := sha1.New()
h.Write(tbsCertContents)
digest := h.Sum()
digest := h.Sum(nil)
signature, err := rsa.SignPKCS1v15(rand, priv, crypto.SHA1, digest)
if err != nil {
@ -1006,7 +1005,7 @@ func ParseDERCRL(derBytes []byte) (certList *pkix.CertificateList, err error) {
// CreateCRL returns a DER encoded CRL, signed by this Certificate, that
// contains the given list of revoked certificates.
func (c *Certificate) CreateCRL(rand io.Reader, priv *rsa.PrivateKey, revokedCerts []pkix.RevokedCertificate, now, expiry *time.Time) (crlBytes []byte, err error) {
func (c *Certificate) CreateCRL(rand io.Reader, priv *rsa.PrivateKey, revokedCerts []pkix.RevokedCertificate, now, expiry time.Time) (crlBytes []byte, err error) {
tbsCertList := pkix.TBSCertificateList{
Version: 2,
Signature: pkix.AlgorithmIdentifier{
@ -1025,7 +1024,7 @@ func (c *Certificate) CreateCRL(rand io.Reader, priv *rsa.PrivateKey, revokedCer
h := sha1.New()
h.Write(tbsCertListContents)
digest := h.Sum()
digest := h.Sum(nil)
signature, err := rsa.SignPKCS1v15(rand, priv, crypto.SHA1, digest)
if err != nil {

View File

@ -250,8 +250,8 @@ func TestCreateSelfSignedCertificate(t *testing.T) {
CommonName: commonName,
Organization: []string{"Acme Co"},
},
NotBefore: time.SecondsToUTC(1000),
NotAfter: time.SecondsToUTC(100000),
NotBefore: time.Unix(1000, 0),
NotAfter: time.Unix(100000, 0),
SubjectKeyId: []byte{1, 2, 3, 4},
KeyUsage: KeyUsageCertSign,
@ -396,8 +396,8 @@ func TestCRLCreation(t *testing.T) {
block, _ = pem.Decode([]byte(pemCertificate))
cert, _ := ParseCertificate(block.Bytes)
now := time.SecondsToUTC(1000)
expiry := time.SecondsToUTC(10000)
now := time.Unix(1000, 0)
expiry := time.Unix(10000, 0)
revokedCerts := []pkix.RevokedCertificate{
{
@ -443,7 +443,7 @@ func TestParseDERCRL(t *testing.T) {
t.Errorf("bad number of revoked certificates. got: %d want: %d", numCerts, expected)
}
if certList.HasExpired(1302517272) {
if certList.HasExpired(time.Unix(1302517272, 0)) {
t.Errorf("CRL has expired (but shouldn't have)")
}
@ -463,7 +463,7 @@ func TestParsePEMCRL(t *testing.T) {
t.Errorf("bad number of revoked certificates. got: %d want: %d", numCerts, expected)
}
if certList.HasExpired(1302517272) {
if certList.HasExpired(time.Unix(1302517272, 0)) {
t.Errorf("CRL has expired (but shouldn't have)")
}

View File

@ -247,7 +247,7 @@ func parseBase128Int(bytes []byte, initOffset int) (ret, offset int, err error)
// UTCTime
func parseUTCTime(bytes []byte) (ret *time.Time, err error) {
func parseUTCTime(bytes []byte) (ret time.Time, err error) {
s := string(bytes)
ret, err = time.Parse("0601021504Z0700", s)
if err == nil {
@ -259,7 +259,7 @@ func parseUTCTime(bytes []byte) (ret *time.Time, err error) {
// parseGeneralizedTime parses the GeneralizedTime from the given byte slice
// and returns the resulting time.
func parseGeneralizedTime(bytes []byte) (ret *time.Time, err error) {
func parseGeneralizedTime(bytes []byte) (ret time.Time, err error) {
return time.Parse("20060102150405Z0700", string(bytes))
}
@ -450,7 +450,7 @@ var (
objectIdentifierType = reflect.TypeOf(ObjectIdentifier{})
enumeratedType = reflect.TypeOf(Enumerated(0))
flagType = reflect.TypeOf(Flag(false))
timeType = reflect.TypeOf(&time.Time{})
timeType = reflect.TypeOf(time.Time{})
rawValueType = reflect.TypeOf(RawValue{})
rawContentsType = reflect.TypeOf(RawContent(nil))
bigIntType = reflect.TypeOf(new(big.Int))
@ -647,7 +647,7 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam
err = err1
return
case timeType:
var time *time.Time
var time time.Time
var err1 error
if universalTag == tagUTCTime {
time, err1 = parseUTCTime(innerBytes)
@ -799,7 +799,7 @@ func setDefaultValue(v reflect.Value, params fieldParameters) (ok bool) {
//
// An ASN.1 ENUMERATED can be written to an Enumerated.
//
// An ASN.1 UTCTIME or GENERALIZEDTIME can be written to a *time.Time.
// An ASN.1 UTCTIME or GENERALIZEDTIME can be written to a time.Time.
//
// An ASN.1 PrintableString or IA5String can be written to a string.
//

View File

@ -202,43 +202,51 @@ func TestObjectIdentifier(t *testing.T) {
type timeTest struct {
in string
ok bool
out *time.Time
out time.Time
}
var utcTestData = []timeTest{
{"910506164540-0700", true, &time.Time{1991, 05, 06, 16, 45, 40, 0, -7 * 60 * 60, ""}},
{"910506164540+0730", true, &time.Time{1991, 05, 06, 16, 45, 40, 0, 7*60*60 + 30*60, ""}},
{"910506234540Z", true, &time.Time{1991, 05, 06, 23, 45, 40, 0, 0, "UTC"}},
{"9105062345Z", true, &time.Time{1991, 05, 06, 23, 45, 0, 0, 0, "UTC"}},
{"a10506234540Z", false, nil},
{"91a506234540Z", false, nil},
{"9105a6234540Z", false, nil},
{"910506a34540Z", false, nil},
{"910506334a40Z", false, nil},
{"91050633444aZ", false, nil},
{"910506334461Z", false, nil},
{"910506334400Za", false, nil},
{"910506164540-0700", true, time.Date(1991, 05, 06, 16, 45, 40, 0, time.FixedZone("", -7*60*60))},
{"910506164540+0730", true, time.Date(1991, 05, 06, 16, 45, 40, 0, time.FixedZone("", 7*60*60+30*60))},
{"910506234540Z", true, time.Date(1991, 05, 06, 23, 45, 40, 0, time.UTC)},
{"9105062345Z", true, time.Date(1991, 05, 06, 23, 45, 0, 0, time.UTC)},
{"a10506234540Z", false, time.Time{}},
{"91a506234540Z", false, time.Time{}},
{"9105a6234540Z", false, time.Time{}},
{"910506a34540Z", false, time.Time{}},
{"910506334a40Z", false, time.Time{}},
{"91050633444aZ", false, time.Time{}},
{"910506334461Z", false, time.Time{}},
{"910506334400Za", false, time.Time{}},
}
func TestUTCTime(t *testing.T) {
for i, test := range utcTestData {
ret, err := parseUTCTime([]byte(test.in))
if (err == nil) != test.ok {
t.Errorf("#%d: Incorrect error result (did fail? %v, expected: %v)", i, err == nil, test.ok)
}
if err == nil {
if !reflect.DeepEqual(test.out, ret) {
t.Errorf("#%d: Bad result: %v (expected %v)", i, ret, test.out)
if err != nil {
if test.ok {
t.Errorf("#%d: parseUTCTime(%q) = error %v", i, err)
}
continue
}
if !test.ok {
t.Errorf("#%d: parseUTCTime(%q) succeeded, should have failed", i)
continue
}
const format = "Jan _2 15:04:05 -0700 2006" // ignore zone name, just offset
have := ret.Format(format)
want := test.out.Format(format)
if have != want {
t.Errorf("#%d: parseUTCTime(%q) = %s, want %s", test.in, have, want)
}
}
}
var generalizedTimeTestData = []timeTest{
{"20100102030405Z", true, &time.Time{2010, 01, 02, 03, 04, 05, 0, 0, "UTC"}},
{"20100102030405", false, nil},
{"20100102030405+0607", true, &time.Time{2010, 01, 02, 03, 04, 05, 0, 6*60*60 + 7*60, ""}},
{"20100102030405-0607", true, &time.Time{2010, 01, 02, 03, 04, 05, 0, -6*60*60 - 7*60, ""}},
{"20100102030405Z", true, time.Date(2010, 01, 02, 03, 04, 05, 0, time.UTC)},
{"20100102030405", false, time.Time{}},
{"20100102030405+0607", true, time.Date(2010, 01, 02, 03, 04, 05, 0, time.FixedZone("", 6*60*60+7*60))},
{"20100102030405-0607", true, time.Date(2010, 01, 02, 03, 04, 05, 0, time.FixedZone("", -6*60*60-7*60))},
}
func TestGeneralizedTime(t *testing.T) {
@ -407,7 +415,7 @@ type AttributeTypeAndValue struct {
}
type Validity struct {
NotBefore, NotAfter *time.Time
NotBefore, NotAfter time.Time
}
type PublicKeyInfo struct {
@ -475,7 +483,10 @@ var derEncodedSelfSignedCert = Certificate{
RelativeDistinguishedNameSET{AttributeTypeAndValue{Type: ObjectIdentifier{2, 5, 4, 3}, Value: "false.example.com"}},
RelativeDistinguishedNameSET{AttributeTypeAndValue{Type: ObjectIdentifier{1, 2, 840, 113549, 1, 9, 1}, Value: "false@example.com"}},
},
Validity: Validity{NotBefore: &time.Time{Year: 2009, Month: 10, Day: 8, Hour: 0, Minute: 25, Second: 53, ZoneOffset: 0, Zone: "UTC"}, NotAfter: &time.Time{Year: 2010, Month: 10, Day: 8, Hour: 0, Minute: 25, Second: 53, ZoneOffset: 0, Zone: "UTC"}},
Validity: Validity{
NotBefore: time.Date(2009, 10, 8, 00, 25, 53, 0, time.UTC),
NotAfter: time.Date(2010, 10, 8, 00, 25, 53, 0, time.UTC),
},
Subject: RDNSequence{
RelativeDistinguishedNameSET{AttributeTypeAndValue{Type: ObjectIdentifier{2, 5, 4, 6}, Value: "XX"}},
RelativeDistinguishedNameSET{AttributeTypeAndValue{Type: ObjectIdentifier{2, 5, 4, 8}, Value: "Some-State"}},

View File

@ -288,52 +288,58 @@ func marshalTwoDigits(out *forkableWriter, v int) (err error) {
return out.WriteByte(byte('0' + v%10))
}
func marshalUTCTime(out *forkableWriter, t *time.Time) (err error) {
func marshalUTCTime(out *forkableWriter, t time.Time) (err error) {
utc := t.UTC()
year, month, day := utc.Date()
switch {
case 1950 <= t.Year && t.Year < 2000:
err = marshalTwoDigits(out, int(t.Year-1900))
case 2000 <= t.Year && t.Year < 2050:
err = marshalTwoDigits(out, int(t.Year-2000))
case 1950 <= year && year < 2000:
err = marshalTwoDigits(out, int(year-1900))
case 2000 <= year && year < 2050:
err = marshalTwoDigits(out, int(year-2000))
default:
return StructuralError{"Cannot represent time as UTCTime"}
}
if err != nil {
return
}
err = marshalTwoDigits(out, t.Month)
err = marshalTwoDigits(out, int(month))
if err != nil {
return
}
err = marshalTwoDigits(out, t.Day)
err = marshalTwoDigits(out, day)
if err != nil {
return
}
err = marshalTwoDigits(out, t.Hour)
hour, min, sec := utc.Clock()
err = marshalTwoDigits(out, hour)
if err != nil {
return
}
err = marshalTwoDigits(out, t.Minute)
err = marshalTwoDigits(out, min)
if err != nil {
return
}
err = marshalTwoDigits(out, t.Second)
err = marshalTwoDigits(out, sec)
if err != nil {
return
}
_, offset := t.Zone()
switch {
case t.ZoneOffset/60 == 0:
case offset/60 == 0:
err = out.WriteByte('Z')
return
case t.ZoneOffset > 0:
case offset > 0:
err = out.WriteByte('+')
case t.ZoneOffset < 0:
case offset < 0:
err = out.WriteByte('-')
}
@ -341,7 +347,7 @@ func marshalUTCTime(out *forkableWriter, t *time.Time) (err error) {
return
}
offsetMinutes := t.ZoneOffset / 60
offsetMinutes := offset / 60
if offsetMinutes < 0 {
offsetMinutes = -offsetMinutes
}
@ -366,7 +372,7 @@ func stripTagAndLength(in []byte) []byte {
func marshalBody(out *forkableWriter, value reflect.Value, params fieldParameters) (err error) {
switch value.Type() {
case timeType:
return marshalUTCTime(out, value.Interface().(*time.Time))
return marshalUTCTime(out, value.Interface().(time.Time))
case bitStringType:
return marshalBitString(out, value.Interface().(BitString))
case objectIdentifierType:

View File

@ -51,10 +51,7 @@ type optionalRawValueTest struct {
type testSET []int
func setPST(t *time.Time) *time.Time {
t.ZoneOffset = -28800
return t
}
var PST = time.FixedZone("PST", -8*60*60)
type marshalTest struct {
in interface{}
@ -73,9 +70,9 @@ var marshalTests = []marshalTest{
{[]byte{1, 2, 3}, "0403010203"},
{implicitTagTest{64}, "3003850140"},
{explicitTagTest{64}, "3005a503020140"},
{time.SecondsToUTC(0), "170d3730303130313030303030305a"},
{time.SecondsToUTC(1258325776), "170d3039313131353232353631365a"},
{setPST(time.SecondsToUTC(1258325776)), "17113039313131353232353631362d30383030"},
{time.Unix(0, 0).UTC(), "170d3730303130313030303030305a"},
{time.Unix(1258325776, 0).UTC(), "170d3039313131353232353631365a"},
{time.Unix(1258325776, 0).In(PST), "17113039313131353232353631362d30383030"},
{BitString{[]byte{0x80}, 1}, "03020780"},
{BitString{[]byte{0x81, 0xf0}, 12}, "03030481f0"},
{ObjectIdentifier([]int{1, 2, 3, 4}), "06032a0304"},
@ -123,7 +120,8 @@ func TestMarshal(t *testing.T) {
}
out, _ := hex.DecodeString(test.out)
if bytes.Compare(out, data) != 0 {
t.Errorf("#%d got: %x want %x", i, data, out)
t.Errorf("#%d got: %x want %x\n\t%q\n\t%q", i, data, out, data, out)
}
}
}

View File

@ -16,6 +16,7 @@ import (
"runtime"
"sort"
"strconv"
"sync"
"unicode"
"unicode/utf8"
)
@ -295,28 +296,10 @@ func (e *encodeState) reflectValueQuoted(v reflect.Value, quoted bool) {
case reflect.Struct:
e.WriteByte('{')
t := v.Type()
n := v.NumField()
first := true
for i := 0; i < n; i++ {
f := t.Field(i)
if f.PkgPath != "" {
continue
}
tag, omitEmpty, quoted := f.Name, false, false
if tv := f.Tag.Get("json"); tv != "" {
if tv == "-" {
continue
}
name, opts := parseTag(tv)
if isValidTag(name) {
tag = name
}
omitEmpty = opts.Contains("omitempty")
quoted = opts.Contains("string")
}
fieldValue := v.Field(i)
if omitEmpty && isEmptyValue(fieldValue) {
for _, ef := range encodeFields(v.Type()) {
fieldValue := v.Field(ef.i)
if ef.omitEmpty && isEmptyValue(fieldValue) {
continue
}
if first {
@ -324,9 +307,9 @@ func (e *encodeState) reflectValueQuoted(v reflect.Value, quoted bool) {
} else {
e.WriteByte(',')
}
e.string(tag)
e.string(ef.tag)
e.WriteByte(':')
e.reflectValueQuoted(fieldValue, quoted)
e.reflectValueQuoted(fieldValue, ef.quoted)
}
e.WriteByte('}')
@ -470,3 +453,63 @@ func (e *encodeState) string(s string) (int, error) {
e.WriteByte('"')
return e.Len() - len0, nil
}
// encodeField contains information about how to encode a field of a
// struct.
type encodeField struct {
i int // field index in struct
tag string
quoted bool
omitEmpty bool
}
var (
typeCacheLock sync.RWMutex
encodeFieldsCache = make(map[reflect.Type][]encodeField)
)
// encodeFields returns a slice of encodeField for a given
// struct type.
func encodeFields(t reflect.Type) []encodeField {
typeCacheLock.RLock()
fs, ok := encodeFieldsCache[t]
typeCacheLock.RUnlock()
if ok {
return fs
}
typeCacheLock.Lock()
defer typeCacheLock.Unlock()
fs, ok = encodeFieldsCache[t]
if ok {
return fs
}
v := reflect.Zero(t)
n := v.NumField()
for i := 0; i < n; i++ {
f := t.Field(i)
if f.PkgPath != "" {
continue
}
var ef encodeField
ef.i = i
ef.tag = f.Name
tv := f.Tag.Get("json")
if tv != "" {
if tv == "-" {
continue
}
name, opts := parseTag(tv)
if isValidTag(name) {
ef.tag = name
}
ef.omitEmpty = opts.Contains("omitempty")
ef.quoted = opts.Contains("string")
}
fs = append(fs, ef)
}
encodeFieldsCache[t] = fs
return fs
}

View File

@ -61,7 +61,7 @@ type StartElement struct {
func (e StartElement) Copy() StartElement {
attrs := make([]Attr, len(e.Attr))
copy(e.Attr, attrs)
copy(attrs, e.Attr)
e.Attr = attrs
return e
}

View File

@ -29,71 +29,69 @@ const testInput = `
</body><!-- missing final newline -->`
var rawTokens = []Token{
CharData([]byte("\n")),
CharData("\n"),
ProcInst{"xml", []byte(`version="1.0" encoding="UTF-8"`)},
CharData([]byte("\n")),
Directive([]byte(`DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
CharData("\n"),
Directive(`DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"`),
),
CharData([]byte("\n")),
CharData("\n"),
StartElement{Name{"", "body"}, []Attr{{Name{"xmlns", "foo"}, "ns1"}, {Name{"", "xmlns"}, "ns2"}, {Name{"xmlns", "tag"}, "ns3"}}},
CharData([]byte("\n ")),
CharData("\n "),
StartElement{Name{"", "hello"}, []Attr{{Name{"", "lang"}, "en"}}},
CharData([]byte("World <>'\" 白鵬翔")),
CharData("World <>'\" 白鵬翔"),
EndElement{Name{"", "hello"}},
CharData([]byte("\n ")),
CharData("\n "),
StartElement{Name{"", "goodbye"}, []Attr{}},
EndElement{Name{"", "goodbye"}},
CharData([]byte("\n ")),
CharData("\n "),
StartElement{Name{"", "outer"}, []Attr{{Name{"foo", "attr"}, "value"}, {Name{"xmlns", "tag"}, "ns4"}}},
CharData([]byte("\n ")),
CharData("\n "),
StartElement{Name{"", "inner"}, []Attr{}},
EndElement{Name{"", "inner"}},
CharData([]byte("\n ")),
CharData("\n "),
EndElement{Name{"", "outer"}},
CharData([]byte("\n ")),
CharData("\n "),
StartElement{Name{"tag", "name"}, []Attr{}},
CharData([]byte("\n ")),
CharData([]byte("Some text here.")),
CharData([]byte("\n ")),
CharData("\n "),
CharData("Some text here."),
CharData("\n "),
EndElement{Name{"tag", "name"}},
CharData([]byte("\n")),
CharData("\n"),
EndElement{Name{"", "body"}},
Comment([]byte(" missing final newline ")),
Comment(" missing final newline "),
}
var cookedTokens = []Token{
CharData([]byte("\n")),
CharData("\n"),
ProcInst{"xml", []byte(`version="1.0" encoding="UTF-8"`)},
CharData([]byte("\n")),
Directive([]byte(`DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
CharData("\n"),
Directive(`DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"`),
),
CharData([]byte("\n")),
CharData("\n"),
StartElement{Name{"ns2", "body"}, []Attr{{Name{"xmlns", "foo"}, "ns1"}, {Name{"", "xmlns"}, "ns2"}, {Name{"xmlns", "tag"}, "ns3"}}},
CharData([]byte("\n ")),
CharData("\n "),
StartElement{Name{"ns2", "hello"}, []Attr{{Name{"", "lang"}, "en"}}},
CharData([]byte("World <>'\" 白鵬翔")),
CharData("World <>'\" 白鵬翔"),
EndElement{Name{"ns2", "hello"}},
CharData([]byte("\n ")),
CharData("\n "),
StartElement{Name{"ns2", "goodbye"}, []Attr{}},
EndElement{Name{"ns2", "goodbye"}},
CharData([]byte("\n ")),
CharData("\n "),
StartElement{Name{"ns2", "outer"}, []Attr{{Name{"ns1", "attr"}, "value"}, {Name{"xmlns", "tag"}, "ns4"}}},
CharData([]byte("\n ")),
CharData("\n "),
StartElement{Name{"ns2", "inner"}, []Attr{}},
EndElement{Name{"ns2", "inner"}},
CharData([]byte("\n ")),
CharData("\n "),
EndElement{Name{"ns2", "outer"}},
CharData([]byte("\n ")),
CharData("\n "),
StartElement{Name{"ns3", "name"}, []Attr{}},
CharData([]byte("\n ")),
CharData([]byte("Some text here.")),
CharData([]byte("\n ")),
CharData("\n "),
CharData("Some text here."),
CharData("\n "),
EndElement{Name{"ns3", "name"}},
CharData([]byte("\n")),
CharData("\n"),
EndElement{Name{"ns2", "body"}},
Comment([]byte(" missing final newline ")),
Comment(" missing final newline "),
}
const testInputAltEncoding = `
@ -101,11 +99,11 @@ const testInputAltEncoding = `
<TAG>VALUE</TAG>`
var rawTokensAltEncoding = []Token{
CharData([]byte("\n")),
CharData("\n"),
ProcInst{"xml", []byte(`version="1.0" encoding="x-testing-uppercase"`)},
CharData([]byte("\n")),
CharData("\n"),
StartElement{Name{"", "tag"}, []Attr{}},
CharData([]byte("value")),
CharData("value"),
EndElement{Name{"", "tag"}},
}
@ -270,21 +268,21 @@ var nestedDirectivesInput = `
`
var nestedDirectivesTokens = []Token{
CharData([]byte("\n")),
Directive([]byte(`DOCTYPE [<!ENTITY rdf "http://www.w3.org/1999/02/22-rdf-syntax-ns#">]`)),
CharData([]byte("\n")),
Directive([]byte(`DOCTYPE [<!ENTITY xlt ">">]`)),
CharData([]byte("\n")),
Directive([]byte(`DOCTYPE [<!ENTITY xlt "<">]`)),
CharData([]byte("\n")),
Directive([]byte(`DOCTYPE [<!ENTITY xlt '>'>]`)),
CharData([]byte("\n")),
Directive([]byte(`DOCTYPE [<!ENTITY xlt '<'>]`)),
CharData([]byte("\n")),
Directive([]byte(`DOCTYPE [<!ENTITY xlt '">'>]`)),
CharData([]byte("\n")),
Directive([]byte(`DOCTYPE [<!ENTITY xlt "'<">]`)),
CharData([]byte("\n")),
CharData("\n"),
Directive(`DOCTYPE [<!ENTITY rdf "http://www.w3.org/1999/02/22-rdf-syntax-ns#">]`),
CharData("\n"),
Directive(`DOCTYPE [<!ENTITY xlt ">">]`),
CharData("\n"),
Directive(`DOCTYPE [<!ENTITY xlt "<">]`),
CharData("\n"),
Directive(`DOCTYPE [<!ENTITY xlt '>'>]`),
CharData("\n"),
Directive(`DOCTYPE [<!ENTITY xlt '<'>]`),
CharData("\n"),
Directive(`DOCTYPE [<!ENTITY xlt '">'>]`),
CharData("\n"),
Directive(`DOCTYPE [<!ENTITY xlt "'<">]`),
CharData("\n"),
}
func TestNestedDirectives(t *testing.T) {
@ -488,10 +486,13 @@ func TestCopyTokenStartElement(t *testing.T) {
elt := StartElement{Name{"", "hello"}, []Attr{{Name{"", "lang"}, "en"}}}
var tok1 Token = elt
tok2 := CopyToken(tok1)
if tok1.(StartElement).Attr[0].Value != "en" {
t.Error("CopyToken overwrote Attr[0]")
}
if !reflect.DeepEqual(tok1, tok2) {
t.Error("CopyToken(StartElement) != StartElement")
}
elt.Attr[0] = Attr{Name{"", "lang"}, "de"}
tok1.(StartElement).Attr[0] = Attr{Name{"", "lang"}, "de"}
if reflect.DeepEqual(tok1, tok2) {
t.Error("CopyToken(CharData) uses same buffer.")
}

View File

@ -150,15 +150,15 @@ func processFiles(filenames []string, allFiles bool) {
switch info, err := os.Stat(filename); {
case err != nil:
report(err)
case info.IsRegular():
if allFiles || isGoFilename(info.Name) {
filenames[i] = filename
i++
}
case info.IsDirectory():
case info.IsDir():
if allFiles || *recursive {
processDirectory(filename)
}
default:
if allFiles || isGoFilename(info.Name()) {
filenames[i] = filename
i++
}
}
}
fset := token.NewFileSet()

View File

@ -1,57 +0,0 @@
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package gui defines a basic graphical user interface programming model.
package gui
import (
"image"
"image/draw"
)
// A Window represents a single graphics window.
type Window interface {
// Screen returns an editable Image for the window.
Screen() draw.Image
// FlushImage flushes changes made to Screen() back to screen.
FlushImage()
// EventChan returns a channel carrying UI events such as key presses,
// mouse movements and window resizes.
EventChan() <-chan interface{}
// Close closes the window.
Close() error
}
// A KeyEvent is sent for a key press or release.
type KeyEvent struct {
// The value k represents key k being pressed.
// The value -k represents key k being released.
// The specific set of key values is not specified,
// but ordinary characters represent themselves.
Key int
}
// A MouseEvent is sent for a button press or release or for a mouse movement.
type MouseEvent struct {
// Buttons is a bit mask of buttons: 1<<0 is left, 1<<1 middle, 1<<2 right.
// It represents button state and not necessarily the state delta: bit 0
// being on means that the left mouse button is down, but does not imply
// that the same button was up in the previous MouseEvent.
Buttons int
// Loc is the location of the cursor.
Loc image.Point
// Nsec is the event's timestamp.
Nsec int64
}
// A ConfigEvent is sent each time the window's color model or size changes.
// The client should respond by calling Window.Screen to obtain a new image.
type ConfigEvent struct {
Config image.Config
}
// An ErrEvent is sent when an error occurs.
type ErrEvent struct {
Err error
}

View File

@ -1,96 +0,0 @@
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package x11
import (
"bufio"
"errors"
"io"
"os"
)
// readU16BE reads a big-endian uint16 from r, using b as a scratch buffer.
func readU16BE(r io.Reader, b []byte) (uint16, error) {
_, err := io.ReadFull(r, b[0:2])
if err != nil {
return 0, err
}
return uint16(b[0])<<8 + uint16(b[1]), nil
}
// readStr reads a length-prefixed string from r, using b as a scratch buffer.
func readStr(r io.Reader, b []byte) (string, error) {
n, err := readU16BE(r, b)
if err != nil {
return "", err
}
if int(n) > len(b) {
return "", errors.New("Xauthority entry too long for buffer")
}
_, err = io.ReadFull(r, b[0:n])
if err != nil {
return "", err
}
return string(b[0:n]), nil
}
// readAuth reads the X authority file and returns the name/data pair for the display.
// displayStr is the "12" out of a $DISPLAY like ":12.0".
func readAuth(displayStr string) (name, data string, err error) {
// b is a scratch buffer to use and should be at least 256 bytes long
// (i.e. it should be able to hold a hostname).
var b [256]byte
// As per /usr/include/X11/Xauth.h.
const familyLocal = 256
fn := os.Getenv("XAUTHORITY")
if fn == "" {
home := os.Getenv("HOME")
if home == "" {
err = errors.New("Xauthority not found: $XAUTHORITY, $HOME not set")
return
}
fn = home + "/.Xauthority"
}
r, err := os.Open(fn)
if err != nil {
return
}
defer r.Close()
br := bufio.NewReader(r)
hostname, err := os.Hostname()
if err != nil {
return
}
for {
var family uint16
var addr, disp, name0, data0 string
family, err = readU16BE(br, b[0:2])
if err != nil {
return
}
addr, err = readStr(br, b[0:])
if err != nil {
return
}
disp, err = readStr(br, b[0:])
if err != nil {
return
}
name0, err = readStr(br, b[0:])
if err != nil {
return
}
data0, err = readStr(br, b[0:])
if err != nil {
return
}
if family == familyLocal && addr == hostname && disp == displayStr {
return name0, data0, nil
}
}
panic("unreachable")
}

View File

@ -1,631 +0,0 @@
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package x11 implements an X11 backend for the exp/gui package.
//
// The X protocol specification is at ftp://ftp.x.org/pub/X11R7.0/doc/PDF/proto.pdf.
// A summary of the wire format can be found in XCB's xproto.xml.
package x11
import (
"bufio"
"errors"
"exp/gui"
"image"
"image/draw"
"io"
"log"
"net"
"os"
"strconv"
"strings"
"time"
)
type resID uint32 // X resource IDs.
// TODO(nigeltao): Handle window resizes.
const (
windowHeight = 600
windowWidth = 800
)
const (
keymapLo = 8
keymapHi = 255
)
type conn struct {
c io.Closer
r *bufio.Reader
w *bufio.Writer
gc, window, root, visual resID
img *image.RGBA
eventc chan interface{}
mouseState gui.MouseEvent
buf [256]byte // General purpose scratch buffer.
flush chan bool
flushBuf0 [24]byte
flushBuf1 [4 * 1024]byte
}
// writeSocket runs in its own goroutine, serving both FlushImage calls
// directly from the exp/gui client and indirectly from X expose events.
// It paints c.img to the X server via PutImage requests.
func (c *conn) writeSocket() {
defer c.c.Close()
for _ = range c.flush {
b := c.img.Bounds()
if b.Empty() {
continue
}
// Each X request has a 16-bit length (in terms of 4-byte units). To avoid going over
// this limit, we send PutImage for each row of the image, rather than trying to paint
// the entire image in one X request. This approach could easily be optimized (or the
// X protocol may have an escape sequence to delimit very large requests).
// TODO(nigeltao): See what XCB's xcb_put_image does in this situation.
units := 6 + b.Dx()
if units > 0xffff || b.Dy() > 0xffff {
log.Print("x11: window is too large for PutImage")
return
}
c.flushBuf0[0] = 0x48 // PutImage opcode.
c.flushBuf0[1] = 0x02 // XCB_IMAGE_FORMAT_Z_PIXMAP.
c.flushBuf0[2] = uint8(units)
c.flushBuf0[3] = uint8(units >> 8)
setU32LE(c.flushBuf0[4:8], uint32(c.window))
setU32LE(c.flushBuf0[8:12], uint32(c.gc))
setU32LE(c.flushBuf0[12:16], 1<<16|uint32(b.Dx()))
c.flushBuf0[21] = 0x18 // depth = 24 bits.
for y := b.Min.Y; y < b.Max.Y; y++ {
setU32LE(c.flushBuf0[16:20], uint32(y<<16))
if _, err := c.w.Write(c.flushBuf0[:24]); err != nil {
if err != io.EOF {
log.Println("x11:", err)
}
return
}
p := c.img.Pix[(y-b.Min.Y)*c.img.Stride:]
for x, dx := 0, 4*b.Dx(); x < dx; {
nx := dx - x
if nx > len(c.flushBuf1) {
nx = len(c.flushBuf1) &^ 3
}
for i := 0; i < nx; i += 4 {
// X11's order is BGRX, not RGBA.
c.flushBuf1[i+0] = p[x+i+2]
c.flushBuf1[i+1] = p[x+i+1]
c.flushBuf1[i+2] = p[x+i+0]
}
x += nx
if _, err := c.w.Write(c.flushBuf1[:nx]); err != nil {
if err != io.EOF {
log.Println("x11:", err)
}
return
}
}
}
if err := c.w.Flush(); err != nil {
if err != io.EOF {
log.Println("x11:", err)
}
return
}
}
}
func (c *conn) Screen() draw.Image { return c.img }
func (c *conn) FlushImage() {
select {
case c.flush <- false:
// Flush notification sent.
default:
// Could not send.
// Flush notification must be pending already.
}
}
func (c *conn) Close() error {
// Shut down the writeSocket goroutine. This will close the socket to the
// X11 server, which will cause c.eventc to close.
close(c.flush)
for _ = range c.eventc {
// Drain the channel to allow the readSocket goroutine to shut down.
}
return nil
}
func (c *conn) EventChan() <-chan interface{} { return c.eventc }
// readSocket runs in its own goroutine, reading X events and sending gui
// events on c's EventChan.
func (c *conn) readSocket() {
var (
keymap [256][]int
keysymsPerKeycode int
)
defer close(c.eventc)
for {
// X events are always 32 bytes long.
if _, err := io.ReadFull(c.r, c.buf[:32]); err != nil {
if err != io.EOF {
c.eventc <- gui.ErrEvent{err}
}
return
}
switch c.buf[0] {
case 0x01: // Reply from a request (e.g. GetKeyboardMapping).
cookie := int(c.buf[3])<<8 | int(c.buf[2])
if cookie != 1 {
// We issued only one request (GetKeyboardMapping) with a cookie of 1,
// so we shouldn't get any other reply from the X server.
c.eventc <- gui.ErrEvent{errors.New("x11: unexpected cookie")}
return
}
keysymsPerKeycode = int(c.buf[1])
b := make([]int, 256*keysymsPerKeycode)
for i := range keymap {
keymap[i] = b[i*keysymsPerKeycode : (i+1)*keysymsPerKeycode]
}
for i := keymapLo; i <= keymapHi; i++ {
m := keymap[i]
for j := range m {
u, err := readU32LE(c.r, c.buf[:4])
if err != nil {
if err != io.EOF {
c.eventc <- gui.ErrEvent{err}
}
return
}
m[j] = int(u)
}
}
case 0x02, 0x03: // Key press, key release.
// X Keyboard Encoding is documented at http://tronche.com/gui/x/xlib/input/keyboard-encoding.html
// TODO(nigeltao): Do we need to implement the "MODE SWITCH / group modifier" feature
// or is that some no-longer-used X construct?
if keysymsPerKeycode < 2 {
// Either we haven't yet received the GetKeyboardMapping reply or
// the X server has sent one that's too short.
continue
}
keycode := int(c.buf[1])
shift := int(c.buf[28]) & 0x01
keysym := keymap[keycode][shift]
if keysym == 0 {
keysym = keymap[keycode][0]
}
// TODO(nigeltao): Should we send KeyEvents for Shift/Ctrl/Alt? Should Shift-A send
// the same int down the channel as the sent on just the A key?
// TODO(nigeltao): How should IME events (e.g. key presses that should generate CJK text) work? Or
// is that outside the scope of the gui.Window interface?
if c.buf[0] == 0x03 {
keysym = -keysym
}
c.eventc <- gui.KeyEvent{keysym}
case 0x04, 0x05: // Button press, button release.
mask := 1 << (c.buf[1] - 1)
if c.buf[0] == 0x04 {
c.mouseState.Buttons |= mask
} else {
c.mouseState.Buttons &^= mask
}
c.mouseState.Nsec = time.Nanoseconds()
c.eventc <- c.mouseState
case 0x06: // Motion notify.
c.mouseState.Loc.X = int(int16(c.buf[25])<<8 | int16(c.buf[24]))
c.mouseState.Loc.Y = int(int16(c.buf[27])<<8 | int16(c.buf[26]))
c.mouseState.Nsec = time.Nanoseconds()
c.eventc <- c.mouseState
case 0x0c: // Expose.
// A single user action could trigger multiple expose events (e.g. if moving another
// window with XShape'd rounded corners over our window). In that case, the X server will
// send a uint16 count (in bytes 16-17) of the number of additional expose events coming.
// We could parse each event for the (x, y, width, height) and maintain a minimal dirty
// rectangle, but for now, the simplest approach is to paint the entire window, when
// receiving the final event in the series.
if c.buf[17] == 0 && c.buf[16] == 0 {
// TODO(nigeltao): Should we ignore the very first expose event? A freshly mapped window
// will trigger expose, but until the first c.FlushImage call, there's probably nothing to
// paint but black. For an 800x600 window, at 4 bytes per pixel, each repaint writes about
// 2MB over the socket.
c.FlushImage()
}
// TODO(nigeltao): Should we listen to DestroyNotify (0x11) and ResizeRequest (0x19) events?
// What about EnterNotify (0x07) and LeaveNotify (0x08)?
}
}
}
// connect connects to the X server given by the full X11 display name (e.g.
// ":12.0") and returns the connection as well as the portion of the full name
// that is the display number (e.g. "12").
// Examples:
// connect(":1") // calls net.Dial("unix", "", "/tmp/.X11-unix/X1"), displayStr="1"
// connect("/tmp/launch-123/:0") // calls net.Dial("unix", "", "/tmp/launch-123/:0"), displayStr="0"
// connect("hostname:2.1") // calls net.Dial("tcp", "", "hostname:6002"), displayStr="2"
// connect("tcp/hostname:1.0") // calls net.Dial("tcp", "", "hostname:6001"), displayStr="1"
func connect(display string) (conn net.Conn, displayStr string, err error) {
colonIdx := strings.LastIndex(display, ":")
if colonIdx < 0 {
return nil, "", errors.New("bad display: " + display)
}
// Parse the section before the colon.
var protocol, host, socket string
if display[0] == '/' {
socket = display[:colonIdx]
} else {
if i := strings.LastIndex(display, "/"); i < 0 {
// The default protocol is TCP.
protocol = "tcp"
host = display[:colonIdx]
} else {
protocol = display[:i]
host = display[i+1 : colonIdx]
}
}
// Parse the section after the colon.
after := display[colonIdx+1:]
if after == "" {
return nil, "", errors.New("bad display: " + display)
}
if i := strings.LastIndex(after, "."); i < 0 {
displayStr = after
} else {
displayStr = after[:i]
}
displayInt, err := strconv.Atoi(displayStr)
if err != nil || displayInt < 0 {
return nil, "", errors.New("bad display: " + display)
}
// Make the connection.
if socket != "" {
conn, err = net.Dial("unix", socket+":"+displayStr)
} else if host != "" {
conn, err = net.Dial(protocol, host+":"+strconv.Itoa(6000+displayInt))
} else {
conn, err = net.Dial("unix", "/tmp/.X11-unix/X"+displayStr)
}
if err != nil {
return nil, "", errors.New("cannot connect to " + display + ": " + err.Error())
}
return
}
// authenticate authenticates ourselves with the X server.
// displayStr is the "12" out of ":12.0".
func authenticate(w *bufio.Writer, displayStr string) error {
key, value, err := readAuth(displayStr)
if err != nil {
return err
}
// Assume that the authentication protocol is "MIT-MAGIC-COOKIE-1".
if len(key) != 18 || len(value) != 16 {
return errors.New("unsupported Xauth")
}
// 0x006c means little-endian. 0x000b, 0x0000 means X major version 11, minor version 0.
// 0x0012 and 0x0010 means the auth key and value have lengths 18 and 16.
// The final 0x0000 is padding, so that the string length is a multiple of 4.
_, err = io.WriteString(w, "\x6c\x00\x0b\x00\x00\x00\x12\x00\x10\x00\x00\x00")
if err != nil {
return err
}
_, err = io.WriteString(w, key)
if err != nil {
return err
}
// Again, the 0x0000 is padding.
_, err = io.WriteString(w, "\x00\x00")
if err != nil {
return err
}
_, err = io.WriteString(w, value)
if err != nil {
return err
}
err = w.Flush()
if err != nil {
return err
}
return nil
}
// readU8 reads a uint8 from r, using b as a scratch buffer.
func readU8(r io.Reader, b []byte) (uint8, error) {
_, err := io.ReadFull(r, b[:1])
if err != nil {
return 0, err
}
return uint8(b[0]), nil
}
// readU16LE reads a little-endian uint16 from r, using b as a scratch buffer.
func readU16LE(r io.Reader, b []byte) (uint16, error) {
_, err := io.ReadFull(r, b[:2])
if err != nil {
return 0, err
}
return uint16(b[0]) | uint16(b[1])<<8, nil
}
// readU32LE reads a little-endian uint32 from r, using b as a scratch buffer.
func readU32LE(r io.Reader, b []byte) (uint32, error) {
_, err := io.ReadFull(r, b[:4])
if err != nil {
return 0, err
}
return uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24, nil
}
// setU32LE sets b[:4] to be the little-endian representation of u.
func setU32LE(b []byte, u uint32) {
b[0] = byte((u >> 0) & 0xff)
b[1] = byte((u >> 8) & 0xff)
b[2] = byte((u >> 16) & 0xff)
b[3] = byte((u >> 24) & 0xff)
}
// checkPixmapFormats checks that we have an agreeable X pixmap Format.
func checkPixmapFormats(r io.Reader, b []byte, n int) (agree bool, err error) {
for i := 0; i < n; i++ {
_, err = io.ReadFull(r, b[:8])
if err != nil {
return
}
// Byte 0 is depth, byte 1 is bits-per-pixel, byte 2 is scanline-pad, the rest (5) is padding.
if b[0] == 24 && b[1] == 32 {
agree = true
}
}
return
}
// checkDepths checks that we have an agreeable X Depth (i.e. one that has an agreeable X VisualType).
func checkDepths(r io.Reader, b []byte, n int, visual uint32) (agree bool, err error) {
for i := 0; i < n; i++ {
var depth, visualsLen uint16
depth, err = readU16LE(r, b)
if err != nil {
return
}
depth &= 0xff
visualsLen, err = readU16LE(r, b)
if err != nil {
return
}
// Ignore 4 bytes of padding.
_, err = io.ReadFull(r, b[:4])
if err != nil {
return
}
for j := 0; j < int(visualsLen); j++ {
// Read 24 bytes: visual(4), class(1), bits per rgb value(1), colormap entries(2),
// red mask(4), green mask(4), blue mask(4), padding(4).
v, _ := readU32LE(r, b)
_, _ = readU32LE(r, b)
rm, _ := readU32LE(r, b)
gm, _ := readU32LE(r, b)
bm, _ := readU32LE(r, b)
_, err = readU32LE(r, b)
if err != nil {
return
}
if v == visual && rm == 0xff0000 && gm == 0xff00 && bm == 0xff && depth == 24 {
agree = true
}
}
}
return
}
// checkScreens checks that we have an agreeable X Screen.
func checkScreens(r io.Reader, b []byte, n int) (root, visual uint32, err error) {
for i := 0; i < n; i++ {
var root0, visual0, x uint32
root0, err = readU32LE(r, b)
if err != nil {
return
}
// Ignore the next 7x4 bytes, which is: colormap, whitepixel, blackpixel, current input masks,
// width and height (pixels), width and height (mm), min and max installed maps.
_, err = io.ReadFull(r, b[:28])
if err != nil {
return
}
visual0, err = readU32LE(r, b)
if err != nil {
return
}
// Next 4 bytes: backing stores, save unders, root depth, allowed depths length.
x, err = readU32LE(r, b)
if err != nil {
return
}
nDepths := int(x >> 24)
var agree bool
agree, err = checkDepths(r, b, nDepths, visual0)
if err != nil {
return
}
if agree && root == 0 {
root = root0
visual = visual0
}
}
return
}
// handshake performs the protocol handshake with the X server, and ensures
// that the server provides a compatible Screen, Depth, etc.
func (c *conn) handshake() error {
_, err := io.ReadFull(c.r, c.buf[:8])
if err != nil {
return err
}
// Byte 0 should be 1 (success), bytes 2:6 should be 0xb0000000 (major/minor version 11.0).
if c.buf[0] != 1 || c.buf[2] != 11 || c.buf[3] != 0 || c.buf[4] != 0 || c.buf[5] != 0 {
return errors.New("unsupported X version")
}
// Ignore the release number.
_, err = io.ReadFull(c.r, c.buf[:4])
if err != nil {
return err
}
// Read the resource ID base.
resourceIdBase, err := readU32LE(c.r, c.buf[:4])
if err != nil {
return err
}
// Read the resource ID mask.
resourceIdMask, err := readU32LE(c.r, c.buf[:4])
if err != nil {
return err
}
if resourceIdMask < 256 {
return errors.New("X resource ID mask is too small")
}
// Ignore the motion buffer size.
_, err = io.ReadFull(c.r, c.buf[:4])
if err != nil {
return err
}
// Read the vendor length and round it up to a multiple of 4,
// for X11 protocol alignment reasons.
vendorLen, err := readU16LE(c.r, c.buf[:2])
if err != nil {
return err
}
vendorLen = (vendorLen + 3) &^ 3
// Read the maximum request length.
maxReqLen, err := readU16LE(c.r, c.buf[:2])
if err != nil {
return err
}
if maxReqLen != 0xffff {
return errors.New("unsupported X maximum request length")
}
// Read the roots length.
rootsLen, err := readU8(c.r, c.buf[:1])
if err != nil {
return err
}
// Read the pixmap formats length.
pixmapFormatsLen, err := readU8(c.r, c.buf[:1])
if err != nil {
return err
}
// Ignore some things that we don't care about (totaling 10 + vendorLen bytes):
// imageByteOrder(1), bitmapFormatBitOrder(1), bitmapFormatScanlineUnit(1) bitmapFormatScanlinePad(1),
// minKeycode(1), maxKeycode(1), padding(4), vendor (vendorLen).
if 10+int(vendorLen) > cap(c.buf) {
return errors.New("unsupported X vendor")
}
_, err = io.ReadFull(c.r, c.buf[:10+int(vendorLen)])
if err != nil {
return err
}
// Check that we have an agreeable pixmap format.
agree, err := checkPixmapFormats(c.r, c.buf[:8], int(pixmapFormatsLen))
if err != nil {
return err
}
if !agree {
return errors.New("unsupported X pixmap formats")
}
// Check that we have an agreeable screen.
root, visual, err := checkScreens(c.r, c.buf[:24], int(rootsLen))
if err != nil {
return err
}
if root == 0 || visual == 0 {
return errors.New("unsupported X screen")
}
c.gc = resID(resourceIdBase)
c.window = resID(resourceIdBase + 1)
c.root = resID(root)
c.visual = resID(visual)
return nil
}
// NewWindow calls NewWindowDisplay with $DISPLAY.
func NewWindow() (gui.Window, error) {
display := os.Getenv("DISPLAY")
if len(display) == 0 {
return nil, errors.New("$DISPLAY not set")
}
return NewWindowDisplay(display)
}
// NewWindowDisplay returns a new gui.Window, backed by a newly created and
// mapped X11 window. The X server to connect to is specified by the display
// string, such as ":1".
func NewWindowDisplay(display string) (gui.Window, error) {
socket, displayStr, err := connect(display)
if err != nil {
return nil, err
}
c := new(conn)
c.c = socket
c.r = bufio.NewReader(socket)
c.w = bufio.NewWriter(socket)
err = authenticate(c.w, displayStr)
if err != nil {
return nil, err
}
err = c.handshake()
if err != nil {
return nil, err
}
// Now that we're connected, show a window, via three X protocol messages.
// First, issue a GetKeyboardMapping request. This is the first request, and
// will be associated with a cookie of 1.
setU32LE(c.buf[0:4], 0x00020065) // 0x65 is the GetKeyboardMapping opcode, and the message is 2 x 4 bytes long.
setU32LE(c.buf[4:8], uint32((keymapHi-keymapLo+1)<<8|keymapLo))
// Second, create a graphics context (GC).
setU32LE(c.buf[8:12], 0x00060037) // 0x37 is the CreateGC opcode, and the message is 6 x 4 bytes long.
setU32LE(c.buf[12:16], uint32(c.gc))
setU32LE(c.buf[16:20], uint32(c.root))
setU32LE(c.buf[20:24], 0x00010004) // Bit 2 is XCB_GC_FOREGROUND, bit 16 is XCB_GC_GRAPHICS_EXPOSURES.
setU32LE(c.buf[24:28], 0x00000000) // The Foreground is black.
setU32LE(c.buf[28:32], 0x00000000) // GraphicsExposures' value is unused.
// Third, create the window.
setU32LE(c.buf[32:36], 0x000a0001) // 0x01 is the CreateWindow opcode, and the message is 10 x 4 bytes long.
setU32LE(c.buf[36:40], uint32(c.window))
setU32LE(c.buf[40:44], uint32(c.root))
setU32LE(c.buf[44:48], 0x00000000) // Initial (x, y) is (0, 0).
setU32LE(c.buf[48:52], windowHeight<<16|windowWidth)
setU32LE(c.buf[52:56], 0x00010000) // Border width is 0, XCB_WINDOW_CLASS_INPUT_OUTPUT is 1.
setU32LE(c.buf[56:60], uint32(c.visual))
setU32LE(c.buf[60:64], 0x00000802) // Bit 1 is XCB_CW_BACK_PIXEL, bit 11 is XCB_CW_EVENT_MASK.
setU32LE(c.buf[64:68], 0x00000000) // The Back-Pixel is black.
setU32LE(c.buf[68:72], 0x0000804f) // Key/button press and release, pointer motion, and expose event masks.
// Fourth, map the window.
setU32LE(c.buf[72:76], 0x00020008) // 0x08 is the MapWindow opcode, and the message is 2 x 4 bytes long.
setU32LE(c.buf[76:80], uint32(c.window))
// Write the bytes.
_, err = c.w.Write(c.buf[:80])
if err != nil {
return nil, err
}
err = c.w.Flush()
if err != nil {
return nil, err
}
c.img = image.NewRGBA(image.Rect(0, 0, windowWidth, windowHeight))
c.eventc = make(chan interface{}, 16)
c.flush = make(chan bool, 1)
go c.readSocket()
go c.writeSocket()
return c, nil
}

View File

@ -7,7 +7,7 @@
//
// Code simply using databases should use package sql.
//
// Drivers only need to be aware of a subset of Go's types. The db package
// Drivers only need to be aware of a subset of Go's types. The sql package
// will convert all types into one of the following:
//
// int64
@ -94,12 +94,35 @@ type Result interface {
// used by multiple goroutines concurrently.
type Stmt interface {
// Close closes the statement.
//
// Closing a statement should not interrupt any outstanding
// query created from that statement. That is, the following
// order of operations is valid:
//
// * create a driver statement
// * call Query on statement, returning Rows
// * close the statement
// * read from Rows
//
// If closing a statement invalidates currently-running
// queries, the final step above will incorrectly fail.
//
// TODO(bradfitz): possibly remove the restriction above, if
// enough driver authors object and find it complicates their
// code too much. The sql package could be smarter about
// refcounting the statement and closing it at the appropriate
// time.
Close() error
// NumInput returns the number of placeholder parameters.
// -1 means the driver doesn't know how to count the number of
// placeholders, so we won't sanity check input here and instead let the
// driver deal with errors.
//
// If NumInput returns >= 0, the sql package will sanity check
// argument counts from callers and return errors to the caller
// before the statement's Exec or Query methods are called.
//
// NumInput may also return -1, if the driver doesn't know
// its number of placeholders. In that case, the sql package
// will not sanity check Exec or Query argument counts.
NumInput() int
// Exec executes a query that doesn't return rows, such

View File

@ -90,6 +90,8 @@ type fakeStmt struct {
cmd string
table string
closed bool
colName []string // used by CREATE, INSERT, SELECT (selected columns)
colType []string // used by CREATE
colValue []interface{} // used by INSERT (mix of strings and "?" for bound params)
@ -232,6 +234,9 @@ func (c *fakeConn) prepareSelect(stmt *fakeStmt, parts []string) (driver.Stmt, e
stmt.table = parts[0]
stmt.colName = strings.Split(parts[1], ",")
for n, colspec := range strings.Split(parts[2], ",") {
if colspec == "" {
continue
}
nameVal := strings.Split(colspec, "=")
if len(nameVal) != 2 {
return nil, errf("SELECT on table %q has invalid column spec of %q (index %d)", stmt.table, colspec, n)
@ -342,10 +347,16 @@ func (s *fakeStmt) ColumnConverter(idx int) driver.ValueConverter {
}
func (s *fakeStmt) Close() error {
s.closed = true
return nil
}
var errClosed = errors.New("fakedb: statement has been closed")
func (s *fakeStmt) Exec(args []interface{}) (driver.Result, error) {
if s.closed {
return nil, errClosed
}
err := checkSubsetTypes(args)
if err != nil {
return nil, err
@ -405,6 +416,9 @@ func (s *fakeStmt) execInsert(args []interface{}) (driver.Result, error) {
}
func (s *fakeStmt) Query(args []interface{}) (driver.Rows, error) {
if s.closed {
return nil, errClosed
}
err := checkSubsetTypes(args)
if err != nil {
return nil, err

View File

@ -344,25 +344,26 @@ func (tx *Tx) Rollback() error {
return tx.txi.Rollback()
}
// Prepare creates a prepared statement.
// Prepare creates a prepared statement for use within a transaction.
//
// The statement is only valid within the scope of this transaction.
// The returned statement operates within the transaction and can no longer
// be used once the transaction has been committed or rolled back.
//
// To use an existing prepared statement on this transaction, see Tx.Stmt.
func (tx *Tx) Prepare(query string) (*Stmt, error) {
// TODO(bradfitz): the restriction that the returned statement
// is only valid for this Transaction is lame and negates a
// lot of the benefit of prepared statements. We could be
// more efficient here and either provide a method to take an
// existing Stmt (created on perhaps a different Conn), and
// re-create it on this Conn if necessary. Or, better: keep a
// map in DB of query string to Stmts, and have Stmt.Execute
// do the right thing and re-prepare if the Conn in use
// doesn't have that prepared statement. But we'll want to
// avoid caching the statement in the case where we only call
// conn.Prepare implicitly (such as in db.Exec or tx.Exec),
// but the caller package can't be holding a reference to the
// returned statement. Perhaps just looking at the reference
// count (by noting Stmt.Close) would be enough. We might also
// want a finalizer on Stmt to drop the reference count.
// TODO(bradfitz): We could be more efficient here and either
// provide a method to take an existing Stmt (created on
// perhaps a different Conn), and re-create it on this Conn if
// necessary. Or, better: keep a map in DB of query string to
// Stmts, and have Stmt.Execute do the right thing and
// re-prepare if the Conn in use doesn't have that prepared
// statement. But we'll want to avoid caching the statement
// in the case where we only call conn.Prepare implicitly
// (such as in db.Exec or tx.Exec), but the caller package
// can't be holding a reference to the returned statement.
// Perhaps just looking at the reference count (by noting
// Stmt.Close) would be enough. We might also want a finalizer
// on Stmt to drop the reference count.
ci, err := tx.grabConn()
if err != nil {
return nil, err
@ -383,6 +384,39 @@ func (tx *Tx) Prepare(query string) (*Stmt, error) {
return stmt, nil
}
// Stmt returns a transaction-specific prepared statement from
// an existing statement.
//
// Example:
// updateMoney, err := db.Prepare("UPDATE balance SET money=money+? WHERE id=?")
// ...
// tx, err := db.Begin()
// ...
// res, err := tx.Stmt(updateMoney).Exec(123.45, 98293203)
func (tx *Tx) Stmt(stmt *Stmt) *Stmt {
// TODO(bradfitz): optimize this. Currently this re-prepares
// each time. This is fine for now to illustrate the API but
// we should really cache already-prepared statements
// per-Conn. See also the big comment in Tx.Prepare.
if tx.db != stmt.db {
return &Stmt{stickyErr: errors.New("sql: Tx.Stmt: statement from different database used")}
}
ci, err := tx.grabConn()
if err != nil {
return &Stmt{stickyErr: err}
}
defer tx.releaseConn()
si, err := ci.Prepare(stmt.query)
return &Stmt{
db: tx.db,
tx: tx,
txsi: si,
query: stmt.query,
stickyErr: err,
}
}
// Exec executes a query that doesn't return rows.
// For example: an INSERT and UPDATE.
func (tx *Tx) Exec(query string, args ...interface{}) (Result, error) {
@ -448,8 +482,9 @@ type connStmt struct {
// Stmt is a prepared statement. Stmt is safe for concurrent use by multiple goroutines.
type Stmt struct {
// Immutable:
db *DB // where we came from
query string // that created the Sttm
db *DB // where we came from
query string // that created the Stmt
stickyErr error // if non-nil, this error is returned for all operations
// If in a transaction, else both nil:
tx *Tx
@ -513,6 +548,9 @@ func (s *Stmt) Exec(args ...interface{}) (Result, error) {
// statement, a function to call to release the connection, and a
// statement bound to that connection.
func (s *Stmt) connStmt() (ci driver.Conn, releaseConn func(), si driver.Stmt, err error) {
if s.stickyErr != nil {
return nil, nil, nil, s.stickyErr
}
s.mu.Lock()
if s.closed {
s.mu.Unlock()
@ -621,6 +659,9 @@ func (s *Stmt) QueryRow(args ...interface{}) *Row {
// Close closes the statement.
func (s *Stmt) Close() error {
if s.stickyErr != nil {
return s.stickyErr
}
s.mu.Lock()
defer s.mu.Unlock()
if s.closed {

View File

@ -5,6 +5,7 @@
package sql
import (
"reflect"
"strings"
"testing"
)
@ -22,7 +23,6 @@ func newTestDB(t *testing.T, name string) *DB {
exec(t, db, "INSERT|people|name=Alice,age=?", 1)
exec(t, db, "INSERT|people|name=Bob,age=?", 2)
exec(t, db, "INSERT|people|name=Chris,age=?", 3)
}
return db
}
@ -42,6 +42,40 @@ func closeDB(t *testing.T, db *DB) {
}
func TestQuery(t *testing.T) {
db := newTestDB(t, "people")
defer closeDB(t, db)
rows, err := db.Query("SELECT|people|age,name|")
if err != nil {
t.Fatalf("Query: %v", err)
}
type row struct {
age int
name string
}
got := []row{}
for rows.Next() {
var r row
err = rows.Scan(&r.age, &r.name)
if err != nil {
t.Fatalf("Scan: %v", err)
}
got = append(got, r)
}
err = rows.Err()
if err != nil {
t.Fatalf("Err: %v", err)
}
want := []row{
{age: 1, name: "Alice"},
{age: 2, name: "Bob"},
{age: 3, name: "Chris"},
}
if !reflect.DeepEqual(got, want) {
t.Logf(" got: %#v\nwant: %#v", got, want)
}
}
func TestQueryRow(t *testing.T) {
db := newTestDB(t, "people")
defer closeDB(t, db)
var name string
@ -75,6 +109,24 @@ func TestQuery(t *testing.T) {
}
}
func TestStatementErrorAfterClose(t *testing.T) {
db := newTestDB(t, "people")
defer closeDB(t, db)
stmt, err := db.Prepare("SELECT|people|age|name=?")
if err != nil {
t.Fatalf("Prepare: %v", err)
}
err = stmt.Close()
if err != nil {
t.Fatalf("Close: %v", err)
}
var name string
err = stmt.QueryRow("foo").Scan(&name)
if err == nil {
t.Errorf("expected error from QueryRow.Scan after Stmt.Close")
}
}
func TestStatementQueryRow(t *testing.T) {
db := newTestDB(t, "people")
defer closeDB(t, db)
@ -114,7 +166,7 @@ func TestBogusPreboundParameters(t *testing.T) {
}
}
func TestDb(t *testing.T) {
func TestExec(t *testing.T) {
db := newTestDB(t, "foo")
defer closeDB(t, db)
exec(t, db, "CREATE|t1|name=string,age=int32,dead=bool")
@ -154,3 +206,25 @@ func TestDb(t *testing.T) {
}
}
}
func TestTxStmt(t *testing.T) {
db := newTestDB(t, "")
defer closeDB(t, db)
exec(t, db, "CREATE|t1|name=string,age=int32,dead=bool")
stmt, err := db.Prepare("INSERT|t1|name=?,age=?")
if err != nil {
t.Fatalf("Stmt, err = %v, %v", stmt, err)
}
tx, err := db.Begin()
if err != nil {
t.Fatalf("Begin = %v", err)
}
_, err = tx.Stmt(stmt).Exec("Bobby", 7)
if err != nil {
t.Fatalf("Exec = %v", err)
}
err = tx.Commit()
if err != nil {
t.Fatalf("Commit = %v", err)
}
}

View File

@ -244,13 +244,13 @@ func (c *channel) Write(data []byte) (n int, err error) {
packet := make([]byte, 1+4+4+len(todo))
packet[0] = msgChannelData
packet[1] = byte(c.theirId) >> 24
packet[2] = byte(c.theirId) >> 16
packet[3] = byte(c.theirId) >> 8
packet[1] = byte(c.theirId >> 24)
packet[2] = byte(c.theirId >> 16)
packet[3] = byte(c.theirId >> 8)
packet[4] = byte(c.theirId)
packet[5] = byte(len(todo)) >> 24
packet[6] = byte(len(todo)) >> 16
packet[7] = byte(len(todo)) >> 8
packet[5] = byte(len(todo) >> 24)
packet[6] = byte(len(todo) >> 16)
packet[7] = byte(len(todo) >> 8)
packet[8] = byte(len(todo))
copy(packet[9:], todo)

View File

@ -172,40 +172,12 @@ func (c *ClientConn) kexDH(group *dhGroup, hashFunc crypto.Hash, magics *handsha
marshalInt(K, kInt)
h.Write(K)
H := h.Sum()
H := h.Sum(nil)
return H, K, nil
}
// openChan opens a new client channel. The most common session type is "session".
// The full set of valid session types are listed in RFC 4250 4.9.1.
func (c *ClientConn) openChan(typ string) (*clientChan, error) {
ch := c.newChan(c.transport)
if err := c.writePacket(marshal(msgChannelOpen, channelOpenMsg{
ChanType: typ,
PeersId: ch.id,
PeersWindow: 1 << 14,
MaxPacketSize: 1 << 15, // RFC 4253 6.1
})); err != nil {
c.chanlist.remove(ch.id)
return nil, err
}
// wait for response
switch msg := (<-ch.msg).(type) {
case *channelOpenConfirmMsg:
ch.peersId = msg.MyId
ch.win <- int(msg.MyWindow)
case *channelOpenFailureMsg:
c.chanlist.remove(ch.id)
return nil, errors.New(msg.Message)
default:
c.chanlist.remove(ch.id)
return nil, errors.New("Unexpected packet")
}
return ch, nil
}
// mainloop reads incoming messages and routes channel messages
// mainLoop reads incoming messages and routes channel messages
// to their respective ClientChans.
func (c *ClientConn) mainLoop() {
// TODO(dfc) signal the underlying close to all channels
@ -271,7 +243,7 @@ func (c *ClientConn) mainLoop() {
case *windowAdjustMsg:
c.getChan(msg.PeersId).win <- int(msg.AdditionalBytes)
default:
fmt.Printf("mainLoop: unhandled %#v\n", msg)
fmt.Printf("mainLoop: unhandled message %T: %v\n", msg, msg)
}
}
}
@ -338,27 +310,16 @@ func newClientChan(t *transport, id uint32) *clientChan {
// Close closes the channel. This does not close the underlying connection.
func (c *clientChan) Close() error {
return c.writePacket(marshal(msgChannelClose, channelCloseMsg{
PeersId: c.id,
PeersId: c.peersId,
}))
}
func (c *clientChan) sendChanReq(req channelRequestMsg) error {
if err := c.writePacket(marshal(msgChannelRequest, req)); err != nil {
return err
}
msg := <-c.msg
if _, ok := msg.(*channelRequestSuccessMsg); ok {
return nil
}
return fmt.Errorf("failed to complete request: %s, %#v", req.Request, msg)
}
// Thread safe channel list.
type chanlist struct {
// protects concurrent access to chans
sync.Mutex
// chans are indexed by the local id of the channel, clientChan.id.
// The PeersId value of messages received by ClientConn.mainloop is
// The PeersId value of messages received by ClientConn.mainLoop is
// used to locate the right local clientChan in this slice.
chans []*clientChan
}
@ -395,7 +356,7 @@ func (c *chanlist) remove(id uint32) {
// A chanWriter represents the stdin of a remote process.
type chanWriter struct {
win chan int // receives window adjustments
id uint32 // this channel's id
peersId uint32 // the peer's id
rwin int // current rwin size
packetWriter // for sending channelDataMsg
}
@ -414,8 +375,8 @@ func (w *chanWriter) Write(data []byte) (n int, err error) {
n = len(data)
packet := make([]byte, 0, 9+n)
packet = append(packet, msgChannelData,
byte(w.id)>>24, byte(w.id)>>16, byte(w.id)>>8, byte(w.id),
byte(n)>>24, byte(n)>>16, byte(n)>>8, byte(n))
byte(w.peersId>>24), byte(w.peersId>>16), byte(w.peersId>>8), byte(w.peersId),
byte(n>>24), byte(n>>16), byte(n>>8), byte(n))
err = w.writePacket(append(packet, data...))
w.rwin -= n
return
@ -424,7 +385,7 @@ func (w *chanWriter) Write(data []byte) (n int, err error) {
}
func (w *chanWriter) Close() error {
return w.writePacket(marshal(msgChannelEOF, channelEOFMsg{w.id}))
return w.writePacket(marshal(msgChannelEOF, channelEOFMsg{w.peersId}))
}
// A chanReader represents stdout or stderr of a remote process.
@ -433,8 +394,8 @@ type chanReader struct {
// If writes to this channel block, they will block mainLoop, making
// it unable to receive new messages from the remote side.
data chan []byte // receives data from remote
id uint32
packetWriter // for sending windowAdjustMsg
peersId uint32 // the peer's id
packetWriter // for sending windowAdjustMsg
buf []byte
}
@ -446,7 +407,7 @@ func (r *chanReader) Read(data []byte) (int, error) {
n := copy(data, r.buf)
r.buf = r.buf[n:]
msg := windowAdjustMsg{
PeersId: r.id,
PeersId: r.peersId,
AdditionalBytes: uint32(n),
}
return n, r.writePacket(marshal(msgChannelWindowAdjust, msg))
@ -458,7 +419,3 @@ func (r *chanReader) Read(data []byte) (int, error) {
}
panic("unreachable")
}
func (r *chanReader) Close() error {
return r.writePacket(marshal(msgChannelEOF, channelEOFMsg{r.id}))
}

View File

@ -70,7 +70,7 @@ func (k *keychain) Sign(i int, rand io.Reader, data []byte) (sig []byte, err err
hashFunc := crypto.SHA1
h := hashFunc.New()
h.Write(data)
digest := h.Sum()
digest := h.Sum(nil)
return rsa.SignPKCS1v15(rand, k.keys[i], hashFunc, digest)
}

View File

@ -224,3 +224,16 @@ func buildDataSignedForAuth(sessionId []byte, req userAuthRequestMsg, algo, pubK
r = marshalString(r, pubKey)
return ret
}
// safeString sanitises s according to RFC 4251, section 9.2.
// All control characters except tab, carriage return and newline are
// replaced by 0x20.
func safeString(s string) string {
out := []byte(s)
for i, c := range out {
if c < 0x20 && c != 0xd && c != 0xa && c != 0x9 {
out[i] = 0x20
}
}
return string(out)
}

View File

@ -0,0 +1,26 @@
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package ssh
import (
"testing"
)
var strings = map[string]string{
"\x20\x0d\x0a": "\x20\x0d\x0a",
"flibble": "flibble",
"new\x20line": "new\x20line",
"123456\x07789": "123456 789",
"\t\t\x10\r\n": "\t\t \r\n",
}
func TestSafeString(t *testing.T) {
for s, expected := range strings {
actual := safeString(s)
if expected != actual {
t.Errorf("expected: %v, actual: %v", []byte(expected), []byte(actual))
}
}
}

View File

@ -92,9 +92,9 @@ Each ClientConn can support multiple interactive sessions, represented by a Sess
session, err := client.NewSession()
Once a Session is created, you can execute a single command on the remote side
using the Exec method.
using the Run method.
if err := session.Exec("/usr/bin/whoami"); err != nil {
if err := session.Run("/usr/bin/whoami"); err != nil {
panic("Failed to exec: " + err.String())
}
reader := bufio.NewReader(session.Stdin)

View File

@ -207,11 +207,11 @@ func (s *ServerConn) kexDH(group *dhGroup, hashFunc crypto.Hash, magics *handsha
marshalInt(K, kInt)
h.Write(K)
H = h.Sum()
H = h.Sum(nil)
h.Reset()
h.Write(H)
hh := h.Sum()
hh := h.Sum(nil)
var sig []byte
switch hostKeyAlgo {
@ -478,7 +478,7 @@ userAuthLoop:
hashFunc := crypto.SHA1
h := hashFunc.New()
h.Write(signedData)
digest := h.Sum()
digest := h.Sum(nil)
rsaKey, ok := parseRSA(pubKey)
if !ok {
return ParseError{msgUserAuthRequest}

View File

@ -8,125 +8,409 @@ package ssh
// "RFC 4254, section 6".
import (
"encoding/binary"
"bytes"
"errors"
"fmt"
"io"
"io/ioutil"
)
type Signal string
// POSIX signals as listed in RFC 4254 Section 6.10.
const (
SIGABRT Signal = "ABRT"
SIGALRM Signal = "ALRM"
SIGFPE Signal = "FPE"
SIGHUP Signal = "HUP"
SIGILL Signal = "ILL"
SIGINT Signal = "INT"
SIGKILL Signal = "KILL"
SIGPIPE Signal = "PIPE"
SIGQUIT Signal = "QUIT"
SIGSEGV Signal = "SEGV"
SIGTERM Signal = "TERM"
SIGUSR1 Signal = "USR1"
SIGUSR2 Signal = "USR2"
)
// A Session represents a connection to a remote command or shell.
type Session struct {
// Writes to Stdin are made available to the remote command's standard input.
// Closing Stdin causes the command to observe an EOF on its standard input.
Stdin io.WriteCloser
// Stdin specifies the remote process's standard input.
// If Stdin is nil, the remote process reads from an empty
// bytes.Buffer.
Stdin io.Reader
// Reads from Stdout and Stderr consume from the remote command's standard
// output and error streams, respectively.
// There is a fixed amount of buffering that is shared for the two streams.
// Failing to read from either may eventually cause the command to block.
// Closing Stdout unblocks such writes and causes them to return errors.
Stdout io.ReadCloser
Stderr io.Reader
// Stdout and Stderr specify the remote process's standard
// output and error.
//
// If either is nil, Run connects the corresponding file
// descriptor to an instance of ioutil.Discard. There is a
// fixed amount of buffering that is shared for the two streams.
// If either blocks it may eventually cause the remote
// command to block.
Stdout io.Writer
Stderr io.Writer
*clientChan // the channel backing this session
started bool // started is set to true once a Shell or Exec is invoked.
started bool // true once Start, Run or Shell is invoked.
closeAfterWait []io.Closer
copyFuncs []func() error
errch chan error // one send per copyFunc
}
// RFC 4254 Section 6.4.
type setenvRequest struct {
PeersId uint32
Request string
WantReply bool
Name string
Value string
}
// Setenv sets an environment variable that will be applied to any
// command executed by Shell or Exec.
// command executed by Shell or Run.
func (s *Session) Setenv(name, value string) error {
n, v := []byte(name), []byte(value)
nlen, vlen := stringLength(n), stringLength(v)
payload := make([]byte, nlen+vlen)
marshalString(payload[:nlen], n)
marshalString(payload[nlen:], v)
return s.sendChanReq(channelRequestMsg{
PeersId: s.id,
Request: "env",
WantReply: true,
RequestSpecificData: payload,
})
req := setenvRequest{
PeersId: s.peersId,
Request: "env",
WantReply: true,
Name: name,
Value: value,
}
if err := s.writePacket(marshal(msgChannelRequest, req)); err != nil {
return err
}
return s.waitForResponse()
}
// An empty mode list (a string of 1 character, opcode 0), see RFC 4254 Section 8.
var emptyModeList = []byte{0, 0, 0, 1, 0}
// An empty mode list, see RFC 4254 Section 8.
var emptyModelist = "\x00"
// RFC 4254 Section 6.2.
type ptyRequestMsg struct {
PeersId uint32
Request string
WantReply bool
Term string
Columns uint32
Rows uint32
Width uint32
Height uint32
Modelist string
}
// RequestPty requests the association of a pty with the session on the remote host.
func (s *Session) RequestPty(term string, h, w int) error {
buf := make([]byte, 4+len(term)+16+len(emptyModeList))
b := marshalString(buf, []byte(term))
binary.BigEndian.PutUint32(b, uint32(h))
binary.BigEndian.PutUint32(b[4:], uint32(w))
binary.BigEndian.PutUint32(b[8:], uint32(h*8))
binary.BigEndian.PutUint32(b[12:], uint32(w*8))
copy(b[16:], emptyModeList)
return s.sendChanReq(channelRequestMsg{
PeersId: s.id,
Request: "pty-req",
WantReply: true,
RequestSpecificData: buf,
})
}
// Exec runs cmd on the remote host. Typically, the remote
// server passes cmd to the shell for interpretation.
// A Session only accepts one call to Exec or Shell.
func (s *Session) Exec(cmd string) error {
if s.started {
return errors.New("session already started")
req := ptyRequestMsg{
PeersId: s.peersId,
Request: "pty-req",
WantReply: true,
Term: term,
Columns: uint32(w),
Rows: uint32(h),
Width: uint32(w * 8),
Height: uint32(h * 8),
Modelist: emptyModelist,
}
cmdLen := stringLength([]byte(cmd))
payload := make([]byte, cmdLen)
marshalString(payload, []byte(cmd))
s.started = true
return s.sendChanReq(channelRequestMsg{
PeersId: s.id,
Request: "exec",
WantReply: true,
RequestSpecificData: payload,
})
if err := s.writePacket(marshal(msgChannelRequest, req)); err != nil {
return err
}
return s.waitForResponse()
}
// Shell starts a login shell on the remote host. A Session only
// accepts one call to Exec or Shell.
// RFC 4254 Section 6.9.
type signalMsg struct {
PeersId uint32
Request string
WantReply bool
Signal string
}
// Signal sends the given signal to the remote process.
// sig is one of the SIG* constants.
func (s *Session) Signal(sig Signal) error {
req := signalMsg{
PeersId: s.peersId,
Request: "signal",
WantReply: false,
Signal: string(sig),
}
return s.writePacket(marshal(msgChannelRequest, req))
}
// RFC 4254 Section 6.5.
type execMsg struct {
PeersId uint32
Request string
WantReply bool
Command string
}
// Start runs cmd on the remote host. Typically, the remote
// server passes cmd to the shell for interpretation.
// A Session only accepts one call to Run, Start or Shell.
func (s *Session) Start(cmd string) error {
if s.started {
return errors.New("ssh: session already started")
}
req := execMsg{
PeersId: s.peersId,
Request: "exec",
WantReply: true,
Command: cmd,
}
if err := s.writePacket(marshal(msgChannelRequest, req)); err != nil {
return err
}
if err := s.waitForResponse(); err != nil {
return fmt.Errorf("ssh: could not execute command %s: %v", cmd, err)
}
return s.start()
}
// Run runs cmd on the remote host and waits for it to terminate.
// Typically, the remote server passes cmd to the shell for
// interpretation. A Session only accepts one call to Run,
// Start or Shell.
func (s *Session) Run(cmd string) error {
err := s.Start(cmd)
if err != nil {
return err
}
return s.Wait()
}
// Shell starts a login shell on the remote host. A Session only
// accepts one call to Run, Start or Shell.
func (s *Session) Shell() error {
if s.started {
return errors.New("session already started")
return errors.New("ssh: session already started")
}
s.started = true
return s.sendChanReq(channelRequestMsg{
PeersId: s.id,
req := channelRequestMsg{
PeersId: s.peersId,
Request: "shell",
WantReply: true,
})
}
if err := s.writePacket(marshal(msgChannelRequest, req)); err != nil {
return err
}
if err := s.waitForResponse(); err != nil {
return fmt.Errorf("ssh: cound not execute shell: %v", err)
}
return s.start()
}
func (s *Session) waitForResponse() error {
msg := <-s.msg
switch msg.(type) {
case *channelRequestSuccessMsg:
return nil
case *channelRequestFailureMsg:
return errors.New("request failed")
}
return fmt.Errorf("unknown packet %T received: %v", msg, msg)
}
func (s *Session) start() error {
s.started = true
type F func(*Session) error
for _, setupFd := range []F{(*Session).stdin, (*Session).stdout, (*Session).stderr} {
if err := setupFd(s); err != nil {
return err
}
}
s.errch = make(chan error, len(s.copyFuncs))
for _, fn := range s.copyFuncs {
go func(fn func() error) {
s.errch <- fn()
}(fn)
}
return nil
}
// Wait waits for the remote command to exit.
func (s *Session) Wait() error {
if !s.started {
return errors.New("ssh: session not started")
}
waitErr := s.wait()
var copyError error
for _ = range s.copyFuncs {
if err := <-s.errch; err != nil && copyError == nil {
copyError = err
}
}
for _, fd := range s.closeAfterWait {
fd.Close()
}
if waitErr != nil {
return waitErr
}
return copyError
}
func (s *Session) wait() error {
for {
switch msg := (<-s.msg).(type) {
case *channelRequestMsg:
// TODO(dfc) improve this behavior to match os.Waitmsg
switch msg.Request {
case "exit-status":
d := msg.RequestSpecificData
status := int(d[0])<<24 | int(d[1])<<16 | int(d[2])<<8 | int(d[3])
if status > 0 {
return fmt.Errorf("remote process exited with %d", status)
}
return nil
case "exit-signal":
// TODO(dfc) make a more readable error message
return fmt.Errorf("%v", msg.RequestSpecificData)
default:
return fmt.Errorf("wait: unexpected channel request: %v", msg)
}
default:
return fmt.Errorf("wait: unexpected packet %T received: %v", msg, msg)
}
}
panic("unreachable")
}
func (s *Session) stdin() error {
if s.Stdin == nil {
s.Stdin = new(bytes.Buffer)
}
s.copyFuncs = append(s.copyFuncs, func() error {
w := &chanWriter{
packetWriter: s,
peersId: s.peersId,
win: s.win,
}
_, err := io.Copy(w, s.Stdin)
if err1 := w.Close(); err == nil {
err = err1
}
return err
})
return nil
}
func (s *Session) stdout() error {
if s.Stdout == nil {
s.Stdout = ioutil.Discard
}
s.copyFuncs = append(s.copyFuncs, func() error {
r := &chanReader{
packetWriter: s,
peersId: s.peersId,
data: s.data,
}
_, err := io.Copy(s.Stdout, r)
return err
})
return nil
}
func (s *Session) stderr() error {
if s.Stderr == nil {
s.Stderr = ioutil.Discard
}
s.copyFuncs = append(s.copyFuncs, func() error {
r := &chanReader{
packetWriter: s,
peersId: s.peersId,
data: s.dataExt,
}
_, err := io.Copy(s.Stderr, r)
return err
})
return nil
}
// StdinPipe returns a pipe that will be connected to the
// remote command's standard input when the command starts.
func (s *Session) StdinPipe() (io.WriteCloser, error) {
if s.Stdin != nil {
return nil, errors.New("ssh: Stdin already set")
}
if s.started {
return nil, errors.New("ssh: StdinPipe after process started")
}
pr, pw := io.Pipe()
s.Stdin = pr
s.closeAfterWait = append(s.closeAfterWait, pr)
return pw, nil
}
// StdoutPipe returns a pipe that will be connected to the
// remote command's standard output when the command starts.
// There is a fixed amount of buffering that is shared between
// stdout and stderr streams. If the StdoutPipe reader is
// not serviced fast enought it may eventually cause the
// remote command to block.
func (s *Session) StdoutPipe() (io.ReadCloser, error) {
if s.Stdout != nil {
return nil, errors.New("ssh: Stdout already set")
}
if s.started {
return nil, errors.New("ssh: StdoutPipe after process started")
}
pr, pw := io.Pipe()
s.Stdout = pw
s.closeAfterWait = append(s.closeAfterWait, pw)
return pr, nil
}
// StderrPipe returns a pipe that will be connected to the
// remote command's standard error when the command starts.
// There is a fixed amount of buffering that is shared between
// stdout and stderr streams. If the StderrPipe reader is
// not serviced fast enought it may eventually cause the
// remote command to block.
func (s *Session) StderrPipe() (io.ReadCloser, error) {
if s.Stderr != nil {
return nil, errors.New("ssh: Stderr already set")
}
if s.started {
return nil, errors.New("ssh: StderrPipe after process started")
}
pr, pw := io.Pipe()
s.Stderr = pw
s.closeAfterWait = append(s.closeAfterWait, pw)
return pr, nil
}
// TODO(dfc) add Output and CombinedOutput helpers
// NewSession returns a new interactive session on the remote host.
func (c *ClientConn) NewSession() (*Session, error) {
ch, err := c.openChan("session")
if err != nil {
ch := c.newChan(c.transport)
if err := c.writePacket(marshal(msgChannelOpen, channelOpenMsg{
ChanType: "session",
PeersId: ch.id,
PeersWindow: 1 << 14,
MaxPacketSize: 1 << 15, // RFC 4253 6.1
})); err != nil {
c.chanlist.remove(ch.id)
return nil, err
}
return &Session{
Stdin: &chanWriter{
packetWriter: ch,
id: ch.id,
win: ch.win,
},
Stdout: &chanReader{
packetWriter: ch,
id: ch.id,
data: ch.data,
},
Stderr: &chanReader{
packetWriter: ch,
id: ch.id,
data: ch.dataExt,
},
clientChan: ch,
}, nil
// wait for response
msg := <-ch.msg
switch msg := msg.(type) {
case *channelOpenConfirmMsg:
ch.peersId = msg.MyId
ch.win <- int(msg.MyWindow)
return &Session{
clientChan: ch,
}, nil
case *channelOpenFailureMsg:
c.chanlist.remove(ch.id)
return nil, fmt.Errorf("ssh: channel open failed: %s", msg.Message)
}
c.chanlist.remove(ch.id)
return nil, fmt.Errorf("ssh: unexpected message %T: %v", msg, msg)
}

View File

@ -0,0 +1,149 @@
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package ssh
// Session tests.
import (
"bytes"
"io"
"testing"
)
// dial constructs a new test server and returns a *ClientConn.
func dial(t *testing.T) *ClientConn {
pw := password("tiger")
serverConfig.PasswordCallback = func(user, pass string) bool {
return user == "testuser" && pass == string(pw)
}
serverConfig.PubKeyCallback = nil
l, err := Listen("tcp", "127.0.0.1:0", serverConfig)
if err != nil {
t.Fatalf("unable to listen: %s", err)
}
go func() {
defer l.Close()
conn, err := l.Accept()
if err != nil {
t.Errorf("Unable to accept: %v", err)
return
}
defer conn.Close()
if err := conn.Handshake(); err != nil {
t.Errorf("Unable to handshake: %v", err)
return
}
for {
ch, err := conn.Accept()
if err == io.EOF {
return
}
if err != nil {
t.Errorf("Unable to accept incoming channel request: %v", err)
return
}
if ch.ChannelType() != "session" {
ch.Reject(UnknownChannelType, "unknown channel type")
continue
}
ch.Accept()
go func() {
defer ch.Close()
// this string is returned to stdout
shell := NewServerShell(ch, "golang")
shell.ReadLine()
type exitMsg struct {
PeersId uint32
Request string
WantReply bool
Status uint32
}
// TODO(dfc) casting to the concrete type should not be
// necessary to send a packet.
msg := exitMsg{
PeersId: ch.(*channel).theirId,
Request: "exit-status",
WantReply: false,
Status: 0,
}
ch.(*channel).serverConn.writePacket(marshal(msgChannelRequest, msg))
}()
}
t.Log("done")
}()
config := &ClientConfig{
User: "testuser",
Auth: []ClientAuth{
ClientAuthPassword(pw),
},
}
c, err := Dial("tcp", l.Addr().String(), config)
if err != nil {
t.Fatalf("unable to dial remote side: %s", err)
}
return c
}
// Test a simple string is returned to session.Stdout.
func TestSessionShell(t *testing.T) {
conn := dial(t)
defer conn.Close()
session, err := conn.NewSession()
if err != nil {
t.Fatalf("Unable to request new session: %s", err)
}
defer session.Close()
stdout := new(bytes.Buffer)
session.Stdout = stdout
if err := session.Shell(); err != nil {
t.Fatalf("Unable to execute command: %s", err)
}
if err := session.Wait(); err != nil {
t.Fatalf("Remote command did not exit cleanly: %s", err)
}
actual := stdout.String()
if actual != "golang" {
t.Fatalf("Remote shell did not return expected string: expected=golang, actual=%s", actual)
}
}
// TODO(dfc) add support for Std{in,err}Pipe when the Server supports it.
// Test a simple string is returned via StdoutPipe.
func TestSessionStdoutPipe(t *testing.T) {
conn := dial(t)
defer conn.Close()
session, err := conn.NewSession()
if err != nil {
t.Fatalf("Unable to request new session: %s", err)
}
defer session.Close()
stdout, err := session.StdoutPipe()
if err != nil {
t.Fatalf("Unable to request StdoutPipe(): %v", err)
}
var buf bytes.Buffer
if err := session.Shell(); err != nil {
t.Fatalf("Unable to execute command: %s", err)
}
done := make(chan bool, 1)
go func() {
if _, err := io.Copy(&buf, stdout); err != nil {
t.Errorf("Copy of stdout failed: %v", err)
}
done <- true
}()
if err := session.Wait(); err != nil {
t.Fatalf("Remote command did not exit cleanly: %s", err)
}
<-done
actual := buf.String()
if actual != "golang" {
t.Fatalf("Remote shell did not return expected string: expected=golang, actual=%s", actual)
}
}

View File

@ -86,12 +86,12 @@ func (c *ClientConn) dial(laddr string, lport int, raddr string, rport int) (*tc
clientChan: ch,
Reader: &chanReader{
packetWriter: ch,
id: ch.id,
peersId: ch.peersId,
data: ch.data,
},
Writer: &chanWriter{
packetWriter: ch,
id: ch.id,
peersId: ch.peersId,
win: ch.win,
},
}, nil

View File

@ -0,0 +1,59 @@
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package ssh
// direct-tcpip functional tests
import (
"net"
"net/http"
"testing"
)
func TestTCPIPHTTP(t *testing.T) {
if *sshuser == "" {
t.Log("ssh.user not defined, skipping test")
return
}
// google.com will generate at least one redirect, possibly three
// depending on your location.
doTest(t, "http://google.com")
}
func TestTCPIPHTTPS(t *testing.T) {
if *sshuser == "" {
t.Log("ssh.user not defined, skipping test")
return
}
doTest(t, "https://encrypted.google.com/")
}
func doTest(t *testing.T, url string) {
config := &ClientConfig{
User: *sshuser,
Auth: []ClientAuth{
ClientAuthPassword(password(*sshpass)),
},
}
conn, err := Dial("tcp", "localhost:22", config)
if err != nil {
t.Fatalf("Unable to connect: %s", err)
}
defer conn.Close()
tr := &http.Transport{
Dial: func(n, addr string) (net.Conn, error) {
return conn.Dial(n, addr)
},
}
client := &http.Client{
Transport: tr,
}
resp, err := client.Get(url)
if err != nil {
t.Fatalf("unable to proxy: %s", err)
}
// got a body without error
t.Log(resp)
}

View File

@ -123,7 +123,7 @@ func (r *reader) readOnePacket() ([]byte, error) {
if r.mac != nil {
r.mac.Write(packet[:length-1])
if subtle.ConstantTimeCompare(r.mac.Sum(), mac) != 1 {
if subtle.ConstantTimeCompare(r.mac.Sum(nil), mac) != 1 {
return nil, errors.New("ssh: MAC failure")
}
}
@ -201,7 +201,7 @@ func (w *writer) writePacket(packet []byte) error {
}
if w.mac != nil {
if _, err := w.Write(w.mac.Sum()); err != nil {
if _, err := w.Write(w.mac.Sum(nil)); err != nil {
return err
}
}
@ -297,7 +297,7 @@ func generateKeyMaterial(out, tag []byte, K, H, sessionId []byte, h hash.Hash) {
h.Write(digestsSoFar)
}
digest := h.Sum()
digest := h.Sum(nil)
n := copy(out, digest)
out = out[n:]
if len(out) > 0 {
@ -317,9 +317,9 @@ func (t truncatingMAC) Write(data []byte) (int, error) {
return t.hmac.Write(data)
}
func (t truncatingMAC) Sum() []byte {
digest := t.hmac.Sum()
return digest[:t.length]
func (t truncatingMAC) Sum(in []byte) []byte {
out := t.hmac.Sum(in)
return out[:len(in)+t.length]
}
func (t truncatingMAC) Reset() {

View File

@ -202,7 +202,7 @@ func TestCheck(t *testing.T) {
// For easy debugging w/o changing the testing code,
// if there is a local test file, only test that file.
const testfile = "test.go"
if fi, err := os.Stat(testfile); err == nil && fi.IsRegular() {
if fi, err := os.Stat(testfile); err == nil && !fi.IsDir() {
fmt.Printf("WARNING: Testing only %s (remove it to run all tests)\n", testfile)
check(t, testfile, []string{testfile})
return

View File

@ -59,7 +59,7 @@ func findPkg(path string) (filename, id string) {
// try extensions
for _, ext := range pkgExts {
filename = noext + ext
if f, err := os.Stat(filename); err == nil && f.IsRegular() {
if f, err := os.Stat(filename); err == nil && !f.IsDir() {
return
}
}

View File

@ -58,32 +58,32 @@ func testPath(t *testing.T, path string) bool {
return true
}
const maxTime = 3e9 // maximum allotted testing time in ns
const maxTime = 3 * time.Second
func testDir(t *testing.T, dir string, endTime int64) (nimports int) {
func testDir(t *testing.T, dir string, endTime time.Time) (nimports int) {
dirname := filepath.Join(pkgRoot, dir)
list, err := ioutil.ReadDir(dirname)
if err != nil {
t.Errorf("testDir(%s): %s", dirname, err)
}
for _, f := range list {
if time.Nanoseconds() >= endTime {
if time.Now().After(endTime) {
t.Log("testing time used up")
return
}
switch {
case f.IsRegular():
case !f.IsDir():
// try extensions
for _, ext := range pkgExts {
if strings.HasSuffix(f.Name, ext) {
name := f.Name[0 : len(f.Name)-len(ext)] // remove extension
if strings.HasSuffix(f.Name(), ext) {
name := f.Name()[0 : len(f.Name())-len(ext)] // remove extension
if testPath(t, filepath.Join(dir, name)) {
nimports++
}
}
}
case f.IsDirectory():
nimports += testDir(t, filepath.Join(dir, f.Name), endTime)
case f.IsDir():
nimports += testDir(t, filepath.Join(dir, f.Name()), endTime)
}
}
return
@ -96,6 +96,6 @@ func TestGcImport(t *testing.T) {
if testPath(t, "./testdata/exports") {
nimports++
}
nimports += testDir(t, "", time.Nanoseconds()+maxTime) // installed packages
nimports += testDir(t, "", time.Now().Add(maxTime)) // installed packages
t.Logf("tested %d imports", nimports)
}

View File

@ -47,8 +47,10 @@ func TestFmtInterface(t *testing.T) {
const b32 uint32 = 1<<32 - 1
const b64 uint64 = 1<<64 - 1
var array = []int{1, 2, 3, 4, 5}
var iarray = []interface{}{1, "hello", 2.5, nil}
var array = [5]int{1, 2, 3, 4, 5}
var iarray = [4]interface{}{1, "hello", 2.5, nil}
var slice = array[:]
var islice = iarray[:]
type A struct {
i int
@ -327,6 +329,12 @@ var fmttests = []struct {
{"%v", &array, "&[1 2 3 4 5]"},
{"%v", &iarray, "&[1 hello 2.5 <nil>]"},
// slices
{"%v", slice, "[1 2 3 4 5]"},
{"%v", islice, "[1 hello 2.5 <nil>]"},
{"%v", &slice, "&[1 2 3 4 5]"},
{"%v", &islice, "&[1 hello 2.5 <nil>]"},
// complexes with %v
{"%v", 1 + 2i, "(1+2i)"},
{"%v", complex64(1 + 2i), "(1+2i)"},
@ -359,6 +367,10 @@ var fmttests = []struct {
{"%#v", SI{}, `fmt_test.SI{I:interface {}(nil)}`},
{"%#v", []int(nil), `[]int(nil)`},
{"%#v", []int{}, `[]int{}`},
{"%#v", array, `[5]int{1, 2, 3, 4, 5}`},
{"%#v", &array, `&[5]int{1, 2, 3, 4, 5}`},
{"%#v", iarray, `[4]interface {}{1, "hello", 2.5, interface {}(nil)}`},
{"%#v", &iarray, `&[4]interface {}{1, "hello", 2.5, interface {}(nil)}`},
{"%#v", map[int]byte(nil), `map[int] uint8(nil)`},
{"%#v", map[int]byte{}, `map[int] uint8{}`},

View File

@ -877,7 +877,7 @@ BigSwitch:
}
if goSyntax {
p.buf.WriteString(value.Type().String())
if f.IsNil() {
if f.Kind() == reflect.Slice && f.IsNil() {
p.buf.WriteString("(nil)")
break
}

Some files were not shown because too many files have changed in this diff Show More