mirror of https://github.com/docker/cli.git
Update vndr commit to latest
Some fixes and it also preserves directories with *.c files now.
See a6e196d8b4...1fc68ee0c8
Signed-off-by: Vincent Demeester <vincent@sbr.pm>
This commit is contained in:
parent
fd6165399d
commit
08d6c18570
|
@ -3,7 +3,7 @@ FROM golang:1.10.2-alpine
|
||||||
|
|
||||||
RUN apk add -U git make bash coreutils ca-certificates curl
|
RUN apk add -U git make bash coreutils ca-certificates curl
|
||||||
|
|
||||||
ARG VNDR_SHA=a6e196d8b4b0cbbdc29aebdb20c59ac6926bb384
|
ARG VNDR_SHA=1fc68ee0c852556a9ed53cbde16247033f104111
|
||||||
RUN go get -d github.com/LK4D4/vndr && \
|
RUN go get -d github.com/LK4D4/vndr && \
|
||||||
cd /go/src/github.com/LK4D4/vndr && \
|
cd /go/src/github.com/LK4D4/vndr && \
|
||||||
git checkout -q "$VNDR_SHA" && \
|
git checkout -q "$VNDR_SHA" && \
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
The `contrib` directory contains scripts, images, and other helpful things
|
||||||
|
which are not part of the core docker distribution. Please note that they
|
||||||
|
could be out of date, since they do not receive the same attention as the
|
||||||
|
rest of the repository.
|
|
@ -0,0 +1,10 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
printf("EUID=%d\n", geteuid());
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
#define _GNU_SOURCE
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
int err = acct("/tmp/t");
|
||||||
|
if (err == -1) {
|
||||||
|
fprintf(stderr, "acct failed: %s\n", strerror(errno));
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
exit(EXIT_SUCCESS);
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
.globl _start
|
||||||
|
.text
|
||||||
|
_start:
|
||||||
|
xorl %eax, %eax
|
||||||
|
incl %eax
|
||||||
|
movb $0, %bl
|
||||||
|
int $0x80
|
|
@ -0,0 +1,63 @@
|
||||||
|
#define _GNU_SOURCE
|
||||||
|
#include <errno.h>
|
||||||
|
#include <sched.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#define STACK_SIZE (1024 * 1024) /* Stack size for cloned child */
|
||||||
|
|
||||||
|
struct clone_args {
|
||||||
|
char **argv;
|
||||||
|
};
|
||||||
|
|
||||||
|
// child_exec is the func that will be executed as the result of clone
|
||||||
|
static int child_exec(void *stuff)
|
||||||
|
{
|
||||||
|
struct clone_args *args = (struct clone_args *)stuff;
|
||||||
|
if (execvp(args->argv[0], args->argv) != 0) {
|
||||||
|
fprintf(stderr, "failed to execvp arguments %s\n",
|
||||||
|
strerror(errno));
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
// we should never reach here!
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
struct clone_args args;
|
||||||
|
args.argv = &argv[1];
|
||||||
|
|
||||||
|
int clone_flags = CLONE_NEWNS | CLONE_NEWPID | SIGCHLD;
|
||||||
|
|
||||||
|
// allocate stack for child
|
||||||
|
char *stack; /* Start of stack buffer */
|
||||||
|
char *child_stack; /* End of stack buffer */
|
||||||
|
stack =
|
||||||
|
mmap(NULL, STACK_SIZE, PROT_READ | PROT_WRITE,
|
||||||
|
MAP_SHARED | MAP_ANON | MAP_STACK, -1, 0);
|
||||||
|
if (stack == MAP_FAILED) {
|
||||||
|
fprintf(stderr, "mmap failed: %s\n", strerror(errno));
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
child_stack = stack + STACK_SIZE; /* Assume stack grows downward */
|
||||||
|
|
||||||
|
// the result of this call is that our child_exec will be run in another
|
||||||
|
// process returning its pid
|
||||||
|
pid_t pid = clone(child_exec, child_stack, clone_flags, &args);
|
||||||
|
if (pid < 0) {
|
||||||
|
fprintf(stderr, "clone failed: %s\n", strerror(errno));
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
// lets wait on our child process here before we, the parent, exits
|
||||||
|
if (waitpid(pid, NULL, 0) == -1) {
|
||||||
|
fprintf(stderr, "failed to wait pid %d\n", pid);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
exit(EXIT_SUCCESS);
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <netinet/ip.h>
|
||||||
|
#include <netinet/udp.h>
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
if (socket(PF_INET, SOCK_RAW, IPPROTO_UDP) == -1) {
|
||||||
|
perror("socket");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
if (setgid(1) == -1) {
|
||||||
|
perror("setgid");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
if (setuid(1) == -1) {
|
||||||
|
perror("setuid");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
int s;
|
||||||
|
struct sockaddr_in sin;
|
||||||
|
|
||||||
|
s = socket(AF_INET, SOCK_STREAM, 0);
|
||||||
|
if (s == -1) {
|
||||||
|
perror("socket");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
sin.sin_family = AF_INET;
|
||||||
|
sin.sin_addr.s_addr = INADDR_ANY;
|
||||||
|
sin.sin_port = htons(80);
|
||||||
|
|
||||||
|
if (bind(s, (struct sockaddr *)&sin, sizeof(sin)) == -1) {
|
||||||
|
perror("bind");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
close(s);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,63 @@
|
||||||
|
#define _GNU_SOURCE
|
||||||
|
#include <errno.h>
|
||||||
|
#include <sched.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#define STACK_SIZE (1024 * 1024) /* Stack size for cloned child */
|
||||||
|
|
||||||
|
struct clone_args {
|
||||||
|
char **argv;
|
||||||
|
};
|
||||||
|
|
||||||
|
// child_exec is the func that will be executed as the result of clone
|
||||||
|
static int child_exec(void *stuff)
|
||||||
|
{
|
||||||
|
struct clone_args *args = (struct clone_args *)stuff;
|
||||||
|
if (execvp(args->argv[0], args->argv) != 0) {
|
||||||
|
fprintf(stderr, "failed to execvp arguments %s\n",
|
||||||
|
strerror(errno));
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
// we should never reach here!
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
struct clone_args args;
|
||||||
|
args.argv = &argv[1];
|
||||||
|
|
||||||
|
int clone_flags = CLONE_NEWUSER | SIGCHLD;
|
||||||
|
|
||||||
|
// allocate stack for child
|
||||||
|
char *stack; /* Start of stack buffer */
|
||||||
|
char *child_stack; /* End of stack buffer */
|
||||||
|
stack =
|
||||||
|
mmap(NULL, STACK_SIZE, PROT_READ | PROT_WRITE,
|
||||||
|
MAP_SHARED | MAP_ANON | MAP_STACK, -1, 0);
|
||||||
|
if (stack == MAP_FAILED) {
|
||||||
|
fprintf(stderr, "mmap failed: %s\n", strerror(errno));
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
child_stack = stack + STACK_SIZE; /* Assume stack grows downward */
|
||||||
|
|
||||||
|
// the result of this call is that our child_exec will be run in another
|
||||||
|
// process returning its pid
|
||||||
|
pid_t pid = clone(child_exec, child_stack, clone_flags, &args);
|
||||||
|
if (pid < 0) {
|
||||||
|
fprintf(stderr, "clone failed: %s\n", strerror(errno));
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
// lets wait on our child process here before we, the parent, exits
|
||||||
|
if (waitpid(pid, NULL, 0) == -1) {
|
||||||
|
fprintf(stderr, "failed to wait pid %d\n", pid);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
exit(EXIT_SUCCESS);
|
||||||
|
}
|
|
@ -0,0 +1,197 @@
|
||||||
|
// Copyright 2012 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.
|
||||||
|
|
||||||
|
// This code can be compiled and used to test the otr package against libotr.
|
||||||
|
// See otr_test.go.
|
||||||
|
|
||||||
|
// +build ignore
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include <proto.h>
|
||||||
|
#include <message.h>
|
||||||
|
#include <privkey.h>
|
||||||
|
|
||||||
|
static int g_session_established = 0;
|
||||||
|
|
||||||
|
OtrlPolicy policy(void *opdata, ConnContext *context) {
|
||||||
|
return OTRL_POLICY_ALWAYS;
|
||||||
|
}
|
||||||
|
|
||||||
|
int is_logged_in(void *opdata, const char *accountname, const char *protocol,
|
||||||
|
const char *recipient) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void inject_message(void *opdata, const char *accountname, const char *protocol,
|
||||||
|
const char *recipient, const char *message) {
|
||||||
|
printf("%s\n", message);
|
||||||
|
fflush(stdout);
|
||||||
|
fprintf(stderr, "libotr helper sent: %s\n", message);
|
||||||
|
}
|
||||||
|
|
||||||
|
void update_context_list(void *opdata) {}
|
||||||
|
|
||||||
|
void new_fingerprint(void *opdata, OtrlUserState us, const char *accountname,
|
||||||
|
const char *protocol, const char *username,
|
||||||
|
unsigned char fingerprint[20]) {
|
||||||
|
fprintf(stderr, "NEW FINGERPRINT\n");
|
||||||
|
g_session_established = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void write_fingerprints(void *opdata) {}
|
||||||
|
|
||||||
|
void gone_secure(void *opdata, ConnContext *context) {}
|
||||||
|
|
||||||
|
void gone_insecure(void *opdata, ConnContext *context) {}
|
||||||
|
|
||||||
|
void still_secure(void *opdata, ConnContext *context, int is_reply) {}
|
||||||
|
|
||||||
|
int max_message_size(void *opdata, ConnContext *context) { return 99999; }
|
||||||
|
|
||||||
|
const char *account_name(void *opdata, const char *account,
|
||||||
|
const char *protocol) {
|
||||||
|
return "ACCOUNT";
|
||||||
|
}
|
||||||
|
|
||||||
|
void account_name_free(void *opdata, const char *account_name) {}
|
||||||
|
|
||||||
|
const char *error_message(void *opdata, ConnContext *context,
|
||||||
|
OtrlErrorCode err_code) {
|
||||||
|
return "ERR";
|
||||||
|
}
|
||||||
|
|
||||||
|
void error_message_free(void *opdata, const char *msg) {}
|
||||||
|
|
||||||
|
void resent_msg_prefix_free(void *opdata, const char *prefix) {}
|
||||||
|
|
||||||
|
void handle_smp_event(void *opdata, OtrlSMPEvent smp_event,
|
||||||
|
ConnContext *context, unsigned short progress_event,
|
||||||
|
char *question) {}
|
||||||
|
|
||||||
|
void handle_msg_event(void *opdata, OtrlMessageEvent msg_event,
|
||||||
|
ConnContext *context, const char *message,
|
||||||
|
gcry_error_t err) {
|
||||||
|
fprintf(stderr, "msg event: %d %s\n", msg_event, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
OtrlMessageAppOps uiops = {
|
||||||
|
policy,
|
||||||
|
NULL,
|
||||||
|
is_logged_in,
|
||||||
|
inject_message,
|
||||||
|
update_context_list,
|
||||||
|
new_fingerprint,
|
||||||
|
write_fingerprints,
|
||||||
|
gone_secure,
|
||||||
|
gone_insecure,
|
||||||
|
still_secure,
|
||||||
|
max_message_size,
|
||||||
|
account_name,
|
||||||
|
account_name_free,
|
||||||
|
NULL, /* received_symkey */
|
||||||
|
error_message,
|
||||||
|
error_message_free,
|
||||||
|
NULL, /* resent_msg_prefix */
|
||||||
|
resent_msg_prefix_free,
|
||||||
|
handle_smp_event,
|
||||||
|
handle_msg_event,
|
||||||
|
NULL /* create_instag */,
|
||||||
|
NULL /* convert_msg */,
|
||||||
|
NULL /* convert_free */,
|
||||||
|
NULL /* timer_control */,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const char kPrivateKeyData[] =
|
||||||
|
"(privkeys (account (name \"account\") (protocol proto) (private-key (dsa "
|
||||||
|
"(p "
|
||||||
|
"#00FC07ABCF0DC916AFF6E9AE47BEF60C7AB9B4D6B2469E436630E36F8A489BE812486A09F"
|
||||||
|
"30B71224508654940A835301ACC525A4FF133FC152CC53DCC59D65C30A54F1993FE13FE63E"
|
||||||
|
"5823D4C746DB21B90F9B9C00B49EC7404AB1D929BA7FBA12F2E45C6E0A651689750E8528AB"
|
||||||
|
"8C031D3561FECEE72EBB4A090D450A9B7A857#) (q "
|
||||||
|
"#00997BD266EF7B1F60A5C23F3A741F2AEFD07A2081#) (g "
|
||||||
|
"#535E360E8A95EBA46A4F7DE50AD6E9B2A6DB785A66B64EB9F20338D2A3E8FB0E94725848F"
|
||||||
|
"1AA6CC567CB83A1CC517EC806F2E92EAE71457E80B2210A189B91250779434B41FC8A8873F"
|
||||||
|
"6DB94BEA7D177F5D59E7E114EE10A49CFD9CEF88AE43387023B672927BA74B04EB6BBB5E57"
|
||||||
|
"597766A2F9CE3857D7ACE3E1E3BC1FC6F26#) (y "
|
||||||
|
"#0AC8670AD767D7A8D9D14CC1AC6744CD7D76F993B77FFD9E39DF01E5A6536EF65E775FCEF"
|
||||||
|
"2A983E2A19BD6415500F6979715D9FD1257E1FE2B6F5E1E74B333079E7C880D39868462A93"
|
||||||
|
"454B41877BE62E5EF0A041C2EE9C9E76BD1E12AE25D9628DECB097025DD625EF49C3258A1A"
|
||||||
|
"3C0FF501E3DC673B76D7BABF349009B6ECF#) (x "
|
||||||
|
"#14D0345A3562C480A039E3C72764F72D79043216#)))))\n";
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
OTRL_INIT;
|
||||||
|
|
||||||
|
// We have to write the private key information to a file because the libotr
|
||||||
|
// API demands a filename to read from.
|
||||||
|
const char *tmpdir = "/tmp";
|
||||||
|
if (getenv("TMP")) {
|
||||||
|
tmpdir = getenv("TMP");
|
||||||
|
}
|
||||||
|
|
||||||
|
char private_key_file[256];
|
||||||
|
snprintf(private_key_file, sizeof(private_key_file),
|
||||||
|
"%s/libotr_test_helper_privatekeys-XXXXXX", tmpdir);
|
||||||
|
int fd = mkstemp(private_key_file);
|
||||||
|
if (fd == -1) {
|
||||||
|
perror("creating temp file");
|
||||||
|
}
|
||||||
|
write(fd, kPrivateKeyData, sizeof(kPrivateKeyData) - 1);
|
||||||
|
close(fd);
|
||||||
|
|
||||||
|
OtrlUserState userstate = otrl_userstate_create();
|
||||||
|
otrl_privkey_read(userstate, private_key_file);
|
||||||
|
unlink(private_key_file);
|
||||||
|
|
||||||
|
fprintf(stderr, "libotr helper started\n");
|
||||||
|
|
||||||
|
char buf[4096];
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
char *message = fgets(buf, sizeof(buf), stdin);
|
||||||
|
if (strlen(message) == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
message[strlen(message) - 1] = 0;
|
||||||
|
fprintf(stderr, "libotr helper got: %s\n", message);
|
||||||
|
|
||||||
|
char *newmessage = NULL;
|
||||||
|
OtrlTLV *tlvs;
|
||||||
|
int ignore_message = otrl_message_receiving(
|
||||||
|
userstate, &uiops, NULL, "account", "proto", "peer", message,
|
||||||
|
&newmessage, &tlvs, NULL, NULL, NULL);
|
||||||
|
if (tlvs) {
|
||||||
|
otrl_tlv_free(tlvs);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newmessage != NULL) {
|
||||||
|
fprintf(stderr, "libotr got: %s\n", newmessage);
|
||||||
|
otrl_message_free(newmessage);
|
||||||
|
|
||||||
|
gcry_error_t err;
|
||||||
|
char *newmessage = NULL;
|
||||||
|
|
||||||
|
err = otrl_message_sending(userstate, &uiops, NULL, "account", "proto",
|
||||||
|
"peer", 0, "test message", NULL, &newmessage,
|
||||||
|
OTRL_FRAGMENT_SEND_SKIP, NULL, NULL, NULL);
|
||||||
|
if (newmessage == NULL) {
|
||||||
|
fprintf(stderr, "libotr didn't encrypt message\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
write(1, newmessage, strlen(newmessage));
|
||||||
|
write(1, "\n", 1);
|
||||||
|
fprintf(stderr, "libotr sent: %s\n", newmessage);
|
||||||
|
otrl_message_free(newmessage);
|
||||||
|
|
||||||
|
g_session_established = 0;
|
||||||
|
write(1, "?OTRv2?\n", 8);
|
||||||
|
fprintf(stderr, "libotr sent: ?OTRv2\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,572 @@
|
||||||
|
// Copyright 2012 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.
|
||||||
|
|
||||||
|
// This file implements the Socialist Millionaires Protocol as described in
|
||||||
|
// http://www.cypherpunks.ca/otr/Protocol-v2-3.1.0.html. The protocol
|
||||||
|
// specification is required in order to understand this code and, where
|
||||||
|
// possible, the variable names in the code match up with the spec.
|
||||||
|
|
||||||
|
package otr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto/sha256"
|
||||||
|
"errors"
|
||||||
|
"hash"
|
||||||
|
"math/big"
|
||||||
|
)
|
||||||
|
|
||||||
|
type smpFailure string
|
||||||
|
|
||||||
|
func (s smpFailure) Error() string {
|
||||||
|
return string(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
var smpFailureError = smpFailure("otr: SMP protocol failed")
|
||||||
|
var smpSecretMissingError = smpFailure("otr: mutual secret needed")
|
||||||
|
|
||||||
|
const smpVersion = 1
|
||||||
|
|
||||||
|
const (
|
||||||
|
smpState1 = iota
|
||||||
|
smpState2
|
||||||
|
smpState3
|
||||||
|
smpState4
|
||||||
|
)
|
||||||
|
|
||||||
|
type smpState struct {
|
||||||
|
state int
|
||||||
|
a2, a3, b2, b3, pb, qb *big.Int
|
||||||
|
g2a, g3a *big.Int
|
||||||
|
g2, g3 *big.Int
|
||||||
|
g3b, papb, qaqb, ra *big.Int
|
||||||
|
saved *tlv
|
||||||
|
secret *big.Int
|
||||||
|
question string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Conversation) startSMP(question string) (tlvs []tlv) {
|
||||||
|
if c.smp.state != smpState1 {
|
||||||
|
tlvs = append(tlvs, c.generateSMPAbort())
|
||||||
|
}
|
||||||
|
tlvs = append(tlvs, c.generateSMP1(question))
|
||||||
|
c.smp.question = ""
|
||||||
|
c.smp.state = smpState2
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Conversation) resetSMP() {
|
||||||
|
c.smp.state = smpState1
|
||||||
|
c.smp.secret = nil
|
||||||
|
c.smp.question = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Conversation) processSMP(in tlv) (out tlv, complete bool, err error) {
|
||||||
|
data := in.data
|
||||||
|
|
||||||
|
switch in.typ {
|
||||||
|
case tlvTypeSMPAbort:
|
||||||
|
if c.smp.state != smpState1 {
|
||||||
|
err = smpFailureError
|
||||||
|
}
|
||||||
|
c.resetSMP()
|
||||||
|
return
|
||||||
|
case tlvTypeSMP1WithQuestion:
|
||||||
|
// We preprocess this into a SMP1 message.
|
||||||
|
nulPos := bytes.IndexByte(data, 0)
|
||||||
|
if nulPos == -1 {
|
||||||
|
err = errors.New("otr: SMP message with question didn't contain a NUL byte")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.smp.question = string(data[:nulPos])
|
||||||
|
data = data[nulPos+1:]
|
||||||
|
}
|
||||||
|
|
||||||
|
numMPIs, data, ok := getU32(data)
|
||||||
|
if !ok || numMPIs > 20 {
|
||||||
|
err = errors.New("otr: corrupt SMP message")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
mpis := make([]*big.Int, numMPIs)
|
||||||
|
for i := range mpis {
|
||||||
|
var ok bool
|
||||||
|
mpis[i], data, ok = getMPI(data)
|
||||||
|
if !ok {
|
||||||
|
err = errors.New("otr: corrupt SMP message")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch in.typ {
|
||||||
|
case tlvTypeSMP1, tlvTypeSMP1WithQuestion:
|
||||||
|
if c.smp.state != smpState1 {
|
||||||
|
c.resetSMP()
|
||||||
|
out = c.generateSMPAbort()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if c.smp.secret == nil {
|
||||||
|
err = smpSecretMissingError
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err = c.processSMP1(mpis); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.smp.state = smpState3
|
||||||
|
out = c.generateSMP2()
|
||||||
|
case tlvTypeSMP2:
|
||||||
|
if c.smp.state != smpState2 {
|
||||||
|
c.resetSMP()
|
||||||
|
out = c.generateSMPAbort()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if out, err = c.processSMP2(mpis); err != nil {
|
||||||
|
out = c.generateSMPAbort()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.smp.state = smpState4
|
||||||
|
case tlvTypeSMP3:
|
||||||
|
if c.smp.state != smpState3 {
|
||||||
|
c.resetSMP()
|
||||||
|
out = c.generateSMPAbort()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if out, err = c.processSMP3(mpis); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.smp.state = smpState1
|
||||||
|
c.smp.secret = nil
|
||||||
|
complete = true
|
||||||
|
case tlvTypeSMP4:
|
||||||
|
if c.smp.state != smpState4 {
|
||||||
|
c.resetSMP()
|
||||||
|
out = c.generateSMPAbort()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err = c.processSMP4(mpis); err != nil {
|
||||||
|
out = c.generateSMPAbort()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.smp.state = smpState1
|
||||||
|
c.smp.secret = nil
|
||||||
|
complete = true
|
||||||
|
default:
|
||||||
|
panic("unknown SMP message")
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Conversation) calcSMPSecret(mutualSecret []byte, weStarted bool) {
|
||||||
|
h := sha256.New()
|
||||||
|
h.Write([]byte{smpVersion})
|
||||||
|
if weStarted {
|
||||||
|
h.Write(c.PrivateKey.PublicKey.Fingerprint())
|
||||||
|
h.Write(c.TheirPublicKey.Fingerprint())
|
||||||
|
} else {
|
||||||
|
h.Write(c.TheirPublicKey.Fingerprint())
|
||||||
|
h.Write(c.PrivateKey.PublicKey.Fingerprint())
|
||||||
|
}
|
||||||
|
h.Write(c.SSID[:])
|
||||||
|
h.Write(mutualSecret)
|
||||||
|
c.smp.secret = new(big.Int).SetBytes(h.Sum(nil))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Conversation) generateSMP1(question string) tlv {
|
||||||
|
var randBuf [16]byte
|
||||||
|
c.smp.a2 = c.randMPI(randBuf[:])
|
||||||
|
c.smp.a3 = c.randMPI(randBuf[:])
|
||||||
|
g2a := new(big.Int).Exp(g, c.smp.a2, p)
|
||||||
|
g3a := new(big.Int).Exp(g, c.smp.a3, p)
|
||||||
|
h := sha256.New()
|
||||||
|
|
||||||
|
r2 := c.randMPI(randBuf[:])
|
||||||
|
r := new(big.Int).Exp(g, r2, p)
|
||||||
|
c2 := new(big.Int).SetBytes(hashMPIs(h, 1, r))
|
||||||
|
d2 := new(big.Int).Mul(c.smp.a2, c2)
|
||||||
|
d2.Sub(r2, d2)
|
||||||
|
d2.Mod(d2, q)
|
||||||
|
if d2.Sign() < 0 {
|
||||||
|
d2.Add(d2, q)
|
||||||
|
}
|
||||||
|
|
||||||
|
r3 := c.randMPI(randBuf[:])
|
||||||
|
r.Exp(g, r3, p)
|
||||||
|
c3 := new(big.Int).SetBytes(hashMPIs(h, 2, r))
|
||||||
|
d3 := new(big.Int).Mul(c.smp.a3, c3)
|
||||||
|
d3.Sub(r3, d3)
|
||||||
|
d3.Mod(d3, q)
|
||||||
|
if d3.Sign() < 0 {
|
||||||
|
d3.Add(d3, q)
|
||||||
|
}
|
||||||
|
|
||||||
|
var ret tlv
|
||||||
|
if len(question) > 0 {
|
||||||
|
ret.typ = tlvTypeSMP1WithQuestion
|
||||||
|
ret.data = append(ret.data, question...)
|
||||||
|
ret.data = append(ret.data, 0)
|
||||||
|
} else {
|
||||||
|
ret.typ = tlvTypeSMP1
|
||||||
|
}
|
||||||
|
ret.data = appendU32(ret.data, 6)
|
||||||
|
ret.data = appendMPIs(ret.data, g2a, c2, d2, g3a, c3, d3)
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Conversation) processSMP1(mpis []*big.Int) error {
|
||||||
|
if len(mpis) != 6 {
|
||||||
|
return errors.New("otr: incorrect number of arguments in SMP1 message")
|
||||||
|
}
|
||||||
|
g2a := mpis[0]
|
||||||
|
c2 := mpis[1]
|
||||||
|
d2 := mpis[2]
|
||||||
|
g3a := mpis[3]
|
||||||
|
c3 := mpis[4]
|
||||||
|
d3 := mpis[5]
|
||||||
|
h := sha256.New()
|
||||||
|
|
||||||
|
r := new(big.Int).Exp(g, d2, p)
|
||||||
|
s := new(big.Int).Exp(g2a, c2, p)
|
||||||
|
r.Mul(r, s)
|
||||||
|
r.Mod(r, p)
|
||||||
|
t := new(big.Int).SetBytes(hashMPIs(h, 1, r))
|
||||||
|
if c2.Cmp(t) != 0 {
|
||||||
|
return errors.New("otr: ZKP c2 incorrect in SMP1 message")
|
||||||
|
}
|
||||||
|
r.Exp(g, d3, p)
|
||||||
|
s.Exp(g3a, c3, p)
|
||||||
|
r.Mul(r, s)
|
||||||
|
r.Mod(r, p)
|
||||||
|
t.SetBytes(hashMPIs(h, 2, r))
|
||||||
|
if c3.Cmp(t) != 0 {
|
||||||
|
return errors.New("otr: ZKP c3 incorrect in SMP1 message")
|
||||||
|
}
|
||||||
|
|
||||||
|
c.smp.g2a = g2a
|
||||||
|
c.smp.g3a = g3a
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Conversation) generateSMP2() tlv {
|
||||||
|
var randBuf [16]byte
|
||||||
|
b2 := c.randMPI(randBuf[:])
|
||||||
|
c.smp.b3 = c.randMPI(randBuf[:])
|
||||||
|
r2 := c.randMPI(randBuf[:])
|
||||||
|
r3 := c.randMPI(randBuf[:])
|
||||||
|
r4 := c.randMPI(randBuf[:])
|
||||||
|
r5 := c.randMPI(randBuf[:])
|
||||||
|
r6 := c.randMPI(randBuf[:])
|
||||||
|
|
||||||
|
g2b := new(big.Int).Exp(g, b2, p)
|
||||||
|
g3b := new(big.Int).Exp(g, c.smp.b3, p)
|
||||||
|
|
||||||
|
r := new(big.Int).Exp(g, r2, p)
|
||||||
|
h := sha256.New()
|
||||||
|
c2 := new(big.Int).SetBytes(hashMPIs(h, 3, r))
|
||||||
|
d2 := new(big.Int).Mul(b2, c2)
|
||||||
|
d2.Sub(r2, d2)
|
||||||
|
d2.Mod(d2, q)
|
||||||
|
if d2.Sign() < 0 {
|
||||||
|
d2.Add(d2, q)
|
||||||
|
}
|
||||||
|
|
||||||
|
r.Exp(g, r3, p)
|
||||||
|
c3 := new(big.Int).SetBytes(hashMPIs(h, 4, r))
|
||||||
|
d3 := new(big.Int).Mul(c.smp.b3, c3)
|
||||||
|
d3.Sub(r3, d3)
|
||||||
|
d3.Mod(d3, q)
|
||||||
|
if d3.Sign() < 0 {
|
||||||
|
d3.Add(d3, q)
|
||||||
|
}
|
||||||
|
|
||||||
|
c.smp.g2 = new(big.Int).Exp(c.smp.g2a, b2, p)
|
||||||
|
c.smp.g3 = new(big.Int).Exp(c.smp.g3a, c.smp.b3, p)
|
||||||
|
c.smp.pb = new(big.Int).Exp(c.smp.g3, r4, p)
|
||||||
|
c.smp.qb = new(big.Int).Exp(g, r4, p)
|
||||||
|
r.Exp(c.smp.g2, c.smp.secret, p)
|
||||||
|
c.smp.qb.Mul(c.smp.qb, r)
|
||||||
|
c.smp.qb.Mod(c.smp.qb, p)
|
||||||
|
|
||||||
|
s := new(big.Int)
|
||||||
|
s.Exp(c.smp.g2, r6, p)
|
||||||
|
r.Exp(g, r5, p)
|
||||||
|
s.Mul(r, s)
|
||||||
|
s.Mod(s, p)
|
||||||
|
r.Exp(c.smp.g3, r5, p)
|
||||||
|
cp := new(big.Int).SetBytes(hashMPIs(h, 5, r, s))
|
||||||
|
|
||||||
|
// D5 = r5 - r4 cP mod q and D6 = r6 - y cP mod q
|
||||||
|
|
||||||
|
s.Mul(r4, cp)
|
||||||
|
r.Sub(r5, s)
|
||||||
|
d5 := new(big.Int).Mod(r, q)
|
||||||
|
if d5.Sign() < 0 {
|
||||||
|
d5.Add(d5, q)
|
||||||
|
}
|
||||||
|
|
||||||
|
s.Mul(c.smp.secret, cp)
|
||||||
|
r.Sub(r6, s)
|
||||||
|
d6 := new(big.Int).Mod(r, q)
|
||||||
|
if d6.Sign() < 0 {
|
||||||
|
d6.Add(d6, q)
|
||||||
|
}
|
||||||
|
|
||||||
|
var ret tlv
|
||||||
|
ret.typ = tlvTypeSMP2
|
||||||
|
ret.data = appendU32(ret.data, 11)
|
||||||
|
ret.data = appendMPIs(ret.data, g2b, c2, d2, g3b, c3, d3, c.smp.pb, c.smp.qb, cp, d5, d6)
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Conversation) processSMP2(mpis []*big.Int) (out tlv, err error) {
|
||||||
|
if len(mpis) != 11 {
|
||||||
|
err = errors.New("otr: incorrect number of arguments in SMP2 message")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
g2b := mpis[0]
|
||||||
|
c2 := mpis[1]
|
||||||
|
d2 := mpis[2]
|
||||||
|
g3b := mpis[3]
|
||||||
|
c3 := mpis[4]
|
||||||
|
d3 := mpis[5]
|
||||||
|
pb := mpis[6]
|
||||||
|
qb := mpis[7]
|
||||||
|
cp := mpis[8]
|
||||||
|
d5 := mpis[9]
|
||||||
|
d6 := mpis[10]
|
||||||
|
h := sha256.New()
|
||||||
|
|
||||||
|
r := new(big.Int).Exp(g, d2, p)
|
||||||
|
s := new(big.Int).Exp(g2b, c2, p)
|
||||||
|
r.Mul(r, s)
|
||||||
|
r.Mod(r, p)
|
||||||
|
s.SetBytes(hashMPIs(h, 3, r))
|
||||||
|
if c2.Cmp(s) != 0 {
|
||||||
|
err = errors.New("otr: ZKP c2 failed in SMP2 message")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
r.Exp(g, d3, p)
|
||||||
|
s.Exp(g3b, c3, p)
|
||||||
|
r.Mul(r, s)
|
||||||
|
r.Mod(r, p)
|
||||||
|
s.SetBytes(hashMPIs(h, 4, r))
|
||||||
|
if c3.Cmp(s) != 0 {
|
||||||
|
err = errors.New("otr: ZKP c3 failed in SMP2 message")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.smp.g2 = new(big.Int).Exp(g2b, c.smp.a2, p)
|
||||||
|
c.smp.g3 = new(big.Int).Exp(g3b, c.smp.a3, p)
|
||||||
|
|
||||||
|
r.Exp(g, d5, p)
|
||||||
|
s.Exp(c.smp.g2, d6, p)
|
||||||
|
r.Mul(r, s)
|
||||||
|
s.Exp(qb, cp, p)
|
||||||
|
r.Mul(r, s)
|
||||||
|
r.Mod(r, p)
|
||||||
|
|
||||||
|
s.Exp(c.smp.g3, d5, p)
|
||||||
|
t := new(big.Int).Exp(pb, cp, p)
|
||||||
|
s.Mul(s, t)
|
||||||
|
s.Mod(s, p)
|
||||||
|
t.SetBytes(hashMPIs(h, 5, s, r))
|
||||||
|
if cp.Cmp(t) != 0 {
|
||||||
|
err = errors.New("otr: ZKP cP failed in SMP2 message")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var randBuf [16]byte
|
||||||
|
r4 := c.randMPI(randBuf[:])
|
||||||
|
r5 := c.randMPI(randBuf[:])
|
||||||
|
r6 := c.randMPI(randBuf[:])
|
||||||
|
r7 := c.randMPI(randBuf[:])
|
||||||
|
|
||||||
|
pa := new(big.Int).Exp(c.smp.g3, r4, p)
|
||||||
|
r.Exp(c.smp.g2, c.smp.secret, p)
|
||||||
|
qa := new(big.Int).Exp(g, r4, p)
|
||||||
|
qa.Mul(qa, r)
|
||||||
|
qa.Mod(qa, p)
|
||||||
|
|
||||||
|
r.Exp(g, r5, p)
|
||||||
|
s.Exp(c.smp.g2, r6, p)
|
||||||
|
r.Mul(r, s)
|
||||||
|
r.Mod(r, p)
|
||||||
|
|
||||||
|
s.Exp(c.smp.g3, r5, p)
|
||||||
|
cp.SetBytes(hashMPIs(h, 6, s, r))
|
||||||
|
|
||||||
|
r.Mul(r4, cp)
|
||||||
|
d5 = new(big.Int).Sub(r5, r)
|
||||||
|
d5.Mod(d5, q)
|
||||||
|
if d5.Sign() < 0 {
|
||||||
|
d5.Add(d5, q)
|
||||||
|
}
|
||||||
|
|
||||||
|
r.Mul(c.smp.secret, cp)
|
||||||
|
d6 = new(big.Int).Sub(r6, r)
|
||||||
|
d6.Mod(d6, q)
|
||||||
|
if d6.Sign() < 0 {
|
||||||
|
d6.Add(d6, q)
|
||||||
|
}
|
||||||
|
|
||||||
|
r.ModInverse(qb, p)
|
||||||
|
qaqb := new(big.Int).Mul(qa, r)
|
||||||
|
qaqb.Mod(qaqb, p)
|
||||||
|
|
||||||
|
ra := new(big.Int).Exp(qaqb, c.smp.a3, p)
|
||||||
|
r.Exp(qaqb, r7, p)
|
||||||
|
s.Exp(g, r7, p)
|
||||||
|
cr := new(big.Int).SetBytes(hashMPIs(h, 7, s, r))
|
||||||
|
|
||||||
|
r.Mul(c.smp.a3, cr)
|
||||||
|
d7 := new(big.Int).Sub(r7, r)
|
||||||
|
d7.Mod(d7, q)
|
||||||
|
if d7.Sign() < 0 {
|
||||||
|
d7.Add(d7, q)
|
||||||
|
}
|
||||||
|
|
||||||
|
c.smp.g3b = g3b
|
||||||
|
c.smp.qaqb = qaqb
|
||||||
|
|
||||||
|
r.ModInverse(pb, p)
|
||||||
|
c.smp.papb = new(big.Int).Mul(pa, r)
|
||||||
|
c.smp.papb.Mod(c.smp.papb, p)
|
||||||
|
c.smp.ra = ra
|
||||||
|
|
||||||
|
out.typ = tlvTypeSMP3
|
||||||
|
out.data = appendU32(out.data, 8)
|
||||||
|
out.data = appendMPIs(out.data, pa, qa, cp, d5, d6, ra, cr, d7)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Conversation) processSMP3(mpis []*big.Int) (out tlv, err error) {
|
||||||
|
if len(mpis) != 8 {
|
||||||
|
err = errors.New("otr: incorrect number of arguments in SMP3 message")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
pa := mpis[0]
|
||||||
|
qa := mpis[1]
|
||||||
|
cp := mpis[2]
|
||||||
|
d5 := mpis[3]
|
||||||
|
d6 := mpis[4]
|
||||||
|
ra := mpis[5]
|
||||||
|
cr := mpis[6]
|
||||||
|
d7 := mpis[7]
|
||||||
|
h := sha256.New()
|
||||||
|
|
||||||
|
r := new(big.Int).Exp(g, d5, p)
|
||||||
|
s := new(big.Int).Exp(c.smp.g2, d6, p)
|
||||||
|
r.Mul(r, s)
|
||||||
|
s.Exp(qa, cp, p)
|
||||||
|
r.Mul(r, s)
|
||||||
|
r.Mod(r, p)
|
||||||
|
|
||||||
|
s.Exp(c.smp.g3, d5, p)
|
||||||
|
t := new(big.Int).Exp(pa, cp, p)
|
||||||
|
s.Mul(s, t)
|
||||||
|
s.Mod(s, p)
|
||||||
|
t.SetBytes(hashMPIs(h, 6, s, r))
|
||||||
|
if t.Cmp(cp) != 0 {
|
||||||
|
err = errors.New("otr: ZKP cP failed in SMP3 message")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
r.ModInverse(c.smp.qb, p)
|
||||||
|
qaqb := new(big.Int).Mul(qa, r)
|
||||||
|
qaqb.Mod(qaqb, p)
|
||||||
|
|
||||||
|
r.Exp(qaqb, d7, p)
|
||||||
|
s.Exp(ra, cr, p)
|
||||||
|
r.Mul(r, s)
|
||||||
|
r.Mod(r, p)
|
||||||
|
|
||||||
|
s.Exp(g, d7, p)
|
||||||
|
t.Exp(c.smp.g3a, cr, p)
|
||||||
|
s.Mul(s, t)
|
||||||
|
s.Mod(s, p)
|
||||||
|
t.SetBytes(hashMPIs(h, 7, s, r))
|
||||||
|
if t.Cmp(cr) != 0 {
|
||||||
|
err = errors.New("otr: ZKP cR failed in SMP3 message")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var randBuf [16]byte
|
||||||
|
r7 := c.randMPI(randBuf[:])
|
||||||
|
rb := new(big.Int).Exp(qaqb, c.smp.b3, p)
|
||||||
|
|
||||||
|
r.Exp(qaqb, r7, p)
|
||||||
|
s.Exp(g, r7, p)
|
||||||
|
cr = new(big.Int).SetBytes(hashMPIs(h, 8, s, r))
|
||||||
|
|
||||||
|
r.Mul(c.smp.b3, cr)
|
||||||
|
d7 = new(big.Int).Sub(r7, r)
|
||||||
|
d7.Mod(d7, q)
|
||||||
|
if d7.Sign() < 0 {
|
||||||
|
d7.Add(d7, q)
|
||||||
|
}
|
||||||
|
|
||||||
|
out.typ = tlvTypeSMP4
|
||||||
|
out.data = appendU32(out.data, 3)
|
||||||
|
out.data = appendMPIs(out.data, rb, cr, d7)
|
||||||
|
|
||||||
|
r.ModInverse(c.smp.pb, p)
|
||||||
|
r.Mul(pa, r)
|
||||||
|
r.Mod(r, p)
|
||||||
|
s.Exp(ra, c.smp.b3, p)
|
||||||
|
if r.Cmp(s) != 0 {
|
||||||
|
err = smpFailureError
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Conversation) processSMP4(mpis []*big.Int) error {
|
||||||
|
if len(mpis) != 3 {
|
||||||
|
return errors.New("otr: incorrect number of arguments in SMP4 message")
|
||||||
|
}
|
||||||
|
rb := mpis[0]
|
||||||
|
cr := mpis[1]
|
||||||
|
d7 := mpis[2]
|
||||||
|
h := sha256.New()
|
||||||
|
|
||||||
|
r := new(big.Int).Exp(c.smp.qaqb, d7, p)
|
||||||
|
s := new(big.Int).Exp(rb, cr, p)
|
||||||
|
r.Mul(r, s)
|
||||||
|
r.Mod(r, p)
|
||||||
|
|
||||||
|
s.Exp(g, d7, p)
|
||||||
|
t := new(big.Int).Exp(c.smp.g3b, cr, p)
|
||||||
|
s.Mul(s, t)
|
||||||
|
s.Mod(s, p)
|
||||||
|
t.SetBytes(hashMPIs(h, 8, s, r))
|
||||||
|
if t.Cmp(cr) != 0 {
|
||||||
|
return errors.New("otr: ZKP cR failed in SMP4 message")
|
||||||
|
}
|
||||||
|
|
||||||
|
r.Exp(rb, c.smp.a3, p)
|
||||||
|
if r.Cmp(c.smp.papb) != 0 {
|
||||||
|
return smpFailureError
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Conversation) generateSMPAbort() tlv {
|
||||||
|
return tlv{typ: tlvTypeSMPAbort}
|
||||||
|
}
|
||||||
|
|
||||||
|
func hashMPIs(h hash.Hash, magic byte, mpis ...*big.Int) []byte {
|
||||||
|
if h != nil {
|
||||||
|
h.Reset()
|
||||||
|
} else {
|
||||||
|
h = sha256.New()
|
||||||
|
}
|
||||||
|
|
||||||
|
h.Write([]byte{magic})
|
||||||
|
for _, mpi := range mpis {
|
||||||
|
h.Write(appendMPI(nil, mpi))
|
||||||
|
}
|
||||||
|
return h.Sum(nil)
|
||||||
|
}
|
|
@ -0,0 +1,48 @@
|
||||||
|
// Copyright 2012 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.
|
||||||
|
|
||||||
|
// +build windows
|
||||||
|
|
||||||
|
package svc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"golang.org/x/sys/windows"
|
||||||
|
)
|
||||||
|
|
||||||
|
// event represents auto-reset, initially non-signaled Windows event.
|
||||||
|
// It is used to communicate between go and asm parts of this package.
|
||||||
|
type event struct {
|
||||||
|
h windows.Handle
|
||||||
|
}
|
||||||
|
|
||||||
|
func newEvent() (*event, error) {
|
||||||
|
h, err := windows.CreateEvent(nil, 0, 0, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &event{h: h}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *event) Close() error {
|
||||||
|
return windows.CloseHandle(e.h)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *event) Set() error {
|
||||||
|
return windows.SetEvent(e.h)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *event) Wait() error {
|
||||||
|
s, err := windows.WaitForSingleObject(e.h, windows.INFINITE)
|
||||||
|
switch s {
|
||||||
|
case windows.WAIT_OBJECT_0:
|
||||||
|
break
|
||||||
|
case windows.WAIT_FAILED:
|
||||||
|
return err
|
||||||
|
default:
|
||||||
|
return errors.New("unexpected result from WaitForSingleObject")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
// Copyright 2012 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.
|
||||||
|
|
||||||
|
// +build windows
|
||||||
|
// +build !go1.3
|
||||||
|
|
||||||
|
// copied from pkg/runtime
|
||||||
|
typedef unsigned int uint32;
|
||||||
|
typedef unsigned long long int uint64;
|
||||||
|
#ifdef _64BIT
|
||||||
|
typedef uint64 uintptr;
|
||||||
|
#else
|
||||||
|
typedef uint32 uintptr;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// from sys_386.s or sys_amd64.s
|
||||||
|
void ·servicemain(void);
|
||||||
|
|
||||||
|
void
|
||||||
|
·getServiceMain(uintptr *r)
|
||||||
|
{
|
||||||
|
*r = (uintptr)·servicemain;
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
// Copyright 2014 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.
|
||||||
|
|
||||||
|
// +build windows
|
||||||
|
// +build !go1.3
|
||||||
|
|
||||||
|
package svc
|
||||||
|
|
||||||
|
// from go12.c
|
||||||
|
func getServiceMain(r *uintptr)
|
|
@ -0,0 +1,31 @@
|
||||||
|
// Copyright 2014 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.
|
||||||
|
|
||||||
|
// +build windows
|
||||||
|
// +build go1.3
|
||||||
|
|
||||||
|
package svc
|
||||||
|
|
||||||
|
import "unsafe"
|
||||||
|
|
||||||
|
const ptrSize = 4 << (^uintptr(0) >> 63) // unsafe.Sizeof(uintptr(0)) but an ideal const
|
||||||
|
|
||||||
|
// Should be a built-in for unsafe.Pointer?
|
||||||
|
func add(p unsafe.Pointer, x uintptr) unsafe.Pointer {
|
||||||
|
return unsafe.Pointer(uintptr(p) + x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// funcPC returns the entry PC of the function f.
|
||||||
|
// It assumes that f is a func value. Otherwise the behavior is undefined.
|
||||||
|
func funcPC(f interface{}) uintptr {
|
||||||
|
return **(**uintptr)(add(unsafe.Pointer(&f), ptrSize))
|
||||||
|
}
|
||||||
|
|
||||||
|
// from sys_386.s and sys_amd64.s
|
||||||
|
func servicectlhandler(ctl uint32) uintptr
|
||||||
|
func servicemain(argc uint32, argv **uint16)
|
||||||
|
|
||||||
|
func getServiceMain(r *uintptr) {
|
||||||
|
*r = funcPC(servicemain)
|
||||||
|
}
|
|
@ -0,0 +1,62 @@
|
||||||
|
// Copyright 2012 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.
|
||||||
|
|
||||||
|
// +build windows
|
||||||
|
|
||||||
|
package svc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"golang.org/x/sys/windows"
|
||||||
|
)
|
||||||
|
|
||||||
|
func allocSid(subAuth0 uint32) (*windows.SID, error) {
|
||||||
|
var sid *windows.SID
|
||||||
|
err := windows.AllocateAndInitializeSid(&windows.SECURITY_NT_AUTHORITY,
|
||||||
|
1, subAuth0, 0, 0, 0, 0, 0, 0, 0, &sid)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return sid, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsAnInteractiveSession determines if calling process is running interactively.
|
||||||
|
// It queries the process token for membership in the Interactive group.
|
||||||
|
// http://stackoverflow.com/questions/2668851/how-do-i-detect-that-my-application-is-running-as-service-or-in-an-interactive-s
|
||||||
|
func IsAnInteractiveSession() (bool, error) {
|
||||||
|
interSid, err := allocSid(windows.SECURITY_INTERACTIVE_RID)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
defer windows.FreeSid(interSid)
|
||||||
|
|
||||||
|
serviceSid, err := allocSid(windows.SECURITY_SERVICE_RID)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
defer windows.FreeSid(serviceSid)
|
||||||
|
|
||||||
|
t, err := windows.OpenCurrentProcessToken()
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
defer t.Close()
|
||||||
|
|
||||||
|
gs, err := t.GetTokenGroups()
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
p := unsafe.Pointer(&gs.Groups[0])
|
||||||
|
groups := (*[2 << 20]windows.SIDAndAttributes)(p)[:gs.GroupCount]
|
||||||
|
for _, g := range groups {
|
||||||
|
if windows.EqualSid(g.Sid, interSid) {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
if windows.EqualSid(g.Sid, serviceSid) {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false, nil
|
||||||
|
}
|
|
@ -0,0 +1,363 @@
|
||||||
|
// Copyright 2012 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.
|
||||||
|
|
||||||
|
// +build windows
|
||||||
|
|
||||||
|
// Package svc provides everything required to build Windows service.
|
||||||
|
//
|
||||||
|
package svc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"runtime"
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"golang.org/x/sys/windows"
|
||||||
|
)
|
||||||
|
|
||||||
|
// State describes service execution state (Stopped, Running and so on).
|
||||||
|
type State uint32
|
||||||
|
|
||||||
|
const (
|
||||||
|
Stopped = State(windows.SERVICE_STOPPED)
|
||||||
|
StartPending = State(windows.SERVICE_START_PENDING)
|
||||||
|
StopPending = State(windows.SERVICE_STOP_PENDING)
|
||||||
|
Running = State(windows.SERVICE_RUNNING)
|
||||||
|
ContinuePending = State(windows.SERVICE_CONTINUE_PENDING)
|
||||||
|
PausePending = State(windows.SERVICE_PAUSE_PENDING)
|
||||||
|
Paused = State(windows.SERVICE_PAUSED)
|
||||||
|
)
|
||||||
|
|
||||||
|
// Cmd represents service state change request. It is sent to a service
|
||||||
|
// by the service manager, and should be actioned upon by the service.
|
||||||
|
type Cmd uint32
|
||||||
|
|
||||||
|
const (
|
||||||
|
Stop = Cmd(windows.SERVICE_CONTROL_STOP)
|
||||||
|
Pause = Cmd(windows.SERVICE_CONTROL_PAUSE)
|
||||||
|
Continue = Cmd(windows.SERVICE_CONTROL_CONTINUE)
|
||||||
|
Interrogate = Cmd(windows.SERVICE_CONTROL_INTERROGATE)
|
||||||
|
Shutdown = Cmd(windows.SERVICE_CONTROL_SHUTDOWN)
|
||||||
|
ParamChange = Cmd(windows.SERVICE_CONTROL_PARAMCHANGE)
|
||||||
|
NetBindAdd = Cmd(windows.SERVICE_CONTROL_NETBINDADD)
|
||||||
|
NetBindRemove = Cmd(windows.SERVICE_CONTROL_NETBINDREMOVE)
|
||||||
|
NetBindEnable = Cmd(windows.SERVICE_CONTROL_NETBINDENABLE)
|
||||||
|
NetBindDisable = Cmd(windows.SERVICE_CONTROL_NETBINDDISABLE)
|
||||||
|
DeviceEvent = Cmd(windows.SERVICE_CONTROL_DEVICEEVENT)
|
||||||
|
HardwareProfileChange = Cmd(windows.SERVICE_CONTROL_HARDWAREPROFILECHANGE)
|
||||||
|
PowerEvent = Cmd(windows.SERVICE_CONTROL_POWEREVENT)
|
||||||
|
SessionChange = Cmd(windows.SERVICE_CONTROL_SESSIONCHANGE)
|
||||||
|
)
|
||||||
|
|
||||||
|
// Accepted is used to describe commands accepted by the service.
|
||||||
|
// Note that Interrogate is always accepted.
|
||||||
|
type Accepted uint32
|
||||||
|
|
||||||
|
const (
|
||||||
|
AcceptStop = Accepted(windows.SERVICE_ACCEPT_STOP)
|
||||||
|
AcceptShutdown = Accepted(windows.SERVICE_ACCEPT_SHUTDOWN)
|
||||||
|
AcceptPauseAndContinue = Accepted(windows.SERVICE_ACCEPT_PAUSE_CONTINUE)
|
||||||
|
AcceptParamChange = Accepted(windows.SERVICE_ACCEPT_PARAMCHANGE)
|
||||||
|
AcceptNetBindChange = Accepted(windows.SERVICE_ACCEPT_NETBINDCHANGE)
|
||||||
|
AcceptHardwareProfileChange = Accepted(windows.SERVICE_ACCEPT_HARDWAREPROFILECHANGE)
|
||||||
|
AcceptPowerEvent = Accepted(windows.SERVICE_ACCEPT_POWEREVENT)
|
||||||
|
AcceptSessionChange = Accepted(windows.SERVICE_ACCEPT_SESSIONCHANGE)
|
||||||
|
)
|
||||||
|
|
||||||
|
// Status combines State and Accepted commands to fully describe running service.
|
||||||
|
type Status struct {
|
||||||
|
State State
|
||||||
|
Accepts Accepted
|
||||||
|
CheckPoint uint32 // used to report progress during a lengthy operation
|
||||||
|
WaitHint uint32 // estimated time required for a pending operation, in milliseconds
|
||||||
|
}
|
||||||
|
|
||||||
|
// ChangeRequest is sent to the service Handler to request service status change.
|
||||||
|
type ChangeRequest struct {
|
||||||
|
Cmd Cmd
|
||||||
|
EventType uint32
|
||||||
|
EventData uintptr
|
||||||
|
CurrentStatus Status
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handler is the interface that must be implemented to build Windows service.
|
||||||
|
type Handler interface {
|
||||||
|
|
||||||
|
// Execute will be called by the package code at the start of
|
||||||
|
// the service, and the service will exit once Execute completes.
|
||||||
|
// Inside Execute you must read service change requests from r and
|
||||||
|
// act accordingly. You must keep service control manager up to date
|
||||||
|
// about state of your service by writing into s as required.
|
||||||
|
// args contains service name followed by argument strings passed
|
||||||
|
// to the service.
|
||||||
|
// You can provide service exit code in exitCode return parameter,
|
||||||
|
// with 0 being "no error". You can also indicate if exit code,
|
||||||
|
// if any, is service specific or not by using svcSpecificEC
|
||||||
|
// parameter.
|
||||||
|
Execute(args []string, r <-chan ChangeRequest, s chan<- Status) (svcSpecificEC bool, exitCode uint32)
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
// These are used by asm code.
|
||||||
|
goWaitsH uintptr
|
||||||
|
cWaitsH uintptr
|
||||||
|
ssHandle uintptr
|
||||||
|
sName *uint16
|
||||||
|
sArgc uintptr
|
||||||
|
sArgv **uint16
|
||||||
|
ctlHandlerExProc uintptr
|
||||||
|
cSetEvent uintptr
|
||||||
|
cWaitForSingleObject uintptr
|
||||||
|
cRegisterServiceCtrlHandlerExW uintptr
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
k := syscall.MustLoadDLL("kernel32.dll")
|
||||||
|
cSetEvent = k.MustFindProc("SetEvent").Addr()
|
||||||
|
cWaitForSingleObject = k.MustFindProc("WaitForSingleObject").Addr()
|
||||||
|
a := syscall.MustLoadDLL("advapi32.dll")
|
||||||
|
cRegisterServiceCtrlHandlerExW = a.MustFindProc("RegisterServiceCtrlHandlerExW").Addr()
|
||||||
|
}
|
||||||
|
|
||||||
|
// The HandlerEx prototype also has a context pointer but since we don't use
|
||||||
|
// it at start-up time we don't have to pass it over either.
|
||||||
|
type ctlEvent struct {
|
||||||
|
cmd Cmd
|
||||||
|
eventType uint32
|
||||||
|
eventData uintptr
|
||||||
|
errno uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
// service provides access to windows service api.
|
||||||
|
type service struct {
|
||||||
|
name string
|
||||||
|
h windows.Handle
|
||||||
|
cWaits *event
|
||||||
|
goWaits *event
|
||||||
|
c chan ctlEvent
|
||||||
|
handler Handler
|
||||||
|
}
|
||||||
|
|
||||||
|
func newService(name string, handler Handler) (*service, error) {
|
||||||
|
var s service
|
||||||
|
var err error
|
||||||
|
s.name = name
|
||||||
|
s.c = make(chan ctlEvent)
|
||||||
|
s.handler = handler
|
||||||
|
s.cWaits, err = newEvent()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
s.goWaits, err = newEvent()
|
||||||
|
if err != nil {
|
||||||
|
s.cWaits.Close()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &s, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *service) close() error {
|
||||||
|
s.cWaits.Close()
|
||||||
|
s.goWaits.Close()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type exitCode struct {
|
||||||
|
isSvcSpecific bool
|
||||||
|
errno uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *service) updateStatus(status *Status, ec *exitCode) error {
|
||||||
|
if s.h == 0 {
|
||||||
|
return errors.New("updateStatus with no service status handle")
|
||||||
|
}
|
||||||
|
var t windows.SERVICE_STATUS
|
||||||
|
t.ServiceType = windows.SERVICE_WIN32_OWN_PROCESS
|
||||||
|
t.CurrentState = uint32(status.State)
|
||||||
|
if status.Accepts&AcceptStop != 0 {
|
||||||
|
t.ControlsAccepted |= windows.SERVICE_ACCEPT_STOP
|
||||||
|
}
|
||||||
|
if status.Accepts&AcceptShutdown != 0 {
|
||||||
|
t.ControlsAccepted |= windows.SERVICE_ACCEPT_SHUTDOWN
|
||||||
|
}
|
||||||
|
if status.Accepts&AcceptPauseAndContinue != 0 {
|
||||||
|
t.ControlsAccepted |= windows.SERVICE_ACCEPT_PAUSE_CONTINUE
|
||||||
|
}
|
||||||
|
if status.Accepts&AcceptParamChange != 0 {
|
||||||
|
t.ControlsAccepted |= windows.SERVICE_ACCEPT_PARAMCHANGE
|
||||||
|
}
|
||||||
|
if status.Accepts&AcceptNetBindChange != 0 {
|
||||||
|
t.ControlsAccepted |= windows.SERVICE_ACCEPT_NETBINDCHANGE
|
||||||
|
}
|
||||||
|
if status.Accepts&AcceptHardwareProfileChange != 0 {
|
||||||
|
t.ControlsAccepted |= windows.SERVICE_ACCEPT_HARDWAREPROFILECHANGE
|
||||||
|
}
|
||||||
|
if status.Accepts&AcceptPowerEvent != 0 {
|
||||||
|
t.ControlsAccepted |= windows.SERVICE_ACCEPT_POWEREVENT
|
||||||
|
}
|
||||||
|
if status.Accepts&AcceptSessionChange != 0 {
|
||||||
|
t.ControlsAccepted |= windows.SERVICE_ACCEPT_SESSIONCHANGE
|
||||||
|
}
|
||||||
|
if ec.errno == 0 {
|
||||||
|
t.Win32ExitCode = windows.NO_ERROR
|
||||||
|
t.ServiceSpecificExitCode = windows.NO_ERROR
|
||||||
|
} else if ec.isSvcSpecific {
|
||||||
|
t.Win32ExitCode = uint32(windows.ERROR_SERVICE_SPECIFIC_ERROR)
|
||||||
|
t.ServiceSpecificExitCode = ec.errno
|
||||||
|
} else {
|
||||||
|
t.Win32ExitCode = ec.errno
|
||||||
|
t.ServiceSpecificExitCode = windows.NO_ERROR
|
||||||
|
}
|
||||||
|
t.CheckPoint = status.CheckPoint
|
||||||
|
t.WaitHint = status.WaitHint
|
||||||
|
return windows.SetServiceStatus(s.h, &t)
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
sysErrSetServiceStatusFailed = uint32(syscall.APPLICATION_ERROR) + iota
|
||||||
|
sysErrNewThreadInCallback
|
||||||
|
)
|
||||||
|
|
||||||
|
func (s *service) run() {
|
||||||
|
s.goWaits.Wait()
|
||||||
|
s.h = windows.Handle(ssHandle)
|
||||||
|
argv := (*[100]*int16)(unsafe.Pointer(sArgv))[:sArgc]
|
||||||
|
args := make([]string, len(argv))
|
||||||
|
for i, a := range argv {
|
||||||
|
args[i] = syscall.UTF16ToString((*[1 << 20]uint16)(unsafe.Pointer(a))[:])
|
||||||
|
}
|
||||||
|
|
||||||
|
cmdsToHandler := make(chan ChangeRequest)
|
||||||
|
changesFromHandler := make(chan Status)
|
||||||
|
exitFromHandler := make(chan exitCode)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
ss, errno := s.handler.Execute(args, cmdsToHandler, changesFromHandler)
|
||||||
|
exitFromHandler <- exitCode{ss, errno}
|
||||||
|
}()
|
||||||
|
|
||||||
|
status := Status{State: Stopped}
|
||||||
|
ec := exitCode{isSvcSpecific: true, errno: 0}
|
||||||
|
var outch chan ChangeRequest
|
||||||
|
inch := s.c
|
||||||
|
var cmd Cmd
|
||||||
|
var evtype uint32
|
||||||
|
var evdata uintptr
|
||||||
|
loop:
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case r := <-inch:
|
||||||
|
if r.errno != 0 {
|
||||||
|
ec.errno = r.errno
|
||||||
|
break loop
|
||||||
|
}
|
||||||
|
inch = nil
|
||||||
|
outch = cmdsToHandler
|
||||||
|
cmd = r.cmd
|
||||||
|
evtype = r.eventType
|
||||||
|
evdata = r.eventData
|
||||||
|
case outch <- ChangeRequest{cmd, evtype, evdata, status}:
|
||||||
|
inch = s.c
|
||||||
|
outch = nil
|
||||||
|
case c := <-changesFromHandler:
|
||||||
|
err := s.updateStatus(&c, &ec)
|
||||||
|
if err != nil {
|
||||||
|
// best suitable error number
|
||||||
|
ec.errno = sysErrSetServiceStatusFailed
|
||||||
|
if err2, ok := err.(syscall.Errno); ok {
|
||||||
|
ec.errno = uint32(err2)
|
||||||
|
}
|
||||||
|
break loop
|
||||||
|
}
|
||||||
|
status = c
|
||||||
|
case ec = <-exitFromHandler:
|
||||||
|
break loop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
s.updateStatus(&Status{State: Stopped}, &ec)
|
||||||
|
s.cWaits.Set()
|
||||||
|
}
|
||||||
|
|
||||||
|
func newCallback(fn interface{}) (cb uintptr, err error) {
|
||||||
|
defer func() {
|
||||||
|
r := recover()
|
||||||
|
if r == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
cb = 0
|
||||||
|
switch v := r.(type) {
|
||||||
|
case string:
|
||||||
|
err = errors.New(v)
|
||||||
|
case error:
|
||||||
|
err = v
|
||||||
|
default:
|
||||||
|
err = errors.New("unexpected panic in syscall.NewCallback")
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
return syscall.NewCallback(fn), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// BUG(brainman): There is no mechanism to run multiple services
|
||||||
|
// inside one single executable. Perhaps, it can be overcome by
|
||||||
|
// using RegisterServiceCtrlHandlerEx Windows api.
|
||||||
|
|
||||||
|
// Run executes service name by calling appropriate handler function.
|
||||||
|
func Run(name string, handler Handler) error {
|
||||||
|
runtime.LockOSThread()
|
||||||
|
|
||||||
|
tid := windows.GetCurrentThreadId()
|
||||||
|
|
||||||
|
s, err := newService(name, handler)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
ctlHandler := func(ctl uint32, evtype uint32, evdata uintptr, context uintptr) uintptr {
|
||||||
|
e := ctlEvent{cmd: Cmd(ctl), eventType: evtype, eventData: evdata}
|
||||||
|
// We assume that this callback function is running on
|
||||||
|
// the same thread as Run. Nowhere in MS documentation
|
||||||
|
// I could find statement to guarantee that. So putting
|
||||||
|
// check here to verify, otherwise things will go bad
|
||||||
|
// quickly, if ignored.
|
||||||
|
i := windows.GetCurrentThreadId()
|
||||||
|
if i != tid {
|
||||||
|
e.errno = sysErrNewThreadInCallback
|
||||||
|
}
|
||||||
|
s.c <- e
|
||||||
|
// Always return NO_ERROR (0) for now.
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
var svcmain uintptr
|
||||||
|
getServiceMain(&svcmain)
|
||||||
|
t := []windows.SERVICE_TABLE_ENTRY{
|
||||||
|
{syscall.StringToUTF16Ptr(s.name), svcmain},
|
||||||
|
{nil, 0},
|
||||||
|
}
|
||||||
|
|
||||||
|
goWaitsH = uintptr(s.goWaits.h)
|
||||||
|
cWaitsH = uintptr(s.cWaits.h)
|
||||||
|
sName = t[0].ServiceName
|
||||||
|
ctlHandlerExProc, err = newCallback(ctlHandler)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
go s.run()
|
||||||
|
|
||||||
|
err = windows.StartServiceCtrlDispatcher(&t[0])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// StatusHandle returns service status handle. It is safe to call this function
|
||||||
|
// from inside the Handler.Execute because then it is guaranteed to be set.
|
||||||
|
// This code will have to change once multiple services are possible per process.
|
||||||
|
func StatusHandle() windows.Handle {
|
||||||
|
return windows.Handle(ssHandle)
|
||||||
|
}
|
|
@ -0,0 +1,68 @@
|
||||||
|
// Copyright 2012 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.
|
||||||
|
|
||||||
|
// +build windows
|
||||||
|
|
||||||
|
// func servicemain(argc uint32, argv **uint16)
|
||||||
|
TEXT ·servicemain(SB),7,$0
|
||||||
|
MOVL argc+0(FP), AX
|
||||||
|
MOVL AX, ·sArgc(SB)
|
||||||
|
MOVL argv+4(FP), AX
|
||||||
|
MOVL AX, ·sArgv(SB)
|
||||||
|
|
||||||
|
PUSHL BP
|
||||||
|
PUSHL BX
|
||||||
|
PUSHL SI
|
||||||
|
PUSHL DI
|
||||||
|
|
||||||
|
SUBL $12, SP
|
||||||
|
|
||||||
|
MOVL ·sName(SB), AX
|
||||||
|
MOVL AX, (SP)
|
||||||
|
MOVL $·servicectlhandler(SB), AX
|
||||||
|
MOVL AX, 4(SP)
|
||||||
|
MOVL $0, 8(SP)
|
||||||
|
MOVL ·cRegisterServiceCtrlHandlerExW(SB), AX
|
||||||
|
MOVL SP, BP
|
||||||
|
CALL AX
|
||||||
|
MOVL BP, SP
|
||||||
|
CMPL AX, $0
|
||||||
|
JE exit
|
||||||
|
MOVL AX, ·ssHandle(SB)
|
||||||
|
|
||||||
|
MOVL ·goWaitsH(SB), AX
|
||||||
|
MOVL AX, (SP)
|
||||||
|
MOVL ·cSetEvent(SB), AX
|
||||||
|
MOVL SP, BP
|
||||||
|
CALL AX
|
||||||
|
MOVL BP, SP
|
||||||
|
|
||||||
|
MOVL ·cWaitsH(SB), AX
|
||||||
|
MOVL AX, (SP)
|
||||||
|
MOVL $-1, AX
|
||||||
|
MOVL AX, 4(SP)
|
||||||
|
MOVL ·cWaitForSingleObject(SB), AX
|
||||||
|
MOVL SP, BP
|
||||||
|
CALL AX
|
||||||
|
MOVL BP, SP
|
||||||
|
|
||||||
|
exit:
|
||||||
|
ADDL $12, SP
|
||||||
|
|
||||||
|
POPL DI
|
||||||
|
POPL SI
|
||||||
|
POPL BX
|
||||||
|
POPL BP
|
||||||
|
|
||||||
|
MOVL 0(SP), CX
|
||||||
|
ADDL $12, SP
|
||||||
|
JMP CX
|
||||||
|
|
||||||
|
// I do not know why, but this seems to be the only way to call
|
||||||
|
// ctlHandlerProc on Windows 7.
|
||||||
|
|
||||||
|
// func servicectlhandler(ctl uint32, evtype uint32, evdata uintptr, context uintptr) uintptr {
|
||||||
|
TEXT ·servicectlhandler(SB),7,$0
|
||||||
|
MOVL ·ctlHandlerExProc(SB), CX
|
||||||
|
JMP CX
|
|
@ -0,0 +1,42 @@
|
||||||
|
// Copyright 2012 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.
|
||||||
|
|
||||||
|
// +build windows
|
||||||
|
|
||||||
|
// func servicemain(argc uint32, argv **uint16)
|
||||||
|
TEXT ·servicemain(SB),7,$0
|
||||||
|
MOVL CX, ·sArgc(SB)
|
||||||
|
MOVL DX, ·sArgv(SB)
|
||||||
|
|
||||||
|
SUBQ $32, SP // stack for the first 4 syscall params
|
||||||
|
|
||||||
|
MOVQ ·sName(SB), CX
|
||||||
|
MOVQ $·servicectlhandler(SB), DX
|
||||||
|
// BUG(pastarmovj): Figure out a way to pass in context in R8.
|
||||||
|
MOVQ ·cRegisterServiceCtrlHandlerExW(SB), AX
|
||||||
|
CALL AX
|
||||||
|
CMPQ AX, $0
|
||||||
|
JE exit
|
||||||
|
MOVQ AX, ·ssHandle(SB)
|
||||||
|
|
||||||
|
MOVQ ·goWaitsH(SB), CX
|
||||||
|
MOVQ ·cSetEvent(SB), AX
|
||||||
|
CALL AX
|
||||||
|
|
||||||
|
MOVQ ·cWaitsH(SB), CX
|
||||||
|
MOVQ $4294967295, DX
|
||||||
|
MOVQ ·cWaitForSingleObject(SB), AX
|
||||||
|
CALL AX
|
||||||
|
|
||||||
|
exit:
|
||||||
|
ADDQ $32, SP
|
||||||
|
RET
|
||||||
|
|
||||||
|
// I do not know why, but this seems to be the only way to call
|
||||||
|
// ctlHandlerProc on Windows 7.
|
||||||
|
|
||||||
|
// func ·servicectlhandler(ctl uint32, evtype uint32, evdata uintptr, context uintptr) uintptr {
|
||||||
|
TEXT ·servicectlhandler(SB),7,$0
|
||||||
|
MOVQ ·ctlHandlerExProc(SB), AX
|
||||||
|
JMP AX
|
|
@ -0,0 +1,112 @@
|
||||||
|
# Building Kubernetes
|
||||||
|
|
||||||
|
Building Kubernetes is easy if you take advantage of the containerized build environment. This document will help guide you through understanding this build process.
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
1. Docker, using one of the following configurations:
|
||||||
|
* **Mac OS X** You can either use Docker for Mac or docker-machine. See installation instructions [here](https://docs.docker.com/docker-for-mac/).
|
||||||
|
**Note**: You will want to set the Docker VM to have at least 3GB of initial memory or building will likely fail. (See: [#11852]( http://issue.k8s.io/11852)).
|
||||||
|
* **Linux with local Docker** Install Docker according to the [instructions](https://docs.docker.com/installation/#installation) for your OS.
|
||||||
|
* **Remote Docker engine** Use a big machine in the cloud to build faster. This is a little trickier so look at the section later on.
|
||||||
|
2. **Optional** [Google Cloud SDK](https://developers.google.com/cloud/sdk/)
|
||||||
|
|
||||||
|
You must install and configure Google Cloud SDK if you want to upload your release to Google Cloud Storage and may safely omit this otherwise.
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
While it is possible to build Kubernetes using a local golang installation, we have a build process that runs in a Docker container. This simplifies initial set up and provides for a very consistent build and test environment.
|
||||||
|
|
||||||
|
## Key scripts
|
||||||
|
|
||||||
|
The following scripts are found in the `build/` directory. Note that all scripts must be run from the Kubernetes root directory.
|
||||||
|
|
||||||
|
* `build/run.sh`: Run a command in a build docker container. Common invocations:
|
||||||
|
* `build/run.sh make`: Build just linux binaries in the container. Pass options and packages as necessary.
|
||||||
|
* `build/run.sh make cross`: Build all binaries for all platforms
|
||||||
|
* `build/run.sh make test`: Run all unit tests
|
||||||
|
* `build/run.sh make test-integration`: Run integration test
|
||||||
|
* `build/run.sh make test-cmd`: Run CLI tests
|
||||||
|
* `build/copy-output.sh`: This will copy the contents of `_output/dockerized/bin` from the Docker container to the local `_output/dockerized/bin`. It will also copy out specific file patterns that are generated as part of the build process. This is run automatically as part of `build/run.sh`.
|
||||||
|
* `build/make-clean.sh`: Clean out the contents of `_output`, remove any locally built container images and remove the data container.
|
||||||
|
* `/build/shell.sh`: Drop into a `bash` shell in a build container with a snapshot of the current repo code.
|
||||||
|
|
||||||
|
## Basic Flow
|
||||||
|
|
||||||
|
The scripts directly under `build/` are used to build and test. They will ensure that the `kube-build` Docker image is built (based on `build/build-image/Dockerfile`) and then execute the appropriate command in that container. These scripts will both ensure that the right data is cached from run to run for incremental builds and will copy the results back out of the container.
|
||||||
|
|
||||||
|
The `kube-build` container image is built by first creating a "context" directory in `_output/images/build-image`. It is done there instead of at the root of the Kubernetes repo to minimize the amount of data we need to package up when building the image.
|
||||||
|
|
||||||
|
There are 3 different containers instances that are run from this image. The first is a "data" container to store all data that needs to persist across to support incremental builds. Next there is an "rsync" container that is used to transfer data in and out to the data container. Lastly there is a "build" container that is used for actually doing build actions. The data container persists across runs while the rsync and build containers are deleted after each use.
|
||||||
|
|
||||||
|
`rsync` is used transparently behind the scenes to efficiently move data in and out of the container. This will use an ephemeral port picked by Docker. You can modify this by setting the `KUBE_RSYNC_PORT` env variable.
|
||||||
|
|
||||||
|
All Docker names are suffixed with a hash derived from the file path (to allow concurrent usage on things like CI machines) and a version number. When the version number changes all state is cleared and clean build is started. This allows the build infrastructure to be changed and signal to CI systems that old artifacts need to be deleted.
|
||||||
|
|
||||||
|
## Proxy Settings
|
||||||
|
|
||||||
|
If you are behind a proxy and you are letting these scripts use `docker-machine` to set up your local VM for you on macOS, you need to export proxy settings for Kubernetes build, the following environment variables should be defined.
|
||||||
|
|
||||||
|
```
|
||||||
|
export KUBERNETES_HTTP_PROXY=http://username:password@proxyaddr:proxyport
|
||||||
|
export KUBERNETES_HTTPS_PROXY=https://username:password@proxyaddr:proxyport
|
||||||
|
```
|
||||||
|
|
||||||
|
Optionally, you can specify addresses of no proxy for Kubernetes build, for example
|
||||||
|
|
||||||
|
```
|
||||||
|
export KUBERNETES_NO_PROXY=127.0.0.1
|
||||||
|
```
|
||||||
|
|
||||||
|
If you are using sudo to make Kubernetes build for example make quick-release, you need run `sudo -E make quick-release` to pass the environment variables.
|
||||||
|
|
||||||
|
## Really Remote Docker Engine
|
||||||
|
|
||||||
|
It is possible to use a Docker Engine that is running remotely (under your desk or in the cloud). Docker must be configured to connect to that machine and the local rsync port must be forwarded (via SSH or nc) from localhost to the remote machine.
|
||||||
|
|
||||||
|
To do this easily with GCE and `docker-machine`, do something like this:
|
||||||
|
```
|
||||||
|
# Create the remote docker machine on GCE. This is a pretty beefy machine with SSD disk.
|
||||||
|
KUBE_BUILD_VM=k8s-build
|
||||||
|
KUBE_BUILD_GCE_PROJECT=<project>
|
||||||
|
docker-machine create \
|
||||||
|
--driver=google \
|
||||||
|
--google-project=${KUBE_BUILD_GCE_PROJECT} \
|
||||||
|
--google-zone=us-west1-a \
|
||||||
|
--google-machine-type=n1-standard-8 \
|
||||||
|
--google-disk-size=50 \
|
||||||
|
--google-disk-type=pd-ssd \
|
||||||
|
${KUBE_BUILD_VM}
|
||||||
|
|
||||||
|
# Set up local docker to talk to that machine
|
||||||
|
eval $(docker-machine env ${KUBE_BUILD_VM})
|
||||||
|
|
||||||
|
# Pin down the port that rsync will be exposed on on the remote machine
|
||||||
|
export KUBE_RSYNC_PORT=8730
|
||||||
|
|
||||||
|
# forward local 8730 to that machine so that rsync works
|
||||||
|
docker-machine ssh ${KUBE_BUILD_VM} -L ${KUBE_RSYNC_PORT}:localhost:${KUBE_RSYNC_PORT} -N &
|
||||||
|
```
|
||||||
|
|
||||||
|
Look at `docker-machine stop`, `docker-machine start` and `docker-machine rm` to manage this VM.
|
||||||
|
|
||||||
|
## Releasing
|
||||||
|
|
||||||
|
The `build/release.sh` script will build a release. It will build binaries, run tests, (optionally) build runtime Docker images.
|
||||||
|
|
||||||
|
The main output is a tar file: `kubernetes.tar.gz`. This includes:
|
||||||
|
* Cross compiled client utilities.
|
||||||
|
* Script (`kubectl`) for picking and running the right client binary based on platform.
|
||||||
|
* Examples
|
||||||
|
* Cluster deployment scripts for various clouds
|
||||||
|
* Tar file containing all server binaries
|
||||||
|
* Tar file containing salt deployment tree shared across multiple cloud deployments.
|
||||||
|
|
||||||
|
In addition, there are some other tar files that are created:
|
||||||
|
* `kubernetes-client-*.tar.gz` Client binaries for a specific platform.
|
||||||
|
* `kubernetes-server-*.tar.gz` Server binaries for a specific platform.
|
||||||
|
* `kubernetes-salt.tar.gz` The salt script/tree shared across multiple deployment scripts.
|
||||||
|
|
||||||
|
When building final release tars, they are first staged into `_output/release-stage` before being tar'd up and put into `_output/release-tars`.
|
||||||
|
|
||||||
|
[![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/build/README.md?pixel)]()
|
|
@ -0,0 +1,36 @@
|
||||||
|
/*
|
||||||
|
Copyright 2016 The Kubernetes Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Creates a zombie to be reaped by init. Useful for testing. */
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
pid_t pid;
|
||||||
|
pid = fork();
|
||||||
|
if (pid == 0) {
|
||||||
|
while (getppid() > 1)
|
||||||
|
;
|
||||||
|
printf("Child exiting: pid=%d ppid=%d\n", getpid(), getppid());
|
||||||
|
return 0;
|
||||||
|
} else if (pid > 0) {
|
||||||
|
printf("Parent exiting: pid=%d ppid=%d\n", getpid(), getppid());
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
perror("Could not create child");
|
||||||
|
return 1;
|
||||||
|
}
|
|
@ -0,0 +1,51 @@
|
||||||
|
/*
|
||||||
|
Copyright 2016 The Kubernetes Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <signal.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
static void sigdown(int signo) {
|
||||||
|
psignal(signo, "Shutting down, got signal");
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sigreap(int signo) {
|
||||||
|
while (waitpid(-1, NULL, WNOHANG) > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
if (getpid() != 1)
|
||||||
|
/* Not an error because pause sees use outside of infra containers. */
|
||||||
|
fprintf(stderr, "Warning: pause should be the first process\n");
|
||||||
|
|
||||||
|
if (sigaction(SIGINT, &(struct sigaction){.sa_handler = sigdown}, NULL) < 0)
|
||||||
|
return 1;
|
||||||
|
if (sigaction(SIGTERM, &(struct sigaction){.sa_handler = sigdown}, NULL) < 0)
|
||||||
|
return 2;
|
||||||
|
if (sigaction(SIGCHLD, &(struct sigaction){.sa_handler = sigreap,
|
||||||
|
.sa_flags = SA_NOCLDSTOP},
|
||||||
|
NULL) < 0)
|
||||||
|
return 3;
|
||||||
|
|
||||||
|
for (;;)
|
||||||
|
pause();
|
||||||
|
fprintf(stderr, "Error: infinite loop terminated\n");
|
||||||
|
return 42;
|
||||||
|
}
|
Loading…
Reference in New Issue