summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/cmd/dist/build.c13
-rw-r--r--src/cmd/gc/esc.c70
-rw-r--r--src/cmd/go/doc.go14
-rw-r--r--src/cmd/go/get.go78
-rw-r--r--src/cmd/go/tag_test.go71
-rw-r--r--src/cmd/go/vcs.go8
-rw-r--r--src/cmd/godoc/README.godoc-app29
-rw-r--r--src/cmd/godoc/main.go2
-rw-r--r--src/cmd/godoc/setup-godoc-app.bash10
-rw-r--r--src/pkg/crypto/tls/cipher_suites.go4
-rw-r--r--src/pkg/crypto/tls/handshake_server.go29
-rw-r--r--src/pkg/crypto/tls/handshake_server_test.go158
-rw-r--r--src/pkg/crypto/tls/key_agreement.go12
-rw-r--r--src/pkg/database/sql/sql.go3
-rw-r--r--src/pkg/encoding/ascii85/ascii85.go1
-rw-r--r--src/pkg/encoding/ascii85/ascii85_test.go5
-rw-r--r--src/pkg/encoding/asn1/asn1.go16
-rw-r--r--src/pkg/encoding/asn1/asn1_test.go6
-rw-r--r--src/pkg/encoding/base64/base64.go5
-rw-r--r--src/pkg/encoding/base64/base64_test.go3
-rw-r--r--src/pkg/encoding/json/encode.go9
-rw-r--r--src/pkg/encoding/json/tagkey_test.go5
-rw-r--r--src/pkg/encoding/pem/pem.go9
-rw-r--r--src/pkg/encoding/pem/pem_test.go22
-rw-r--r--src/pkg/flag/flag.go10
-rw-r--r--src/pkg/fmt/fmt_test.go3
-rw-r--r--src/pkg/fmt/format.go2
-rw-r--r--src/pkg/html/escape.go8
-rw-r--r--src/pkg/mime/mediatype.go2
-rw-r--r--src/pkg/mime/mediatype_test.go32
-rw-r--r--src/pkg/mime/multipart/multipart.go8
-rw-r--r--src/pkg/mime/multipart/multipart_test.go50
-rwxr-xr-xsrc/pkg/mime/multipart/testdata/nested-mime29
-rw-r--r--src/pkg/net/http/example_test.go5
-rw-r--r--src/pkg/net/http/server.go6
-rw-r--r--src/pkg/net/iprawsock_posix.go4
-rw-r--r--src/pkg/net/tcpsock_posix.go6
-rw-r--r--src/pkg/net/udpsock_posix.go4
-rw-r--r--src/pkg/net/unixsock_posix.go6
-rw-r--r--src/pkg/net/url/url.go8
-rw-r--r--src/pkg/net/url/url_test.go4
-rw-r--r--src/pkg/os/error_windows.go2
-rw-r--r--src/pkg/path/filepath/path.go3
-rw-r--r--src/pkg/path/path.go2
-rw-r--r--src/pkg/reflect/all_test.go5
-rw-r--r--src/pkg/reflect/type.go1
-rw-r--r--src/pkg/runtime/extern.go3
-rw-r--r--src/pkg/text/template/exec.go7
-rw-r--r--src/pkg/text/template/exec_test.go8
-rw-r--r--src/pkg/text/template/funcs.go2
-rw-r--r--src/pkg/time/format.go6
-rw-r--r--src/pkg/time/time.go6
-rw-r--r--src/pkg/time/time_test.go3
-rw-r--r--src/pkg/time/zoneinfo_windows.go3
-rwxr-xr-xsrc/sudo.bash7
55 files changed, 568 insertions, 259 deletions
diff --git a/src/cmd/dist/build.c b/src/cmd/dist/build.c
index 567c9f336..3ef65f85d 100644
--- a/src/cmd/dist/build.c
+++ b/src/cmd/dist/build.c
@@ -19,7 +19,7 @@ char *goos;
char *goroot = GOROOT_FINAL;
char *goroot_final = GOROOT_FINAL;
char *workdir;
-char *tooldir;
+char *tooldir;
char *gochar;
char *goversion;
char *slash; // / for unix, \ for windows
@@ -1462,7 +1462,7 @@ void
cmdbanner(int argc, char **argv)
{
char *pathsep;
- Buf b, b1, search;
+ Buf b, b1, search, path;
ARGBEGIN{
case 'v':
@@ -1478,6 +1478,7 @@ cmdbanner(int argc, char **argv)
binit(&b);
binit(&b1);
binit(&search);
+ binit(&path);
xprintf("\n");
xprintf("---\n");
@@ -1495,9 +1496,10 @@ cmdbanner(int argc, char **argv)
xprintf("*** You need to add %s to your PATH.\n", gobin);
if(streq(gohostos, "darwin")) {
- xprintf("\n"
- "On OS X the debuggers must be installed setgid procmod.\n"
- "Read and run ./sudo.bash to install the debuggers.\n");
+ if(isfile(bpathf(&path, "%s/cov", tooldir)))
+ xprintf("\n"
+ "On OS X the debuggers must be installed setgid procmod.\n"
+ "Read and run ./sudo.bash to install the debuggers.\n");
}
if(!streq(goroot_final, goroot)) {
@@ -1509,6 +1511,7 @@ cmdbanner(int argc, char **argv)
bfree(&b);
bfree(&b1);
bfree(&search);
+ bfree(&path);
}
// Version prints the Go version.
diff --git a/src/cmd/gc/esc.c b/src/cmd/gc/esc.c
index 2614b5f35..8a265ce59 100644
--- a/src/cmd/gc/esc.c
+++ b/src/cmd/gc/esc.c
@@ -131,7 +131,12 @@ escfunc(Node *func)
}
// walk will take the address of cvar->closure later and assign it to cvar.
- // handle that here by linking a fake oaddr node directly to the closure.
+ // linking a fake oaddr node directly to the closure handles the case
+ // of the closure itself leaking. Following the flow of the value to th
+ // paramref is done in escflow, because if we did that here, it would look
+ // like the original is assigned out of its loop depth, whereas it's just
+ // assigned to something in an inner function. A paramref itself is never
+ // moved to the heap, only its original.
for(ll=curfn->cvars; ll; ll=ll->next) {
if(ll->n->op == OXXX) // see dcl.c:398
continue;
@@ -221,16 +226,19 @@ esc(Node *n)
if(n->op == OFOR || n->op == ORANGE)
loopdepth++;
- esc(n->left);
- esc(n->right);
- esc(n->ntest);
- esc(n->nincr);
- esclist(n->ninit);
- esclist(n->nbody);
- esclist(n->nelse);
- esclist(n->list);
- esclist(n->rlist);
-
+ if(n->op == OCLOSURE) {
+ escfunc(n);
+ } else {
+ esc(n->left);
+ esc(n->right);
+ esc(n->ntest);
+ esc(n->nincr);
+ esclist(n->ninit);
+ esclist(n->nbody);
+ esclist(n->nelse);
+ esclist(n->list);
+ esclist(n->rlist);
+ }
if(n->op == OFOR || n->op == ORANGE)
loopdepth--;
@@ -379,8 +387,8 @@ esc(Node *n)
}
break;
- case OADDR:
case OCLOSURE:
+ case OADDR:
case OMAKECHAN:
case OMAKEMAP:
case OMAKESLICE:
@@ -407,8 +415,8 @@ escassign(Node *dst, Node *src)
return;
if(debug['m'] > 1)
- print("%L:[%d] %S escassign: %hN = %hN\n", lineno, loopdepth,
- (curfn && curfn->nname) ? curfn->nname->sym : S, dst, src);
+ print("%L:[%d] %S escassign: %hN(%hJ) = %hN(%hJ)\n", lineno, loopdepth,
+ (curfn && curfn->nname) ? curfn->nname->sym : S, dst, dst, src, src);
setlineno(dst);
@@ -467,7 +475,11 @@ escassign(Node *dst, Node *src)
case OARRAYLIT:
case OMAPLIT:
case OSTRUCTLIT:
- // loopdepth was set in the defining statement or function header
+ case OMAKECHAN:
+ case OMAKEMAP:
+ case OMAKESLICE:
+ case ONEW:
+ case OCLOSURE:
escflows(dst, src);
break;
@@ -500,18 +512,6 @@ escassign(Node *dst, Node *src)
escassign(dst, src->left);
break;
- case OMAKECHAN:
- case OMAKEMAP:
- case OMAKESLICE:
- case ONEW:
- escflows(dst, src);
- break;
-
- case OCLOSURE:
- escflows(dst, src);
- escfunc(src);
- break;
-
case OADD:
case OSUB:
case OOR:
@@ -543,7 +543,7 @@ escassign(Node *dst, Node *src)
// This is a bit messier than fortunate, pulled out of escassign's big
// switch for clarity. We either have the paramnodes, which may be
// connected to other things throug flows or we have the parameter type
-// nodes, which may be marked 'n(ofloworescape)'. Navigating the ast is slightly
+// nodes, which may be marked "noescape". Navigating the ast is slightly
// different for methods vs plain functions and for imported vs
// this-package
static void
@@ -711,8 +711,8 @@ escwalk(int level, Node *dst, Node *src)
src->walkgen = walkgen;
if(debug['m']>1)
- print("escwalk: level:%d depth:%d %.*s %hN scope:%S[%d]\n",
- level, pdepth, pdepth, "\t\t\t\t\t\t\t\t\t\t", src,
+ print("escwalk: level:%d depth:%d %.*s %hN(%hJ) scope:%S[%d]\n",
+ level, pdepth, pdepth, "\t\t\t\t\t\t\t\t\t\t", src, src,
(src->curfn && src->curfn->nname) ? src->curfn->nname->sym : S, src->escloopdepth);
pdepth++;
@@ -726,6 +726,16 @@ escwalk(int level, Node *dst, Node *src)
if(debug['m'])
warnl(src->lineno, "leaking param: %hN", src);
}
+ // handle the missing flow ref <- orig
+ // a paramref is automagically dereferenced, and taking its
+ // address produces the address of the original, so all we have to do here
+ // is keep track of the value flow, so level is unchanged.
+ // alternatively, we could have substituted PPARAMREFs with their ->closure in esc/escassign/flow,
+ if(src->class == PPARAMREF) {
+ if(leaks && debug['m'])
+ warnl(src->lineno, "leaking closure reference %hN", src);
+ escwalk(level, dst, src->closure);
+ }
break;
case OPTRLIT:
diff --git a/src/cmd/go/doc.go b/src/cmd/go/doc.go
index 4bfd5236d..32ede3964 100644
--- a/src/cmd/go/doc.go
+++ b/src/cmd/go/doc.go
@@ -227,15 +227,11 @@ The -u flag instructs get to use the network to update the named packages
and their dependencies. By default, get uses the network to check out
missing packages but does not use it to look for updates to existing packages.
-When checking out or updating a package, get looks for a branch or
-tag that matches the locally installed version of Go. If the local
-version "is release.rNN", it searches for "go.rNN". (For an
-installation using Go version "weekly.YYYY-MM-DD", it searches for a
-package version labeled "go.YYYY-MM-DD".) If the desired version
-cannot be found but others exist with labels in the correct format,
-get retrieves the most recent version before the desired label.
-Finally, if all else fails it retrieves the most recent version of
-the package.
+When checking out or updating a package, get looks for a branch or tag
+that matches the locally installed version of Go. The most important
+rule is that if the local installation is running version "go1", get
+searches for a branch or tag named "go1". If no such version exists it
+retrieves the most recent version of the package.
For more about specifying packages, see 'go help packages'.
diff --git a/src/cmd/go/get.go b/src/cmd/go/get.go
index f70b6761d..fe45697e2 100644
--- a/src/cmd/go/get.go
+++ b/src/cmd/go/get.go
@@ -37,15 +37,11 @@ The -u flag instructs get to use the network to update the named packages
and their dependencies. By default, get uses the network to check out
missing packages but does not use it to look for updates to existing packages.
-When checking out or updating a package, get looks for a branch or
-tag that matches the locally installed version of Go. If the local
-version "is release.rNN", it searches for "go.rNN". (For an
-installation using Go version "weekly.YYYY-MM-DD", it searches for a
-package version labeled "go.YYYY-MM-DD".) If the desired version
-cannot be found but others exist with labels in the correct format,
-get retrieves the most recent version before the desired label.
-Finally, if all else fails it retrieves the most recent version of
-the package.
+When checking out or updating a package, get looks for a branch or tag
+that matches the locally installed version of Go. The most important
+rule is that if the local installation is running version "go1", get
+searches for a branch or tag named "go1". If no such version exists it
+retrieves the most recent version of the package.
For more about specifying packages, see 'go help packages'.
@@ -339,56 +335,32 @@ var goTag = regexp.MustCompile(
// Version "goX" (or "goX.Y" or "goX.Y.Z") matches tags of the same form.
// Version "release.rN" matches tags of the form "go.rN" (N being a floating-point number).
// Version "weekly.YYYY-MM-DD" matches tags like "go.weekly.YYYY-MM-DD".
+//
+// NOTE(rsc): Eventually we will need to decide on some logic here.
+// For now, there is only "go1". This matches the docs in go help get.
func selectTag(goVersion string, tags []string) (match string) {
- const rPrefix = "release.r"
- if strings.HasPrefix(goVersion, rPrefix) {
- p := "go.r"
- v, err := strconv.ParseFloat(goVersion[len(rPrefix):], 64)
- if err != nil {
- return ""
- }
- var matchf float64
- for _, t := range tags {
- if !strings.HasPrefix(t, p) {
- continue
- }
- tf, err := strconv.ParseFloat(t[len(p):], 64)
- if err != nil {
- continue
- }
- if matchf < tf && tf <= v {
- match, matchf = t, tf
- }
+ for _, t := range tags {
+ if t == "go1" {
+ return "go1"
}
}
-
- const wPrefix = "weekly."
- if strings.HasPrefix(goVersion, wPrefix) {
- p := "go.weekly."
- v := goVersion[len(wPrefix):]
- for _, t := range tags {
- if !strings.HasPrefix(t, p) {
- continue
- }
- if match < t && t[len(p):] <= v {
- match = t
- }
- }
- }
-
- if goTag.MatchString(goVersion) {
- v := goVersion
- for _, t := range tags {
- if !goTag.MatchString(t) {
- continue
- }
- if cmpGoVersion(match, t) < 0 && cmpGoVersion(t, v) <= 0 {
- match = t
+ return ""
+
+ /*
+ if goTag.MatchString(goVersion) {
+ v := goVersion
+ for _, t := range tags {
+ if !goTag.MatchString(t) {
+ continue
+ }
+ if cmpGoVersion(match, t) < 0 && cmpGoVersion(t, v) <= 0 {
+ match = t
+ }
}
}
- }
- return match
+ return match
+ */
}
// cmpGoVersion returns -1, 0, +1 reporting whether
diff --git a/src/cmd/go/tag_test.go b/src/cmd/go/tag_test.go
index 556a84a8e..ffe218c7b 100644
--- a/src/cmd/go/tag_test.go
+++ b/src/cmd/go/tag_test.go
@@ -50,41 +50,44 @@ var selectTagTests = []struct {
version string
selected string
}{
- {"release.r57", ""},
- {"release.r58.2", "go.r58.1"},
- {"release.r59", "go.r59"},
- {"release.r59.1", "go.r59.1"},
- {"release.r60", "go.r59.1"},
- {"release.r60.1", "go.r59.1"},
- {"release.r61", "go.r61"},
- {"release.r66", "go.r61.1"},
- {"weekly.2010-01-01", ""},
- {"weekly.2010-01-02", "go.weekly.2010-01-02"},
- {"weekly.2010-01-02.1", "go.weekly.2010-01-02"},
- {"weekly.2010-01-03", "go.weekly.2010-01-02"},
- {"weekly.2011-10-12", "go.weekly.2011-10-12"},
- {"weekly.2011-10-12.1", "go.weekly.2011-10-12.1"},
- {"weekly.2011-10-13", "go.weekly.2011-10-12.1"},
- {"weekly.2011-10-14", "go.weekly.2011-10-14"},
- {"weekly.2011-10-14.1", "go.weekly.2011-10-14"},
- {"weekly.2011-11-01", "go.weekly.2011-11-01"},
- {"weekly.2014-01-01", "go.weekly.2011-11-01"},
- {"weekly.3000-01-01", "go.weekly.2011-11-01"},
- {"go1", "go1"},
- {"go1.1", "go1.0.1"},
- {"go1.998", "go1.9.2"},
- {"go1.1000", "go1.999"},
- {"go6", "go5"},
+ /*
+ {"release.r57", ""},
+ {"release.r58.2", "go.r58.1"},
+ {"release.r59", "go.r59"},
+ {"release.r59.1", "go.r59.1"},
+ {"release.r60", "go.r59.1"},
+ {"release.r60.1", "go.r59.1"},
+ {"release.r61", "go.r61"},
+ {"release.r66", "go.r61.1"},
+ {"weekly.2010-01-01", ""},
+ {"weekly.2010-01-02", "go.weekly.2010-01-02"},
+ {"weekly.2010-01-02.1", "go.weekly.2010-01-02"},
+ {"weekly.2010-01-03", "go.weekly.2010-01-02"},
+ {"weekly.2011-10-12", "go.weekly.2011-10-12"},
+ {"weekly.2011-10-12.1", "go.weekly.2011-10-12.1"},
+ {"weekly.2011-10-13", "go.weekly.2011-10-12.1"},
+ {"weekly.2011-10-14", "go.weekly.2011-10-14"},
+ {"weekly.2011-10-14.1", "go.weekly.2011-10-14"},
+ {"weekly.2011-11-01", "go.weekly.2011-11-01"},
+ {"weekly.2014-01-01", "go.weekly.2011-11-01"},
+ {"weekly.3000-01-01", "go.weekly.2011-11-01"},
+ {"go1", "go1"},
+ {"go1.1", "go1.0.1"},
+ {"go1.998", "go1.9.2"},
+ {"go1.1000", "go1.999"},
+ {"go6", "go5"},
- // faulty versions:
- {"release.f00", ""},
- {"weekly.1999-01-01", ""},
- {"junk", ""},
- {"", ""},
- {"go2x", ""},
- {"go200000000000", ""},
- {"go2.", ""},
- {"go2.0", ""},
+ // faulty versions:
+ {"release.f00", ""},
+ {"weekly.1999-01-01", ""},
+ {"junk", ""},
+ {"", ""},
+ {"go2x", ""},
+ {"go200000000000", ""},
+ {"go2.", ""},
+ {"go2.0", ""},
+ */
+ {"anything", "go1"},
}
func TestSelectTag(t *testing.T) {
diff --git a/src/cmd/go/vcs.go b/src/cmd/go/vcs.go
index 5f63f8b56..1c121672f 100644
--- a/src/cmd/go/vcs.go
+++ b/src/cmd/go/vcs.go
@@ -422,11 +422,15 @@ func repoRootForImportPathStatic(importPath, scheme string) (*repoRoot, error) {
func repoRootForImportDynamic(importPath string) (*repoRoot, error) {
slash := strings.Index(importPath, "/")
if slash < 0 {
- return nil, fmt.Errorf("missing / in import %q", importPath)
+ return nil, errors.New("import path doesn't contain a slash")
+ }
+ host := importPath[:slash]
+ if !strings.Contains(host, ".") {
+ return nil, errors.New("import path doesn't contain a hostname")
}
urlStr, body, err := httpsOrHTTP(importPath)
if err != nil {
- return nil, fmt.Errorf("http/https fetch for import %q: %v", importPath, err)
+ return nil, fmt.Errorf("http/https fetch: %v", err)
}
defer body.Close()
metaImport, err := matchGoImport(parseMetaGoImports(body), importPath)
diff --git a/src/cmd/godoc/README.godoc-app b/src/cmd/godoc/README.godoc-app
index 88cfee41e..cff7d387c 100644
--- a/src/cmd/godoc/README.godoc-app
+++ b/src/cmd/godoc/README.godoc-app
@@ -8,8 +8,8 @@ godoc on appengine
Prerequisites
-------------
-* Go appengine SDK 1.5.5 - 2011-10-11
- http://code.google.com/appengine/downloads.html#Google_App_Engine_SDK_for_Go
+* Go appengine SDK
+ https://developers.google.com/appengine/downloads#Google_App_Engine_SDK_for_Go
* Go sources at tip under $GOROOT
@@ -23,10 +23,6 @@ Directory structure
* $APPDIR contains the following entries (this may change depending on
app-engine release and version of godoc):
- alt/
- encoding/binary/
- go/*
- index/suffixarray/
app.yaml
godoc.zip
godoc/
@@ -36,31 +32,16 @@ Directory structure
For instance:
application: godoc-app
- version: 1-5-5
+ version: 1
runtime: go
- api_version: 3
+ api_version: go1
handlers:
- url: /.*
script: _go_app
* The godoc/ directory contains a copy of the files under $GOROOT/src/cmd/godoc
- with modifications:
-
- - doc.go is excluded (it belongs to pseudo-package ̉documentationÓ)
- - main.go is excluded (appinit.go is taking its place)
-
- Additional manual modifications are required to refer to the alt/ packages
- where the app-engine library is not up-to-date with the godoc version.
-
-* The alt/ directory contains up-to-date copies of Go packages that a tip-based
- godoc is dependent on but which do not yet exist in the current app-engine SDK.
- At the time of this writing (10/14/2011) this is the entire go directory tree
- (for the missing FileSet serialization code in go/token) as well as the
- index/suffixarray package (for the missing suffix array serialization code).
- The latest (alt/)index/suffixarray package internally requires the latest
- version of encoding/binary, which is why it also needs to be present under
- alt/.
+ with doc.go excluded (it belongs to pseudo-package "documentation")
Configuring and running godoc
diff --git a/src/cmd/godoc/main.go b/src/cmd/godoc/main.go
index da4fc63b5..2e2889ed3 100644
--- a/src/cmd/godoc/main.go
+++ b/src/cmd/godoc/main.go
@@ -23,6 +23,8 @@
// godoc crypto/block Cipher NewCMAC
// - prints doc for Cipher and NewCMAC in package crypto/block
+// +build !appengine
+
package main
import (
diff --git a/src/cmd/godoc/setup-godoc-app.bash b/src/cmd/godoc/setup-godoc-app.bash
index 755d965d5..b8dc4dcf9 100644
--- a/src/cmd/godoc/setup-godoc-app.bash
+++ b/src/cmd/godoc/setup-godoc-app.bash
@@ -44,8 +44,8 @@ getArgs() {
if [ ! -d $GOROOT ]; then
error "$GOROOT is not a directory"
fi
- if [ ! -x $GOROOT/src/cmd/godoc/godoc ]; then
- error "$GOROOT/src/cmd/godoc/godoc does not exist or is not executable"
+ if [ ! -x $GOROOT/bin/godoc ]; then
+ error "$GOROOT/bin/godoc does not exist or is not executable"
fi
if [ ! -d $APPDIR ]; then
error "$APPDIR is not a directory"
@@ -72,15 +72,15 @@ cleanup() {
makeZipfile() {
echo "*** make $APPDIR/$ZIPFILE"
- zip -q -r $APPDIR/$ZIPFILE $GOROOT -i \*.go -i \*.html -i \*.css -i \*.js -i \*.txt -i \*.c -i \*.h -i \*.s -i \*.png -i \*.jpg -i \*.sh -i \*.ico
+ zip -q -r $APPDIR/$ZIPFILE $GOROOT -i \*.go -i \*.html -i \*.xml -i \*.css -i \*.js -i \*.txt -i \*.c -i \*.h -i \*.s -i \*.png -i \*.jpg -i \*.sh -i \*.ico
}
makeIndexfile() {
echo "*** make $APPDIR/$INDEXFILE"
OUT=/tmp/godoc.out
- $GOROOT/src/cmd/godoc/godoc -write_index -index_files=$APPDIR/$INDEXFILE -zip=$APPDIR/$ZIPFILE 2> $OUT
+ $GOROOT/bin/godoc -write_index -index_files=$APPDIR/$INDEXFILE -zip=$APPDIR/$ZIPFILE 2> $OUT
if [ $? != 0 ]; then
- error "$GOROOT/src/cmd/godoc/godoc failed - see $OUT for details"
+ error "$GOROOT/bin/godoc failed - see $OUT for details"
fi
}
diff --git a/src/pkg/crypto/tls/cipher_suites.go b/src/pkg/crypto/tls/cipher_suites.go
index 00695e7d1..5039f319f 100644
--- a/src/pkg/crypto/tls/cipher_suites.go
+++ b/src/pkg/crypto/tls/cipher_suites.go
@@ -23,8 +23,8 @@ type keyAgreement interface {
// In the case that the key agreement protocol doesn't use a
// ServerKeyExchange message, generateServerKeyExchange can return nil,
// nil.
- generateServerKeyExchange(*Config, *clientHelloMsg, *serverHelloMsg) (*serverKeyExchangeMsg, error)
- processClientKeyExchange(*Config, *clientKeyExchangeMsg, uint16) ([]byte, error)
+ generateServerKeyExchange(*Config, *Certificate, *clientHelloMsg, *serverHelloMsg) (*serverKeyExchangeMsg, error)
+ processClientKeyExchange(*Config, *Certificate, *clientKeyExchangeMsg, uint16) ([]byte, error)
// On the client side, the next two methods are called in order.
diff --git a/src/pkg/crypto/tls/handshake_server.go b/src/pkg/crypto/tls/handshake_server.go
index 77e56a754..76adc540c 100644
--- a/src/pkg/crypto/tls/handshake_server.go
+++ b/src/pkg/crypto/tls/handshake_server.go
@@ -112,37 +112,38 @@ FindCipherSuite:
hello.nextProtoNeg = true
hello.nextProtos = config.NextProtos
}
- if clientHello.ocspStapling && len(config.Certificates[0].OCSPStaple) > 0 {
- hello.ocspStapling = true
- }
-
- finishedHash.Write(hello.marshal())
- c.writeRecord(recordTypeHandshake, hello.marshal())
if len(config.Certificates) == 0 {
return c.sendAlert(alertInternalError)
}
-
- certMsg := new(certificateMsg)
+ cert := &config.Certificates[0]
if len(clientHello.serverName) > 0 {
c.serverName = clientHello.serverName
- certMsg.certificates = config.getCertificateForName(clientHello.serverName).Certificate
- } else {
- certMsg.certificates = config.Certificates[0].Certificate
+ cert = config.getCertificateForName(clientHello.serverName)
}
+
+ if clientHello.ocspStapling && len(cert.OCSPStaple) > 0 {
+ hello.ocspStapling = true
+ }
+
+ finishedHash.Write(hello.marshal())
+ c.writeRecord(recordTypeHandshake, hello.marshal())
+
+ certMsg := new(certificateMsg)
+ certMsg.certificates = cert.Certificate
finishedHash.Write(certMsg.marshal())
c.writeRecord(recordTypeHandshake, certMsg.marshal())
if hello.ocspStapling {
certStatus := new(certificateStatusMsg)
certStatus.statusType = statusTypeOCSP
- certStatus.response = config.Certificates[0].OCSPStaple
+ certStatus.response = cert.OCSPStaple
finishedHash.Write(certStatus.marshal())
c.writeRecord(recordTypeHandshake, certStatus.marshal())
}
keyAgreement := suite.ka()
- skx, err := keyAgreement.generateServerKeyExchange(config, clientHello, hello)
+ skx, err := keyAgreement.generateServerKeyExchange(config, cert, clientHello, hello)
if err != nil {
c.sendAlert(alertHandshakeFailure)
return err
@@ -288,7 +289,7 @@ FindCipherSuite:
finishedHash.Write(certVerify.marshal())
}
- preMasterSecret, err := keyAgreement.processClientKeyExchange(config, ckx, c.vers)
+ preMasterSecret, err := keyAgreement.processClientKeyExchange(config, cert, ckx, c.vers)
if err != nil {
c.sendAlert(alertHandshakeFailure)
return err
diff --git a/src/pkg/crypto/tls/handshake_server_test.go b/src/pkg/crypto/tls/handshake_server_test.go
index 08a0ccb09..7c1267101 100644
--- a/src/pkg/crypto/tls/handshake_server_test.go
+++ b/src/pkg/crypto/tls/handshake_server_test.go
@@ -37,9 +37,12 @@ func init() {
testConfig = new(Config)
testConfig.Time = func() time.Time { return time.Unix(0, 0) }
testConfig.Rand = zeroSource{}
- testConfig.Certificates = make([]Certificate, 1)
+ testConfig.Certificates = make([]Certificate, 2)
testConfig.Certificates[0].Certificate = [][]byte{testCertificate}
testConfig.Certificates[0].PrivateKey = testPrivateKey
+ testConfig.Certificates[1].Certificate = [][]byte{testSNICertificate}
+ testConfig.Certificates[1].PrivateKey = testPrivateKey
+ testConfig.BuildNameToCertificate()
testConfig.CipherSuites = []uint16{TLS_RSA_WITH_RC4_128_SHA}
testConfig.InsecureSkipVerify = true
}
@@ -176,6 +179,13 @@ func TestHandshakeServerSSLv3(t *testing.T) {
testServerScript(t, "SSLv3", sslv3ServerScript, testConfig, nil)
}
+// TestHandshakeServerSNI involves a client sending an SNI extension of
+// "snitest.com", which happens to match the CN of testSNICertificate. The test
+// verifies that the server correctly selects that certificate.
+func TestHandshakeServerSNI(t *testing.T) {
+ testServerScript(t, "SNI", selectCertificateBySNIScript, testConfig, nil)
+}
+
type clientauthTest struct {
name string
clientauth ClientAuthType
@@ -258,6 +268,8 @@ func fromHex(s string) []byte {
var testCertificate = fromHex("308202b030820219a00302010202090085b0bba48a7fb8ca300d06092a864886f70d01010505003045310b3009060355040613024155311330110603550408130a536f6d652d53746174653121301f060355040a1318496e7465726e6574205769646769747320507479204c7464301e170d3130303432343039303933385a170d3131303432343039303933385a3045310b3009060355040613024155311330110603550408130a536f6d652d53746174653121301f060355040a1318496e7465726e6574205769646769747320507479204c746430819f300d06092a864886f70d010101050003818d0030818902818100bb79d6f517b5e5bf4610d0dc69bee62b07435ad0032d8a7a4385b71452e7a5654c2c78b8238cb5b482e5de1f953b7e62a52ca533d6fe125c7a56fcf506bffa587b263fb5cd04d3d0c921964ac7f4549f5abfef427100fe1899077f7e887d7df10439c4a22edb51c97ce3c04c3b326601cfafb11db8719a1ddbdb896baeda2d790203010001a381a73081a4301d0603551d0e04160414b1ade2855acfcb28db69ce2369ded3268e18883930750603551d23046e306c8014b1ade2855acfcb28db69ce2369ded3268e188839a149a4473045310b3009060355040613024155311330110603550408130a536f6d652d53746174653121301f060355040a1318496e7465726e6574205769646769747320507479204c746482090085b0bba48a7fb8ca300c0603551d13040530030101ff300d06092a864886f70d010105050003818100086c4524c76bb159ab0c52ccf2b014d7879d7a6475b55a9566e4c52b8eae12661feb4f38b36e60d392fdf74108b52513b1187a24fb301dbaed98b917ece7d73159db95d31d78ea50565cd5825a2d5a5f33c4b6d8c97590968c0f5298b5cd981f89205ff2a01ca31b9694dda9fd57e970e8266d71999b266e3850296c90a7bdd9")
+var testSNICertificate = fromHex("308201f23082015da003020102020100300b06092a864886f70d01010530283110300e060355040a130741636d6520436f311430120603550403130b736e69746573742e636f6d301e170d3132303431313137343033355a170d3133303431313137343533355a30283110300e060355040a130741636d6520436f311430120603550403130b736e69746573742e636f6d30819d300b06092a864886f70d01010103818d0030818902818100bb79d6f517b5e5bf4610d0dc69bee62b07435ad0032d8a7a4385b71452e7a5654c2c78b8238cb5b482e5de1f953b7e62a52ca533d6fe125c7a56fcf506bffa587b263fb5cd04d3d0c921964ac7f4549f5abfef427100fe1899077f7e887d7df10439c4a22edb51c97ce3c04c3b326601cfafb11db8719a1ddbdb896baeda2d790203010001a3323030300e0603551d0f0101ff0404030200a0300d0603551d0e0406040401020304300f0603551d2304083006800401020304300b06092a864886f70d0101050381810089c6455f1c1f5ef8eb1ab174ee2439059f5c4259bb1a8d86cdb1d056f56a717da40e95ab90f59e8deaf627c157995094db0802266eb34fc6842dea8a4b68d9c1389103ab84fb9e1f85d9b5d23ff2312c8670fbb540148245a4ebafe264d90c8a4cf4f85b0fac12ac2fc4a3154bad52462868af96c62c6525d652b6e31845bdcc")
+
var testPrivateKey = &rsa.PrivateKey{
PublicKey: rsa.PublicKey{
N: bigFromString("131650079503776001033793877885499001334664249354723305978524647182322416328664556247316495448366990052837680518067798333412266673813370895702118944398081598789828837447552603077848001020611640547221687072142537202428102790818451901395596882588063427854225330436740647715202971973145151161964464812406232198521"),
@@ -947,6 +959,142 @@ var sslv3ServerScript = [][]byte{
},
}
+var selectCertificateBySNIScript = [][]byte{
+ {
+ 0x16, 0x03, 0x01, 0x00, 0x6e, 0x01, 0x00, 0x00,
+ 0x6a, 0x03, 0x01, 0x4f, 0x85, 0xc4, 0xc2, 0xb9,
+ 0x39, 0x80, 0x91, 0x66, 0x65, 0x56, 0x8e, 0xdd,
+ 0x48, 0xe9, 0xca, 0x34, 0x02, 0x3c, 0xaf, 0x0d,
+ 0x73, 0xb5, 0x2a, 0x05, 0x6e, 0xbd, 0x5e, 0x8f,
+ 0x38, 0xf9, 0xe5, 0x00, 0x00, 0x28, 0x00, 0x39,
+ 0x00, 0x38, 0x00, 0x35, 0x00, 0x16, 0x00, 0x13,
+ 0x00, 0x0a, 0x00, 0x33, 0x00, 0x32, 0x00, 0x2f,
+ 0x00, 0x05, 0x00, 0x04, 0x00, 0x15, 0x00, 0x12,
+ 0x00, 0x09, 0x00, 0x14, 0x00, 0x11, 0x00, 0x08,
+ 0x00, 0x06, 0x00, 0x03, 0x00, 0xff, 0x02, 0x01,
+ 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x10, 0x00,
+ 0x0e, 0x00, 0x00, 0x0b, 0x73, 0x6e, 0x69, 0x74,
+ 0x65, 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x00,
+ 0x23, 0x00, 0x00,
+ },
+ {
+ 0x16, 0x03, 0x01, 0x00, 0x2a, 0x02, 0x00, 0x00,
+ 0x26, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x16,
+ 0x03, 0x01, 0x02, 0x00, 0x0b, 0x00, 0x01, 0xfc,
+ 0x00, 0x01, 0xf9, 0x00, 0x01, 0xf6, 0x30, 0x82,
+ 0x01, 0xf2, 0x30, 0x82, 0x01, 0x5d, 0xa0, 0x03,
+ 0x02, 0x01, 0x02, 0x02, 0x01, 0x00, 0x30, 0x0b,
+ 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
+ 0x01, 0x01, 0x05, 0x30, 0x28, 0x31, 0x10, 0x30,
+ 0x0e, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x07,
+ 0x41, 0x63, 0x6d, 0x65, 0x20, 0x43, 0x6f, 0x31,
+ 0x14, 0x30, 0x12, 0x06, 0x03, 0x55, 0x04, 0x03,
+ 0x13, 0x0b, 0x73, 0x6e, 0x69, 0x74, 0x65, 0x73,
+ 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x1e, 0x17,
+ 0x0d, 0x31, 0x32, 0x30, 0x34, 0x31, 0x31, 0x31,
+ 0x37, 0x34, 0x30, 0x33, 0x35, 0x5a, 0x17, 0x0d,
+ 0x31, 0x33, 0x30, 0x34, 0x31, 0x31, 0x31, 0x37,
+ 0x34, 0x35, 0x33, 0x35, 0x5a, 0x30, 0x28, 0x31,
+ 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x0a,
+ 0x13, 0x07, 0x41, 0x63, 0x6d, 0x65, 0x20, 0x43,
+ 0x6f, 0x31, 0x14, 0x30, 0x12, 0x06, 0x03, 0x55,
+ 0x04, 0x03, 0x13, 0x0b, 0x73, 0x6e, 0x69, 0x74,
+ 0x65, 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x30,
+ 0x81, 0x9d, 0x30, 0x0b, 0x06, 0x09, 0x2a, 0x86,
+ 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x03,
+ 0x81, 0x8d, 0x00, 0x30, 0x81, 0x89, 0x02, 0x81,
+ 0x81, 0x00, 0xbb, 0x79, 0xd6, 0xf5, 0x17, 0xb5,
+ 0xe5, 0xbf, 0x46, 0x10, 0xd0, 0xdc, 0x69, 0xbe,
+ 0xe6, 0x2b, 0x07, 0x43, 0x5a, 0xd0, 0x03, 0x2d,
+ 0x8a, 0x7a, 0x43, 0x85, 0xb7, 0x14, 0x52, 0xe7,
+ 0xa5, 0x65, 0x4c, 0x2c, 0x78, 0xb8, 0x23, 0x8c,
+ 0xb5, 0xb4, 0x82, 0xe5, 0xde, 0x1f, 0x95, 0x3b,
+ 0x7e, 0x62, 0xa5, 0x2c, 0xa5, 0x33, 0xd6, 0xfe,
+ 0x12, 0x5c, 0x7a, 0x56, 0xfc, 0xf5, 0x06, 0xbf,
+ 0xfa, 0x58, 0x7b, 0x26, 0x3f, 0xb5, 0xcd, 0x04,
+ 0xd3, 0xd0, 0xc9, 0x21, 0x96, 0x4a, 0xc7, 0xf4,
+ 0x54, 0x9f, 0x5a, 0xbf, 0xef, 0x42, 0x71, 0x00,
+ 0xfe, 0x18, 0x99, 0x07, 0x7f, 0x7e, 0x88, 0x7d,
+ 0x7d, 0xf1, 0x04, 0x39, 0xc4, 0xa2, 0x2e, 0xdb,
+ 0x51, 0xc9, 0x7c, 0xe3, 0xc0, 0x4c, 0x3b, 0x32,
+ 0x66, 0x01, 0xcf, 0xaf, 0xb1, 0x1d, 0xb8, 0x71,
+ 0x9a, 0x1d, 0xdb, 0xdb, 0x89, 0x6b, 0xae, 0xda,
+ 0x2d, 0x79, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3,
+ 0x32, 0x30, 0x30, 0x30, 0x0e, 0x06, 0x03, 0x55,
+ 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03,
+ 0x02, 0x00, 0xa0, 0x30, 0x0d, 0x06, 0x03, 0x55,
+ 0x1d, 0x0e, 0x04, 0x06, 0x04, 0x04, 0x01, 0x02,
+ 0x03, 0x04, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x1d,
+ 0x23, 0x04, 0x08, 0x30, 0x06, 0x80, 0x04, 0x01,
+ 0x02, 0x03, 0x04, 0x30, 0x0b, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05,
+ 0x03, 0x81, 0x81, 0x00, 0x89, 0xc6, 0x45, 0x5f,
+ 0x1c, 0x1f, 0x5e, 0xf8, 0xeb, 0x1a, 0xb1, 0x74,
+ 0xee, 0x24, 0x39, 0x05, 0x9f, 0x5c, 0x42, 0x59,
+ 0xbb, 0x1a, 0x8d, 0x86, 0xcd, 0xb1, 0xd0, 0x56,
+ 0xf5, 0x6a, 0x71, 0x7d, 0xa4, 0x0e, 0x95, 0xab,
+ 0x90, 0xf5, 0x9e, 0x8d, 0xea, 0xf6, 0x27, 0xc1,
+ 0x57, 0x99, 0x50, 0x94, 0xdb, 0x08, 0x02, 0x26,
+ 0x6e, 0xb3, 0x4f, 0xc6, 0x84, 0x2d, 0xea, 0x8a,
+ 0x4b, 0x68, 0xd9, 0xc1, 0x38, 0x91, 0x03, 0xab,
+ 0x84, 0xfb, 0x9e, 0x1f, 0x85, 0xd9, 0xb5, 0xd2,
+ 0x3f, 0xf2, 0x31, 0x2c, 0x86, 0x70, 0xfb, 0xb5,
+ 0x40, 0x14, 0x82, 0x45, 0xa4, 0xeb, 0xaf, 0xe2,
+ 0x64, 0xd9, 0x0c, 0x8a, 0x4c, 0xf4, 0xf8, 0x5b,
+ 0x0f, 0xac, 0x12, 0xac, 0x2f, 0xc4, 0xa3, 0x15,
+ 0x4b, 0xad, 0x52, 0x46, 0x28, 0x68, 0xaf, 0x96,
+ 0xc6, 0x2c, 0x65, 0x25, 0xd6, 0x52, 0xb6, 0xe3,
+ 0x18, 0x45, 0xbd, 0xcc, 0x16, 0x03, 0x01, 0x00,
+ 0x04, 0x0e, 0x00, 0x00, 0x00,
+ },
+ {
+ 0x16, 0x03, 0x01, 0x00, 0x86, 0x10, 0x00, 0x00,
+ 0x82, 0x00, 0x80, 0x70, 0x1d, 0x34, 0x75, 0xa2,
+ 0xe7, 0xe3, 0x2f, 0x3d, 0xc1, 0x1d, 0xca, 0x0b,
+ 0xe3, 0x64, 0xb9, 0x1a, 0x00, 0x69, 0xc4, 0x14,
+ 0x05, 0x07, 0x7e, 0xc3, 0x51, 0x43, 0x52, 0x66,
+ 0xe3, 0xbd, 0xff, 0x1b, 0x1a, 0x6a, 0x84, 0xf2,
+ 0x07, 0x24, 0xd7, 0x12, 0xa8, 0x58, 0xcf, 0x8a,
+ 0x50, 0x30, 0xe8, 0xc8, 0xb2, 0xf9, 0x58, 0x1c,
+ 0x56, 0x53, 0x76, 0x21, 0xe0, 0x03, 0x7f, 0x77,
+ 0xa7, 0xf1, 0xad, 0x67, 0xd4, 0xe2, 0x8f, 0xa0,
+ 0x58, 0x6c, 0xe0, 0x28, 0x59, 0xf3, 0xd1, 0x53,
+ 0x2b, 0x21, 0xbd, 0xa3, 0x84, 0x31, 0x73, 0xbf,
+ 0x84, 0x0f, 0x83, 0xf4, 0xc4, 0xd0, 0xe5, 0x3c,
+ 0x2d, 0x3e, 0xf2, 0x8a, 0x1e, 0xe7, 0xe9, 0x1f,
+ 0x12, 0x13, 0xad, 0x29, 0xd6, 0x0c, 0xc7, 0xc6,
+ 0x05, 0x53, 0x7d, 0x5e, 0xc6, 0x92, 0x72, 0xba,
+ 0xd2, 0x93, 0x8f, 0x53, 0x84, 0x87, 0x44, 0x05,
+ 0x9f, 0x5d, 0x66, 0x14, 0x03, 0x01, 0x00, 0x01,
+ 0x01, 0x16, 0x03, 0x01, 0x00, 0x24, 0xfc, 0x71,
+ 0xaa, 0xa8, 0x37, 0xa8, 0xbd, 0x63, 0xb7, 0xbc,
+ 0x95, 0xef, 0x0c, 0xcf, 0x39, 0x31, 0x93, 0xe6,
+ 0x86, 0xbd, 0x3f, 0x56, 0x9d, 0xf0, 0xb2, 0xb5,
+ 0xd1, 0xa7, 0xc6, 0x45, 0x89, 0x18, 0xfb, 0xa0,
+ 0x7f, 0xc1,
+ },
+ {
+ 0x14, 0x03, 0x01, 0x00, 0x01, 0x01, 0x16, 0x03,
+ 0x01, 0x00, 0x24, 0xb8, 0x6d, 0x9a, 0x90, 0x3c,
+ 0x45, 0xe0, 0xff, 0x63, 0xba, 0xab, 0x3d, 0x7a,
+ 0xa6, 0x49, 0x5a, 0x13, 0xdc, 0x0e, 0xa3, 0xba,
+ 0x7f, 0x04, 0x19, 0x45, 0xfd, 0xfb, 0xbd, 0x00,
+ 0xa3, 0xa7, 0x78, 0x81, 0x38, 0x9f, 0x10, 0x17,
+ 0x03, 0x01, 0x00, 0x21, 0x43, 0xc3, 0x91, 0xb7,
+ 0xbf, 0x50, 0x0b, 0x04, 0xb4, 0x5d, 0xc6, 0x20,
+ 0x64, 0xb8, 0x01, 0x09, 0x25, 0x2c, 0x03, 0x30,
+ 0xc0, 0x77, 0xc9, 0x5e, 0xe6, 0xe0, 0x99, 0xdc,
+ 0xcd, 0x75, 0x9d, 0x51, 0x82, 0x15, 0x03, 0x01,
+ 0x00, 0x16, 0x2d, 0x7a, 0x89, 0x7b, 0x36, 0x85,
+ 0x2a, 0x93, 0xcb, 0x83, 0xa7, 0x2f, 0x9e, 0x91,
+ 0xfc, 0xad, 0x57, 0xca, 0xf5, 0xbc, 0x13, 0x2f,
+ },
+}
+
var clientauthTests = []clientauthTest{
// Server doesn't asks for cert
// go test -run "TestRunServer" -serve -clientauth 0
@@ -1285,7 +1433,7 @@ var clientauthTests = []clientauthTest{
// go test -run "TestRunServer" -serve -clientauth 1
// gnutls-cli --insecure --debug 100 -p 10443 localhost
{"RequestClientCert, client gives it", RequestClientCert,
- []*x509.Certificate{clicert},
+ []*x509.Certificate{clientCertificate},
[][]byte{{
0x16, 0x03, 0x02, 0x00, 0x7a, 0x01, 0x00, 0x00,
0x76, 0x03, 0x02, 0x4e, 0xe7, 0x44, 0xda, 0x58,
@@ -1528,14 +1676,16 @@ var clientauthTests = []clientauthTest{
0x00, 0x16, 0x53, 0xf5, 0xff, 0xe0, 0xa1, 0x6c,
0x33, 0xf4, 0x4e, 0x89, 0x68, 0xe1, 0xf7, 0x61,
0x13, 0xb3, 0x12, 0xa1, 0x8e, 0x5a, 0x7a, 0x02,
- }}},
+ },
+ },
+ },
}
// cert.pem and key.pem were generated with generate_cert.go
// Thus, they have no ExtKeyUsage fields and trigger an error
// when verification is turned on.
-var clicert = loadPEMCert(`
+var clientCertificate = loadPEMCert(`
-----BEGIN CERTIFICATE-----
MIIB7TCCAVigAwIBAgIBADALBgkqhkiG9w0BAQUwJjEQMA4GA1UEChMHQWNtZSBD
bzESMBAGA1UEAxMJMTI3LjAuMC4xMB4XDTExMTIwODA3NTUxMloXDTEyMTIwNzA4
diff --git a/src/pkg/crypto/tls/key_agreement.go b/src/pkg/crypto/tls/key_agreement.go
index a931d8fb5..b6e73fe29 100644
--- a/src/pkg/crypto/tls/key_agreement.go
+++ b/src/pkg/crypto/tls/key_agreement.go
@@ -20,11 +20,11 @@ import (
// encrypts the pre-master secret to the server's public key.
type rsaKeyAgreement struct{}
-func (ka rsaKeyAgreement) generateServerKeyExchange(config *Config, clientHello *clientHelloMsg, hello *serverHelloMsg) (*serverKeyExchangeMsg, error) {
+func (ka rsaKeyAgreement) generateServerKeyExchange(config *Config, cert *Certificate, clientHello *clientHelloMsg, hello *serverHelloMsg) (*serverKeyExchangeMsg, error) {
return nil, nil
}
-func (ka rsaKeyAgreement) processClientKeyExchange(config *Config, ckx *clientKeyExchangeMsg, version uint16) ([]byte, error) {
+func (ka rsaKeyAgreement) processClientKeyExchange(config *Config, cert *Certificate, ckx *clientKeyExchangeMsg, version uint16) ([]byte, error) {
preMasterSecret := make([]byte, 48)
_, err := io.ReadFull(config.rand(), preMasterSecret[2:])
if err != nil {
@@ -44,7 +44,7 @@ func (ka rsaKeyAgreement) processClientKeyExchange(config *Config, ckx *clientKe
ciphertext = ckx.ciphertext[2:]
}
- err = rsa.DecryptPKCS1v15SessionKey(config.rand(), config.Certificates[0].PrivateKey.(*rsa.PrivateKey), ciphertext, preMasterSecret)
+ err = rsa.DecryptPKCS1v15SessionKey(config.rand(), cert.PrivateKey.(*rsa.PrivateKey), ciphertext, preMasterSecret)
if err != nil {
return nil, err
}
@@ -109,7 +109,7 @@ type ecdheRSAKeyAgreement struct {
x, y *big.Int
}
-func (ka *ecdheRSAKeyAgreement) generateServerKeyExchange(config *Config, clientHello *clientHelloMsg, hello *serverHelloMsg) (*serverKeyExchangeMsg, error) {
+func (ka *ecdheRSAKeyAgreement) generateServerKeyExchange(config *Config, cert *Certificate, clientHello *clientHelloMsg, hello *serverHelloMsg) (*serverKeyExchangeMsg, error) {
var curveid uint16
Curve:
@@ -151,7 +151,7 @@ Curve:
copy(serverECDHParams[4:], ecdhePublic)
md5sha1 := md5SHA1Hash(clientHello.random, hello.random, serverECDHParams)
- sig, err := rsa.SignPKCS1v15(config.rand(), config.Certificates[0].PrivateKey.(*rsa.PrivateKey), crypto.MD5SHA1, md5sha1)
+ sig, err := rsa.SignPKCS1v15(config.rand(), cert.PrivateKey.(*rsa.PrivateKey), crypto.MD5SHA1, md5sha1)
if err != nil {
return nil, errors.New("failed to sign ECDHE parameters: " + err.Error())
}
@@ -167,7 +167,7 @@ Curve:
return skx, nil
}
-func (ka *ecdheRSAKeyAgreement) processClientKeyExchange(config *Config, ckx *clientKeyExchangeMsg, version uint16) ([]byte, error) {
+func (ka *ecdheRSAKeyAgreement) processClientKeyExchange(config *Config, cert *Certificate, ckx *clientKeyExchangeMsg, version uint16) ([]byte, error) {
if len(ckx.ciphertext) == 0 || int(ckx.ciphertext[0]) != len(ckx.ciphertext)-1 {
return nil, errors.New("bad ClientKeyExchange")
}
diff --git a/src/pkg/database/sql/sql.go b/src/pkg/database/sql/sql.go
index 51a357b37..89136ef6e 100644
--- a/src/pkg/database/sql/sql.go
+++ b/src/pkg/database/sql/sql.go
@@ -327,6 +327,9 @@ func (db *DB) prepare(query string) (stmt *Stmt, err error) {
// Exec executes a query without returning any rows.
func (db *DB) Exec(query string, args ...interface{}) (Result, error) {
sargs, err := subsetTypeArgs(args)
+ if err != nil {
+ return nil, err
+ }
var res Result
for i := 0; i < 10; i++ {
res, err = db.exec(query, sargs)
diff --git a/src/pkg/encoding/ascii85/ascii85.go b/src/pkg/encoding/ascii85/ascii85.go
index 7d004b5e5..705022792 100644
--- a/src/pkg/encoding/ascii85/ascii85.go
+++ b/src/pkg/encoding/ascii85/ascii85.go
@@ -57,6 +57,7 @@ func Encode(dst, src []byte) int {
if v == 0 && len(src) >= 4 {
dst[0] = 'z'
dst = dst[1:]
+ src = src[4:]
n++
continue
}
diff --git a/src/pkg/encoding/ascii85/ascii85_test.go b/src/pkg/encoding/ascii85/ascii85_test.go
index 70e67d8b0..42cf7e80e 100644
--- a/src/pkg/encoding/ascii85/ascii85_test.go
+++ b/src/pkg/encoding/ascii85/ascii85_test.go
@@ -28,6 +28,11 @@ var pairs = []testpair{
"l(DId<j@<?3r@:F%a+D58'ATD4$Bl@l3De:,-DJs`8ARoFb/0JMK@qB4^F!,R<AKZ&-DfTqBG%G\n" +
">uD.RTpAKYo'+CT/5+Cei#DII?(E,9)oF*2M7/c\n",
},
+ // Special case when shortening !!!!! to z.
+ {
+ "\000\000\000\000",
+ "z",
+ },
}
var bigtest = pairs[len(pairs)-1]
diff --git a/src/pkg/encoding/asn1/asn1.go b/src/pkg/encoding/asn1/asn1.go
index 3bf81a68c..ac2b5f8da 100644
--- a/src/pkg/encoding/asn1/asn1.go
+++ b/src/pkg/encoding/asn1/asn1.go
@@ -377,11 +377,6 @@ func parseTagAndLength(bytes []byte, initOffset int) (ret tagAndLength, offset i
} else {
// Bottom 7 bits give the number of length bytes to follow.
numBytes := int(b & 0x7f)
- // We risk overflowing a signed 32-bit number if we accept more than 3 bytes.
- if numBytes > 3 {
- err = StructuralError{"length too large"}
- return
- }
if numBytes == 0 {
err = SyntaxError{"indefinite length found (not DER)"}
return
@@ -394,8 +389,19 @@ func parseTagAndLength(bytes []byte, initOffset int) (ret tagAndLength, offset i
}
b = bytes[offset]
offset++
+ if ret.length >= 1<<23 {
+ // We can't shift ret.length up without
+ // overflowing.
+ err = StructuralError{"length too large"}
+ return
+ }
ret.length <<= 8
ret.length |= int(b)
+ if ret.length == 0 {
+ // DER requires that lengths be minimal.
+ err = StructuralError{"superfluous leading zeros in length"}
+ return
+ }
}
}
diff --git a/src/pkg/encoding/asn1/asn1_test.go b/src/pkg/encoding/asn1/asn1_test.go
index 93803f435..eb848bdb4 100644
--- a/src/pkg/encoding/asn1/asn1_test.go
+++ b/src/pkg/encoding/asn1/asn1_test.go
@@ -283,6 +283,12 @@ var tagAndLengthData = []tagAndLengthTest{
{[]byte{0x00, 0x83, 0x01, 0x00}, false, tagAndLength{}},
{[]byte{0x1f, 0x85}, false, tagAndLength{}},
{[]byte{0x30, 0x80}, false, tagAndLength{}},
+ // Superfluous zeros in the length should be an error.
+ {[]byte{0xa0, 0x82, 0x00, 0x01}, false, tagAndLength{}},
+ // Lengths up to the maximum size of an int should work.
+ {[]byte{0xa0, 0x84, 0x7f, 0xff, 0xff, 0xff}, true, tagAndLength{2, 0, 0x7fffffff, true}},
+ // Lengths that would overflow an int should be rejected.
+ {[]byte{0xa0, 0x84, 0x80, 0x00, 0x00, 0x00}, false, tagAndLength{}},
}
func TestParseTagAndLength(t *testing.T) {
diff --git a/src/pkg/encoding/base64/base64.go b/src/pkg/encoding/base64/base64.go
index 55f9f67a4..f8a51a4e7 100644
--- a/src/pkg/encoding/base64/base64.go
+++ b/src/pkg/encoding/base64/base64.go
@@ -230,7 +230,12 @@ func (enc *Encoding) decode(dst, src []byte) (n int, end bool, err error) {
if in == '=' && j >= 2 && len(src) < 4 {
// We've reached the end and there's
// padding
+ if len(src) == 0 && j == 2 {
+ // not enough padding
+ return n, false, CorruptInputError(len(osrc))
+ }
if len(src) > 0 && src[0] != '=' {
+ // incorrect padding
return n, false, CorruptInputError(len(osrc) - len(src) - 1)
}
dlen = j
diff --git a/src/pkg/encoding/base64/base64_test.go b/src/pkg/encoding/base64/base64_test.go
index 3e9a84393..9c3537259 100644
--- a/src/pkg/encoding/base64/base64_test.go
+++ b/src/pkg/encoding/base64/base64_test.go
@@ -151,6 +151,9 @@ func TestDecodeCorrupt(t *testing.T) {
{"AAA=AAAA", 3},
{"AAAAA", 4},
{"AAAAAA", 4},
+ {"A=", 1},
+ {"AA=", 3},
+ {"AAAAAA=", 7},
}
for _, e := range examples {
diff --git a/src/pkg/encoding/json/encode.go b/src/pkg/encoding/json/encode.go
index 14957b848..842672c39 100644
--- a/src/pkg/encoding/json/encode.go
+++ b/src/pkg/encoding/json/encode.go
@@ -17,6 +17,7 @@ import (
"runtime"
"sort"
"strconv"
+ "strings"
"sync"
"unicode"
"unicode/utf8"
@@ -415,9 +416,11 @@ func isValidTag(s string) bool {
return false
}
for _, c := range s {
- switch c {
- case '$', '-', '_', '/', '%':
- // Acceptable
+ switch {
+ case strings.ContainsRune("!#$%&()*+-./:<=>?@[]^_{|}~", c):
+ // Backslash and quote chars are reserved, but
+ // otherwise any punctuation chars are allowed
+ // in a tag name.
default:
if !unicode.IsLetter(c) && !unicode.IsDigit(c) {
return false
diff --git a/src/pkg/encoding/json/tagkey_test.go b/src/pkg/encoding/json/tagkey_test.go
index bba573035..da8b12bd8 100644
--- a/src/pkg/encoding/json/tagkey_test.go
+++ b/src/pkg/encoding/json/tagkey_test.go
@@ -40,6 +40,10 @@ type percentSlashTag struct {
V string `json:"text/html%"` // http://golang.org/issue/2718
}
+type punctuationTag struct {
+ V string `json:"!#$%&()*+-./:<=>?@[]^_{|}~"` // http://golang.org/issue/3546
+}
+
type emptyTag struct {
W string
}
@@ -73,6 +77,7 @@ var structTagObjectKeyTests = []struct {
{badFormatTag{"Orfevre"}, "Orfevre", "Y"},
{badCodeTag{"Reliable Man"}, "Reliable Man", "Z"},
{percentSlashTag{"brut"}, "brut", "text/html%"},
+ {punctuationTag{"Union Rags"}, "Union Rags", "!#$%&()*+-./:<=>?@[]^_{|}~"},
}
func TestStructTagObjectKey(t *testing.T) {
diff --git a/src/pkg/encoding/pem/pem.go b/src/pkg/encoding/pem/pem.go
index 38afbb42a..3c1f5ab70 100644
--- a/src/pkg/encoding/pem/pem.go
+++ b/src/pkg/encoding/pem/pem.go
@@ -28,9 +28,10 @@ type Block struct {
}
// getLine results the first \r\n or \n delineated line from the given byte
-// array. The line does not include the \r\n or \n. The remainder of the byte
-// array (also not including the new line bytes) is also returned and this will
-// always be smaller than the original argument.
+// array. The line does not include trailing whitespace or the trailing new
+// line bytes. The remainder of the byte array (also not including the new line
+// bytes) is also returned and this will always be smaller than the original
+// argument.
func getLine(data []byte) (line, rest []byte) {
i := bytes.Index(data, []byte{'\n'})
var j int
@@ -43,7 +44,7 @@ func getLine(data []byte) (line, rest []byte) {
i--
}
}
- return data[0:i], data[j:]
+ return bytes.TrimRight(data[0:i], " \t"), data[j:]
}
// removeWhitespace returns a copy of its input with all spaces, tab and
diff --git a/src/pkg/encoding/pem/pem_test.go b/src/pkg/encoding/pem/pem_test.go
index 9ae1578a5..613353483 100644
--- a/src/pkg/encoding/pem/pem_test.go
+++ b/src/pkg/encoding/pem/pem_test.go
@@ -127,13 +127,13 @@ Certificate chain
-----BEGIN CERTIFICATE-----
testing
-----BEGIN CERTIFICATE-----
------BEGIN CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
MIID6TCCA1ICAQEwDQYJKoZIhvcNAQEFBQAwgYsxCzAJBgNVBAYTAlVTMRMwEQYD
VQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1TYW4gRnJhbmNpc2NvMRQwEgYDVQQK
-EwtHb29nbGUgSW5jLjEMMAoGA1UECxMDRW5nMQwwCgYDVQQDEwNhZ2wxHTAbBgkq
-hkiG9w0BCQEWDmFnbEBnb29nbGUuY29tMB4XDTA5MDkwOTIyMDU0M1oXDTEwMDkw
-OTIyMDU0M1owajELMAkGA1UEBhMCQVUxEzARBgNVBAgTClNvbWUtU3RhdGUxITAf
-BgNVBAoTGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEjMCEGA1UEAxMaZXVyb3Bh
+EwtHb29nbGUgSW5jLjEMMAoGA1UECxMDRW5nMQwwCgYDVQQDEwNhZ2wxHTAbBgkq
+hkiG9w0BCQEWDmFnbEBnb29nbGUuY29tMB4XDTA5MDkwOTIyMDU0M1oXDTEwMDkw
+OTIyMDU0M1owajELMAkGA1UEBhMCQVUxEzARBgNVBAgTClNvbWUtU3RhdGUxITAf
+BgNVBAoTGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEjMCEGA1UEAxMaZXVyb3Bh
LnNmby5jb3JwLmdvb2dsZS5jb20wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK
AoICAQC6pgYt7/EibBDumASF+S0qvqdL/f+nouJw2T1Qc8GmXF/iiUcrsgzh/Fd8
pDhz/T96Qg9IyR4ztuc2MXrmPra+zAuSf5bevFReSqvpIt8Duv0HbDbcqs/XKPfB
@@ -149,15 +149,15 @@ Pomjn71GNTtDeWAXibjCgdL6iHACCF6Htbl0zGlG0OAK+bdn0QIDAQABMA0GCSqG
SIb3DQEBBQUAA4GBAOKnQDtqBV24vVqvesL5dnmyFpFPXBn3WdFfwD6DzEb21UVG
5krmJiu+ViipORJPGMkgoL6BjU21XI95VQbun5P8vvg8Z+FnFsvRFY3e1CCzAVQY
ZsUkLw2I7zI/dNlWdB8Xp7v+3w9sX5N3J/WuJ1KOO5m26kRlHQo7EzT3974g
------END CERTIFICATE-----
+-----END CERTIFICATE-----
1 s:/C=ZA/O=Ca Inc./CN=CA Inc
------BEGIN RSA PRIVATE KEY-----
-Proc-Type: 4,ENCRYPTED
-DEK-Info: DES-EDE3-CBC,80C7C7A09690757A
-
+-----BEGIN RSA PRIVATE KEY-----
+Proc-Type: 4,ENCRYPTED
+DEK-Info: DES-EDE3-CBC,80C7C7A09690757A
+
eQp5ZkH6CyHBz7BZfUPxyLCCmftsBJ7HlqGb8Ld21cSwnzWZ4/SIlhyrUtsfw7VR
-2TTwA+odo9ex7GdxOTaH8oZFumIRoiEjHsk8U7Bhntp+ekkPP79xunnN7hb7hkhr
+2TTwA+odo9ex7GdxOTaH8oZFumIRoiEjHsk8U7Bhntp+ekkPP79xunnN7hb7hkhr
yGDQZgA7s2cQHQ71v3gwT2BACAft26jCjbM1wgNzBnJ8M0Rzn68YWqaPtdBu8qb/
zVR5JB1mnqvTSbFsfF5yMc6o2WQ9jJCl6KypnMl+BpL+dlvdjYVK4l9lYsB1Hs3d
+zDBbWxos818zzhS8/y6eIfiSG27cqrbhURbmgiSfDXjncK4m/pLcQ7mmBL6mFOr
diff --git a/src/pkg/flag/flag.go b/src/pkg/flag/flag.go
index c28d0e720..f0842a18a 100644
--- a/src/pkg/flag/flag.go
+++ b/src/pkg/flag/flag.go
@@ -7,9 +7,11 @@
Usage:
- Define flags using flag.String(), Bool(), Int(), etc. Example:
+ Define flags using flag.String(), Bool(), Int(), etc.
+
+ This declares an integer flag, -flagname, stored in the pointer ip, with type *int.
import "flag"
- var ip *int = flag.Int("flagname", 1234, "help message for flagname")
+ var ip = flag.Int("flagname", 1234, "help message for flagname")
If you like, you can bind the flag to a variable using the Var() functions.
var flagvar int
func init() {
@@ -26,8 +28,8 @@
Flags may then be used directly. If you're using the flags themselves,
they are all pointers; if you bind to variables, they're values.
- fmt.Println("ip has value ", *ip);
- fmt.Println("flagvar has value ", flagvar);
+ fmt.Println("ip has value ", *ip)
+ fmt.Println("flagvar has value ", flagvar)
After parsing, the arguments after the flag are available as the
slice flag.Args() or individually as flag.Arg(i).
diff --git a/src/pkg/fmt/fmt_test.go b/src/pkg/fmt/fmt_test.go
index 758fc50d0..de0342967 100644
--- a/src/pkg/fmt/fmt_test.go
+++ b/src/pkg/fmt/fmt_test.go
@@ -461,6 +461,9 @@ var fmttests = []struct {
// zero reflect.Value, which formats as <nil>.
// This test is just to check that it shows the two NaNs at all.
{"%v", map[float64]int{math.NaN(): 1, math.NaN(): 2}, "map[NaN:<nil> NaN:<nil>]"},
+
+ // Used to crash because nByte didn't allow for a sign.
+ {"%b", int64(-1 << 63), "-1000000000000000000000000000000000000000000000000000000000000000"},
}
func TestSprintf(t *testing.T) {
diff --git a/src/pkg/fmt/format.go b/src/pkg/fmt/format.go
index 2186f334b..caf900d5c 100644
--- a/src/pkg/fmt/format.go
+++ b/src/pkg/fmt/format.go
@@ -10,7 +10,7 @@ import (
)
const (
- nByte = 64
+ nByte = 65 // %b of an int64, plus a sign.
ldigits = "0123456789abcdef"
udigits = "0123456789ABCDEF"
diff --git a/src/pkg/html/escape.go b/src/pkg/html/escape.go
index fee771a57..24cb7af85 100644
--- a/src/pkg/html/escape.go
+++ b/src/pkg/html/escape.go
@@ -210,13 +210,15 @@ func escape(w writer, s string) error {
case '&':
esc = "&amp;"
case '\'':
- esc = "&apos;"
+ // "&#39;" is shorter than "&apos;" and apos was not in HTML until HTML5.
+ esc = "&#39;"
case '<':
esc = "&lt;"
case '>':
esc = "&gt;"
case '"':
- esc = "&quot;"
+ // "&#34;" is shorter than "&quot;".
+ esc = "&#34;"
default:
panic("unrecognized escape character")
}
@@ -231,7 +233,7 @@ func escape(w writer, s string) error {
}
// EscapeString escapes special characters like "<" to become "&lt;". It
-// escapes only five such characters: amp, apos, lt, gt and quot.
+// escapes only five such characters: <, >, &, ' and ".
// UnescapeString(EscapeString(s)) == s always holds, but the converse isn't
// always true.
func EscapeString(s string) string {
diff --git a/src/pkg/mime/mediatype.go b/src/pkg/mime/mediatype.go
index 9398dece1..8396c0a15 100644
--- a/src/pkg/mime/mediatype.go
+++ b/src/pkg/mime/mediatype.go
@@ -99,7 +99,7 @@ func ParseMediaType(v string) (mediatype string, params map[string]string, err e
err = checkMediaTypeDisposition(mediatype)
if err != nil {
- return
+ return "", nil, err
}
params = make(map[string]string)
diff --git a/src/pkg/mime/mediatype_test.go b/src/pkg/mime/mediatype_test.go
index 64ab29134..e41ead237 100644
--- a/src/pkg/mime/mediatype_test.go
+++ b/src/pkg/mime/mediatype_test.go
@@ -244,13 +244,33 @@ func TestParseMediaType(t *testing.T) {
}
}
+type badMediaTypeTest struct {
+ in string
+ err string
+}
+
+var badMediaTypeTests = []badMediaTypeTest{
+ {"bogus ;=========", "mime: invalid media parameter"},
+ {"bogus/<script>alert</script>", "mime: expected token after slash"},
+ {"bogus/bogus<script>alert</script>", "mime: unexpected content after media subtype"},
+}
+
func TestParseMediaTypeBogus(t *testing.T) {
- mt, params, err := ParseMediaType("bogus ;=========")
- if err == nil {
- t.Fatalf("expected an error parsing invalid media type; got type %q, params %#v", mt, params)
- }
- if err.Error() != "mime: invalid media parameter" {
- t.Errorf("expected invalid media parameter; got error %q", err)
+ for _, tt := range badMediaTypeTests {
+ mt, params, err := ParseMediaType(tt.in)
+ if err == nil {
+ t.Errorf("ParseMediaType(%q) = nil error; want parse error", tt.in)
+ continue
+ }
+ if err.Error() != tt.err {
+ t.Errorf("ParseMediaType(%q) = err %q; want %q", tt.in, err.Error(), tt.err)
+ }
+ if params != nil {
+ t.Errorf("ParseMediaType(%q): got non-nil params on error", tt.in)
+ }
+ if mt != "" {
+ t.Errorf("ParseMediaType(%q): got non-empty media type string on error", tt.in)
+ }
}
}
diff --git a/src/pkg/mime/multipart/multipart.go b/src/pkg/mime/multipart/multipart.go
index d733130ab..6ace4be56 100644
--- a/src/pkg/mime/multipart/multipart.go
+++ b/src/pkg/mime/multipart/multipart.go
@@ -185,6 +185,14 @@ func (r *Reader) NextPart() (*Part, error) {
expectNewPart := false
for {
line, err := r.bufReader.ReadSlice('\n')
+ if err == io.EOF && bytes.Equal(line, r.dashBoundaryDash) {
+ // If the buffer ends in "--boundary--" without the
+ // trailing "\r\n", ReadSlice will return an error
+ // (since it's missing the '\n'), but this is a valid
+ // multipart EOF so we need to return io.EOF instead of
+ // a fmt-wrapped one.
+ return nil, io.EOF
+ }
if err != nil {
return nil, fmt.Errorf("multipart: NextPart: %v", err)
}
diff --git a/src/pkg/mime/multipart/multipart_test.go b/src/pkg/mime/multipart/multipart_test.go
index 89ff5e489..ca7108d7a 100644
--- a/src/pkg/mime/multipart/multipart_test.go
+++ b/src/pkg/mime/multipart/multipart_test.go
@@ -10,6 +10,7 @@ import (
"fmt"
"io"
"io/ioutil"
+ "os"
"strings"
"testing"
)
@@ -377,3 +378,52 @@ func TestLineContinuation(t *testing.T) {
}
}
}
+
+// Test parsing an image attachment from gmail, which previously failed.
+func TestNested(t *testing.T) {
+ // nested-mime is the body part of a multipart/mixed email
+ // with boundary e89a8ff1c1e83553e304be640612
+ f, err := os.Open("testdata/nested-mime")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer f.Close()
+ mr := NewReader(f, "e89a8ff1c1e83553e304be640612")
+ p, err := mr.NextPart()
+ if err != nil {
+ t.Fatalf("error reading first section (alternative): %v", err)
+ }
+
+ // Read the inner text/plain and text/html sections of the multipart/alternative.
+ mr2 := NewReader(p, "e89a8ff1c1e83553e004be640610")
+ p, err = mr2.NextPart()
+ if err != nil {
+ t.Fatalf("reading text/plain part: %v", err)
+ }
+ if b, err := ioutil.ReadAll(p); string(b) != "*body*\r\n" || err != nil {
+ t.Fatalf("reading text/plain part: got %q, %v", b, err)
+ }
+ p, err = mr2.NextPart()
+ if err != nil {
+ t.Fatalf("reading text/html part: %v", err)
+ }
+ if b, err := ioutil.ReadAll(p); string(b) != "<b>body</b>\r\n" || err != nil {
+ t.Fatalf("reading text/html part: got %q, %v", b, err)
+ }
+
+ p, err = mr2.NextPart()
+ if err != io.EOF {
+ t.Fatalf("final inner NextPart = %v; want io.EOF", err)
+ }
+
+ // Back to the outer multipart/mixed, reading the image attachment.
+ _, err = mr.NextPart()
+ if err != nil {
+ t.Fatalf("error reading the image attachment at the end: %v", err)
+ }
+
+ _, err = mr.NextPart()
+ if err != io.EOF {
+ t.Fatalf("final outer NextPart = %v; want io.EOF", err)
+ }
+}
diff --git a/src/pkg/mime/multipart/testdata/nested-mime b/src/pkg/mime/multipart/testdata/nested-mime
new file mode 100755
index 000000000..71c238e38
--- /dev/null
+++ b/src/pkg/mime/multipart/testdata/nested-mime
@@ -0,0 +1,29 @@
+--e89a8ff1c1e83553e304be640612
+Content-Type: multipart/alternative; boundary=e89a8ff1c1e83553e004be640610
+
+--e89a8ff1c1e83553e004be640610
+Content-Type: text/plain; charset=UTF-8
+
+*body*
+
+--e89a8ff1c1e83553e004be640610
+Content-Type: text/html; charset=UTF-8
+
+<b>body</b>
+
+--e89a8ff1c1e83553e004be640610--
+--e89a8ff1c1e83553e304be640612
+Content-Type: image/png; name="x.png"
+Content-Disposition: attachment;
+ filename="x.png"
+Content-Transfer-Encoding: base64
+X-Attachment-Id: f_h1edgigu0
+
+iVBORw0KGgoAAAANSUhEUgAAAagAAADrCAIAAACza5XhAAAKMWlDQ1BJQ0MgUHJvZmlsZQAASImd
+lndUU9kWh8+9N71QkhCKlNBraFICSA29SJEuKjEJEErAkAAiNkRUcERRkaYIMijggKNDkbEiioUB
+8b2kqeGaj4aTNftesu5mob4pr07ecMywRwLBvDCJOksqlUyldAZD7g9fxIZRWWPMvXRNJROJRBIG
+Y7Vx0mva1HAwYqibdKONXye3dW4iUonhWFJnqK7OaanU1gGkErFYEgaj0cg8wK+zVPh2ziwnHy07
+U8lYTNapezSzOuevRwLB7CFkqQQCwaJDiBQIBIJFhwh8AoFg0SHUqQUCASRJKkwkhMy/JfODWPEJ
+BIJFhwh8AoFg0TFnQqQ55GtPFopcJsN97e1nYtNuIBYeGBgYCmYrmE3jZ05iaGAoMX0xzxkWz6Hv
+yO7WvrlwzA0uLzrD+VkKqViwl9IfTBVNFMyc/x9alloiPPlqhQAAAABJRU5ErkJggg==
+--e89a8ff1c1e83553e304be640612--
diff --git a/src/pkg/net/http/example_test.go b/src/pkg/net/http/example_test.go
index 2584afc43..ec814407d 100644
--- a/src/pkg/net/http/example_test.go
+++ b/src/pkg/net/http/example_test.go
@@ -49,3 +49,8 @@ func ExampleGet() {
res.Body.Close()
fmt.Printf("%s", robots)
}
+
+func ExampleFileServer() {
+ // we use StripPrefix so that /tmpfiles/somefile will access /tmp/somefile
+ http.Handle("/tmpfiles/", http.StripPrefix("/tmpfiles/", http.FileServer(http.Dir("/tmp"))))
+}
diff --git a/src/pkg/net/http/server.go b/src/pkg/net/http/server.go
index 228ac4019..924ffd348 100644
--- a/src/pkg/net/http/server.go
+++ b/src/pkg/net/http/server.go
@@ -785,8 +785,10 @@ var htmlReplacer = strings.NewReplacer(
"&", "&amp;",
"<", "&lt;",
">", "&gt;",
- `"`, "&quot;",
- "'", "&apos;",
+ // "&#34;" is shorter than "&quot;".
+ `"`, "&#34;",
+ // "&#39;" is shorter than "&apos;" and apos was not in HTML until HTML5.
+ "'", "&#39;",
)
func htmlEscape(s string) string {
diff --git a/src/pkg/net/iprawsock_posix.go b/src/pkg/net/iprawsock_posix.go
index 6bbe67c3d..9fc7ecdb9 100644
--- a/src/pkg/net/iprawsock_posix.go
+++ b/src/pkg/net/iprawsock_posix.go
@@ -83,9 +83,7 @@ func (c *IPConn) Close() error {
if !c.ok() {
return syscall.EINVAL
}
- err := c.fd.Close()
- c.fd = nil
- return err
+ return c.fd.Close()
}
// LocalAddr returns the local network address.
diff --git a/src/pkg/net/tcpsock_posix.go b/src/pkg/net/tcpsock_posix.go
index 15f8efdd7..e6b1937fb 100644
--- a/src/pkg/net/tcpsock_posix.go
+++ b/src/pkg/net/tcpsock_posix.go
@@ -108,9 +108,7 @@ func (c *TCPConn) Close() error {
if !c.ok() {
return syscall.EINVAL
}
- err := c.fd.Close()
- c.fd = nil
- return err
+ return c.fd.Close()
}
// CloseRead shuts down the reading side of the TCP connection.
@@ -359,5 +357,5 @@ func (l *TCPListener) SetDeadline(t time.Time) error {
// File returns a copy of the underlying os.File, set to blocking mode.
// It is the caller's responsibility to close f when finished.
-// Closing c does not affect f, and closing f does not affect c.
+// Closing l does not affect f, and closing f does not affect l.
func (l *TCPListener) File() (f *os.File, err error) { return l.fd.dup() }
diff --git a/src/pkg/net/udpsock_posix.go b/src/pkg/net/udpsock_posix.go
index 9e820e1c5..9c6b6d393 100644
--- a/src/pkg/net/udpsock_posix.go
+++ b/src/pkg/net/udpsock_posix.go
@@ -88,9 +88,7 @@ func (c *UDPConn) Close() error {
if !c.ok() {
return syscall.EINVAL
}
- err := c.fd.Close()
- c.fd = nil
- return err
+ return c.fd.Close()
}
// LocalAddr returns the local network address.
diff --git a/src/pkg/net/unixsock_posix.go b/src/pkg/net/unixsock_posix.go
index 37a2b1e09..57d784c71 100644
--- a/src/pkg/net/unixsock_posix.go
+++ b/src/pkg/net/unixsock_posix.go
@@ -141,9 +141,7 @@ func (c *UnixConn) Close() error {
if !c.ok() {
return syscall.EINVAL
}
- err := c.fd.Close()
- c.fd = nil
- return err
+ return c.fd.Close()
}
// LocalAddr returns the local network address, a *UnixAddr.
@@ -406,7 +404,7 @@ func (l *UnixListener) SetDeadline(t time.Time) (err error) {
// File returns a copy of the underlying os.File, set to blocking mode.
// It is the caller's responsibility to close f when finished.
-// Closing c does not affect f, and closing f does not affect c.
+// Closing l does not affect f, and closing f does not affect l.
func (l *UnixListener) File() (f *os.File, err error) { return l.fd.dup() }
// ListenUnixgram listens for incoming Unix datagram packets addressed to the
diff --git a/src/pkg/net/url/url.go b/src/pkg/net/url/url.go
index 88ff7ebfe..b6e79adc2 100644
--- a/src/pkg/net/url/url.go
+++ b/src/pkg/net/url/url.go
@@ -61,16 +61,16 @@ func (e EscapeError) Error() string {
}
// Return true if the specified character should be escaped when
-// appearing in a URL string, according to RFC 2396.
+// appearing in a URL string, according to RFC 3986.
// When 'all' is true the full range of reserved characters are matched.
func shouldEscape(c byte, mode encoding) bool {
- // RFC 2396 §2.3 Unreserved characters (alphanum)
+ // §2.3 Unreserved characters (alphanum)
if 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' || '0' <= c && c <= '9' {
return false
}
- // TODO: Update the character sets after RFC 3986.
+
switch c {
- case '-', '_', '.', '!', '~', '*', '\'', '(', ')': // §2.3 Unreserved characters (mark)
+ case '-', '_', '.', '~': // §2.3 Unreserved characters (mark)
return false
case '$', '&', '+', ',', '/', ':', ';', '=', '?', '@': // §2.2 Reserved characters (reserved)
diff --git a/src/pkg/net/url/url_test.go b/src/pkg/net/url/url_test.go
index 2d911ed50..d8b253142 100644
--- a/src/pkg/net/url/url_test.go
+++ b/src/pkg/net/url/url_test.go
@@ -394,8 +394,8 @@ var escapeTests = []EscapeTest{
nil,
},
{
- " ?&=#+%!<>#\"{}|\\^[]`☺\t",
- "+%3F%26%3D%23%2B%25!%3C%3E%23%22%7B%7D%7C%5C%5E%5B%5D%60%E2%98%BA%09",
+ " ?&=#+%!<>#\"{}|\\^[]`☺\t:/@$'()*,;",
+ "+%3F%26%3D%23%2B%25%21%3C%3E%23%22%7B%7D%7C%5C%5E%5B%5D%60%E2%98%BA%09%3A%2F%40%24%27%28%29%2A%2C%3B",
nil,
},
}
diff --git a/src/pkg/os/error_windows.go b/src/pkg/os/error_windows.go
index 5d692b073..fbb0d4f3f 100644
--- a/src/pkg/os/error_windows.go
+++ b/src/pkg/os/error_windows.go
@@ -26,5 +26,5 @@ func isPermission(err error) bool {
if pe, ok := err.(*PathError); ok {
err = pe.Err
}
- return err == ErrPermission
+ return err == syscall.ERROR_ACCESS_DENIED || err == ErrPermission
}
diff --git a/src/pkg/path/filepath/path.go b/src/pkg/path/filepath/path.go
index 1e7487263..a4e429bae 100644
--- a/src/pkg/path/filepath/path.go
+++ b/src/pkg/path/filepath/path.go
@@ -30,6 +30,9 @@ const (
// that is, replace "/.." by "/" at the beginning of a path,
// assuming Separator is '/'.
//
+// The returned path ends in a slash only if it represents a root directory,
+// such as "/" on Unix or `C:\` on Windows.
+//
// If the result of this process is an empty string, Clean
// returns the string ".".
//
diff --git a/src/pkg/path/path.go b/src/pkg/path/path.go
index 13abed0b0..a7e041568 100644
--- a/src/pkg/path/path.go
+++ b/src/pkg/path/path.go
@@ -21,6 +21,8 @@ import (
// 4. Eliminate .. elements that begin a rooted path:
// that is, replace "/.." by "/" at the beginning of a path.
//
+// The returned path ends in a slash only if it is the root "/".
+//
// If the result of this process is an empty string, Clean
// returns the string ".".
//
diff --git a/src/pkg/reflect/all_test.go b/src/pkg/reflect/all_test.go
index 6bb061398..e33140563 100644
--- a/src/pkg/reflect/all_test.go
+++ b/src/pkg/reflect/all_test.go
@@ -638,6 +638,7 @@ var (
var deepEqualTests = []DeepEqualTest{
// Equalities
+ {nil, nil, true},
{1, 1, true},
{int32(1), int32(1), true},
{0.5, 0.5, true},
@@ -696,6 +697,10 @@ func TestDeepEqual(t *testing.T) {
}
func TestTypeOf(t *testing.T) {
+ // Special case for nil
+ if typ := TypeOf(nil); typ != nil {
+ t.Errorf("expected nil type for nil value; got %v", typ)
+ }
for _, test := range deepEqualTests {
v := ValueOf(test.a)
if !v.IsValid() {
diff --git a/src/pkg/reflect/type.go b/src/pkg/reflect/type.go
index 64550b8f6..060bde3af 100644
--- a/src/pkg/reflect/type.go
+++ b/src/pkg/reflect/type.go
@@ -940,6 +940,7 @@ func toType(p *runtimeType) Type {
}
// TypeOf returns the reflection Type of the value in the interface{}.
+// TypeOf(nil) returns nil.
func TypeOf(i interface{}) Type {
eface := *(*emptyInterface)(unsafe.Pointer(&i))
return toType(eface.typ)
diff --git a/src/pkg/runtime/extern.go b/src/pkg/runtime/extern.go
index 5fbfe547e..f9c5b8e3d 100644
--- a/src/pkg/runtime/extern.go
+++ b/src/pkg/runtime/extern.go
@@ -20,7 +20,8 @@ func Goexit()
// Caller reports file and line number information about function invocations on
// the calling goroutine's stack. The argument skip is the number of stack frames
-// to ascend, with 0 identifying the caller of Caller. The return values report the
+// to ascend, with 1 identifying the caller of Caller. (For historical reasons the
+// meaning of skip differs between Caller and Callers.) The return values report the
// program counter, file name, and line number within the file of the corresponding
// call. The boolean ok is false if it was not possible to recover the information.
func Caller(skip int) (pc uintptr, file string, line int, ok bool)
diff --git a/src/pkg/text/template/exec.go b/src/pkg/text/template/exec.go
index 9a720cf43..feb434a3b 100644
--- a/src/pkg/text/template/exec.go
+++ b/src/pkg/text/template/exec.go
@@ -491,7 +491,11 @@ func (s *state) evalCall(dot, fun reflect.Value, name string, args []parse.Node,
}
// Add final value if necessary.
if final.IsValid() {
- argv[i] = final
+ t := typ.In(typ.NumIn() - 1)
+ if typ.IsVariadic() {
+ t = t.Elem()
+ }
+ argv[i] = s.validateType(final, t)
}
result := fun.Call(argv)
// If we have an error that is not nil, stop execution and return that error to the caller.
@@ -507,6 +511,7 @@ func (s *state) validateType(value reflect.Value, typ reflect.Type) reflect.Valu
switch typ.Kind() {
case reflect.Interface, reflect.Ptr, reflect.Chan, reflect.Map, reflect.Slice, reflect.Func:
// An untyped nil interface{}. Accept as a proper nil value.
+ // TODO: Can we delete the other types in this list? Should we?
value = reflect.Zero(typ)
default:
s.errorf("invalid value; expected %s", typ)
diff --git a/src/pkg/text/template/exec_test.go b/src/pkg/text/template/exec_test.go
index 5446027ff..37d25f470 100644
--- a/src/pkg/text/template/exec_test.go
+++ b/src/pkg/text/template/exec_test.go
@@ -470,6 +470,9 @@ var execTests = []execTest{
{"bug7a", "{{3 2}}", "", tVal, false},
{"bug7b", "{{$x := 1}}{{$x 2}}", "", tVal, false},
{"bug7c", "{{$x := 1}}{{3 | $x}}", "", tVal, false},
+ // Pipelined arg was not being type-checked.
+ {"bug8a", "{{3|oneArg}}", "", tVal, false},
+ {"bug8b", "{{4|dddArg 3}}", "", tVal, false},
}
func zeroArgs() string {
@@ -480,6 +483,10 @@ func oneArg(a string) string {
return "oneArg=" + a
}
+func dddArg(a int, b ...string) string {
+ return fmt.Sprintln(a, b)
+}
+
// count returns a channel that will deliver n sequential 1-letter strings starting at "a"
func count(n int) chan string {
if n == 0 {
@@ -504,6 +511,7 @@ func testExecute(execTests []execTest, template *Template, t *testing.T) {
b := new(bytes.Buffer)
funcs := FuncMap{
"count": count,
+ "dddArg": dddArg,
"oneArg": oneArg,
"typeOf": typeOf,
"vfunc": vfunc,
diff --git a/src/pkg/text/template/funcs.go b/src/pkg/text/template/funcs.go
index 525179cb4..8fbf0ef50 100644
--- a/src/pkg/text/template/funcs.go
+++ b/src/pkg/text/template/funcs.go
@@ -246,7 +246,7 @@ func not(arg interface{}) (truth bool) {
var (
htmlQuot = []byte("&#34;") // shorter than "&quot;"
- htmlApos = []byte("&#39;") // shorter than "&apos;"
+ htmlApos = []byte("&#39;") // shorter than "&apos;" and apos was not in HTML until HTML5
htmlAmp = []byte("&amp;")
htmlLt = []byte("&lt;")
htmlGt = []byte("&gt;")
diff --git a/src/pkg/time/format.go b/src/pkg/time/format.go
index ad52bab21..efca3a926 100644
--- a/src/pkg/time/format.go
+++ b/src/pkg/time/format.go
@@ -41,8 +41,8 @@ const (
ANSIC = "Mon Jan _2 15:04:05 2006"
UnixDate = "Mon Jan _2 15:04:05 MST 2006"
RubyDate = "Mon Jan 02 15:04:05 -0700 2006"
- RFC822 = "02 Jan 06 1504 MST"
- RFC822Z = "02 Jan 06 1504 -0700" // RFC822 with numeric zone
+ RFC822 = "02 Jan 06 15:04 MST"
+ RFC822Z = "02 Jan 06 15:04 -0700" // RFC822 with numeric zone
RFC850 = "Monday, 02-Jan-06 15:04:05 MST"
RFC1123 = "Mon, 02 Jan 2006 15:04:05 MST"
RFC1123Z = "Mon, 02 Jan 2006 15:04:05 -0700" // RFC1123 with numeric zone
@@ -714,7 +714,7 @@ func Parse(layout, value string) (Time, error) {
}
// Special case: do we have a fractional second but no
// fractional second in the format?
- if len(value) > 2 && value[0] == '.' && isDigit(value, 1) {
+ if len(value) >= 2 && value[0] == '.' && isDigit(value, 1) {
_, std, _ := nextStdChunk(layout)
if len(std) > 0 && std[0] == '.' && isDigit(std, 1) {
// Fractional second in the layout; proceed normally
diff --git a/src/pkg/time/time.go b/src/pkg/time/time.go
index 473bc2a45..2461dac06 100644
--- a/src/pkg/time/time.go
+++ b/src/pkg/time/time.go
@@ -403,7 +403,7 @@ const (
Hour = 60 * Minute
)
-// Duration returns a string representing the duration in the form "72h3m0.5s".
+// String returns a string representing the duration in the form "72h3m0.5s".
// Leading zero units are omitted. As a special case, durations less than one
// second format use a smaller unit (milli-, micro-, or nanoseconds) to ensure
// that the leading digit is non-zero. The zero duration formats as 0,
@@ -763,7 +763,9 @@ func (t Time) Unix() int64 {
}
// UnixNano returns t as a Unix time, the number of nanoseconds elapsed
-// since January 1, 1970 UTC.
+// since January 1, 1970 UTC. The result is undefined if the Unix time
+// in nanoseconds cannot be represented by an int64. Note that this
+// means the result of calling UnixNano on the zero Time is undefined.
func (t Time) UnixNano() int64 {
return (t.sec+internalToUnix)*1e9 + int64(t.nsec)
}
diff --git a/src/pkg/time/time_test.go b/src/pkg/time/time_test.go
index 3430526b8..28047804e 100644
--- a/src/pkg/time/time_test.go
+++ b/src/pkg/time/time_test.go
@@ -223,7 +223,7 @@ var formatTests = []FormatTest{
{"ANSIC", ANSIC, "Wed Feb 4 21:00:57 2009"},
{"UnixDate", UnixDate, "Wed Feb 4 21:00:57 PST 2009"},
{"RubyDate", RubyDate, "Wed Feb 04 21:00:57 -0800 2009"},
- {"RFC822", RFC822, "04 Feb 09 2100 PST"},
+ {"RFC822", RFC822, "04 Feb 09 21:00 PST"},
{"RFC850", RFC850, "Wednesday, 04-Feb-09 21:00:57 PST"},
{"RFC1123", RFC1123, "Wed, 04 Feb 2009 21:00:57 PST"},
{"RFC1123Z", RFC1123Z, "Wed, 04 Feb 2009 21:00:57 -0800"},
@@ -310,6 +310,7 @@ var parseTests = []ParseTest{
{"RFC1123", RFC1123, "Thu, 04 Feb 2010 21:00:57.01234 PST", true, true, 1, 5},
{"RFC1123Z", RFC1123Z, "Thu, 04 Feb 2010 21:00:57.01234 -0800", true, true, 1, 5},
{"RFC3339", RFC3339, "2010-02-04T21:00:57.012345678-08:00", true, false, 1, 9},
+ {"custom: \"2006-01-02 15:04:05\"", "2006-01-02 15:04:05", "2010-02-04 21:00:57.0", false, false, 1, 0},
// Amount of white space should not matter.
{"ANSIC", ANSIC, "Thu Feb 4 21:00:57 2010", false, true, 1, 0},
{"ANSIC", ANSIC, "Thu Feb 4 21:00:57 2010", false, true, 1, 0},
diff --git a/src/pkg/time/zoneinfo_windows.go b/src/pkg/time/zoneinfo_windows.go
index 754e392de..d596fab93 100644
--- a/src/pkg/time/zoneinfo_windows.go
+++ b/src/pkg/time/zoneinfo_windows.go
@@ -83,6 +83,9 @@ func initLocalFromTZI(i *syscall.Timezoneinformation) {
l.cacheStart = -1 << 63
l.cacheEnd = 1<<63 - 1
l.cacheZone = std
+ l.tx = make([]zoneTrans, 1)
+ l.tx[0].when = l.cacheStart
+ l.tx[0].index = 0
return
}
diff --git a/src/sudo.bash b/src/sudo.bash
index cccebd342..40f8d1aa1 100755
--- a/src/sudo.bash
+++ b/src/sudo.bash
@@ -12,12 +12,17 @@ Darwin)
exit 0
esac
+eval $(go env)
+if ! [ -x $GOTOOLDIR/cov -a -x $GOTOOLDIR/prof ]; then
+ echo "You don't need to run sudo.bash." >&2
+ exit 2
+fi
+
if [[ ! -d /usr/local/bin ]]; then
echo 1>&2 'sudo.bash: problem with /usr/local/bin; cannot install tools.'
exit 2
fi
-eval $(go env)
cd $(dirname $0)
for i in prof cov
do