Ejemplo n.º 1
0
bool read_ecdsa_private_key(meshlink_handle_t *mesh) {
	FILE *fp;
	char filename[PATH_MAX];

	snprintf(filename,PATH_MAX, "%s" SLASH "ecdsa_key.priv", mesh->confbase);
	fp = fopen(filename, "rb");

	if(!fp) {
		logger(mesh, MESHLINK_ERROR, "Error reading ECDSA private key file: %s", strerror(errno));
		return false;
	}

	mesh->self->connection->ecdsa = ecdsa_read_pem_private_key(fp);
	fclose(fp);

	if(!mesh->self->connection->ecdsa)
		logger(mesh, MESHLINK_ERROR, "Reading ECDSA private key file failed: %s", strerror(errno));

	return mesh->self->connection->ecdsa;
}
Ejemplo n.º 2
0
static bool read_invitation_key(meshlink_handle_t *mesh) {
	FILE *fp;
	char filename[PATH_MAX];

	if(mesh->invitation_key) {
		ecdsa_free(mesh->invitation_key);
		mesh->invitation_key = NULL;
	}

	snprintf(filename,PATH_MAX, "%s" SLASH "invitations" SLASH "ecdsa_key.priv", mesh->confbase);

	fp = fopen(filename, "rb");

	if(fp) {
		mesh->invitation_key = ecdsa_read_pem_private_key(fp);
		fclose(fp);
		if(!mesh->invitation_key)
			logger(mesh, MESHLINK_ERROR, "Reading ECDSA private key file `%s' failed: %s", filename, strerror(errno));
	}

	return mesh->invitation_key;
}
Ejemplo n.º 3
0
int cmd_invite(int argc, char *argv[]) {
	if(argc < 2) {
		fprintf(stderr, "Not enough arguments!\n");
		return 1;
	}

	// Check validity of the new node's name
	if(!check_id(argv[1])) {
		fprintf(stderr, "Invalid name for node.\n");
		return 1;
	}

	char *myname = get_my_name(true);
	if(!myname)
		return 1;

	// Ensure no host configuration file with that name exists
	char filename[PATH_MAX];
	snprintf(filename, sizeof filename, "%s" SLASH "hosts" SLASH "%s", confbase, argv[1]);
	if(!access(filename, F_OK)) {
		fprintf(stderr, "A host config file for %s already exists!\n", argv[1]);
		return 1;
	}

	// If a daemon is running, ensure no other nodes know about this name
	bool found = false;
	if(connect_tincd(false)) {
		sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);

		while(recvline(fd, line, sizeof line)) {
			char node[4096];
			int code, req;
			if(sscanf(line, "%d %d %s", &code, &req, node) != 3)
				break;
			if(!strcmp(node, argv[1]))
				found = true;
		}

		if(found) {
			fprintf(stderr, "A node with name %s is already known!\n", argv[1]);
			return 1;
		}
	}

	snprintf(filename, sizeof filename, "%s" SLASH "invitations", confbase);
	if(mkdir(filename, 0700) && errno != EEXIST) {
		fprintf(stderr, "Could not create directory %s: %s\n", filename, strerror(errno));
		return 1;
	}

	// Count the number of valid invitations, clean up old ones
	DIR *dir = opendir(filename);
	if(!dir) {
		fprintf(stderr, "Could not read directory %s: %s\n", filename, strerror(errno));
		return 1;
	}

	errno = 0;
	int count = 0;
	struct dirent *ent;
	time_t deadline = time(NULL) - 604800; // 1 week in the past

	while((ent = readdir(dir))) {
		if(strlen(ent->d_name) != 24)
			continue;
		char invname[PATH_MAX];
		struct stat st;
		snprintf(invname, sizeof invname, "%s" SLASH "%s", filename, ent->d_name);
		if(!stat(invname, &st)) {
			if(deadline < st.st_mtime)
				count++;
			else
				unlink(invname);
		} else {
			fprintf(stderr, "Could not stat %s: %s\n", invname, strerror(errno));
			errno = 0;
		}
	}

	closedir(dir);

	if(errno) {
		fprintf(stderr, "Error while reading directory %s: %s\n", filename, strerror(errno));
		return 1;
	}
		
	ecdsa_t *key;
	snprintf(filename, sizeof filename, "%s" SLASH "invitations" SLASH "ed25519_key.priv", confbase);

	// Remove the key if there are no outstanding invitations.
	if(!count)
		unlink(filename);

	// Create a new key if necessary.
	FILE *f = fopen(filename, "r");
	if(!f) {
		if(errno != ENOENT) {
			fprintf(stderr, "Could not read %s: %s\n", filename, strerror(errno));
			return 1;
		}

		key = ecdsa_generate();
		if(!key)
			return 1;
		f = fopen(filename, "w");
		if(!f) {
			fprintf(stderr, "Could not write %s: %s\n", filename, strerror(errno));
			return 1;
		}
		chmod(filename, 0600);
		if(!ecdsa_write_pem_private_key(key, f)) {
			fprintf(stderr, "Could not write ECDSA private key\n");
			fclose(f);
			return 1;
		}
		fclose(f);

		if(connect_tincd(false))
			sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
	} else {
		key = ecdsa_read_pem_private_key(f);
		fclose(f);
		if(!key)
			fprintf(stderr, "Could not read private key from %s\n", filename);
	}

	if(!key)
		return 1;

	// Create a hash of the key.
	char hash[64];
	char *fingerprint = ecdsa_get_base64_public_key(key);
	sha512(fingerprint, strlen(fingerprint), hash);
	b64encode_urlsafe(hash, hash, 18);

	// Create a random cookie for this invitation.
	char cookie[25];
	randomize(cookie, 18);

	// Create a filename that doesn't reveal the cookie itself
	char buf[18 + strlen(fingerprint)];
	char cookiehash[64];
	memcpy(buf, cookie, 18);
	memcpy(buf + 18, fingerprint, sizeof buf - 18);
	sha512(buf, sizeof buf, cookiehash);
	b64encode_urlsafe(cookiehash, cookiehash, 18);

	b64encode_urlsafe(cookie, cookie, 18);

	// Create a file containing the details of the invitation.
	snprintf(filename, sizeof filename, "%s" SLASH "invitations" SLASH "%s", confbase, cookiehash);
	int ifd = open(filename, O_RDWR | O_CREAT | O_EXCL, 0600);
	if(!ifd) {
		fprintf(stderr, "Could not create invitation file %s: %s\n", filename, strerror(errno));
		return 1;
	}
	f = fdopen(ifd, "w");
	if(!f)
		abort();

	// Get the local address
	char *address = get_my_hostname();

	// Fill in the details.
	fprintf(f, "Name = %s\n", argv[1]);
	if(netname)
		fprintf(f, "NetName = %s\n", netname);
	fprintf(f, "ConnectTo = %s\n", myname);

	// Copy Broadcast and Mode
	FILE *tc = fopen(tinc_conf, "r");
	if(tc) {
		char buf[1024];
		while(fgets(buf, sizeof buf, tc)) {
			if((!strncasecmp(buf, "Mode", 4) && strchr(" \t=", buf[4]))
					|| (!strncasecmp(buf, "Broadcast", 9) && strchr(" \t=", buf[9]))) {
				fputs(buf, f);
				// Make sure there is a newline character.
				if(!strchr(buf, '\n'))
					fputc('\n', f);
			}
		}
		fclose(tc);
	}

	fprintf(f, "#---------------------------------------------------------------#\n");
	fprintf(f, "Name = %s\n", myname);

	char filename2[PATH_MAX];
	snprintf(filename2, sizeof filename2, "%s" SLASH "hosts" SLASH "%s", confbase, myname);
	fcopy(f, filename2);
	fclose(f);

	// Create an URL from the local address, key hash and cookie
	char *url;
	xasprintf(&url, "%s/%s%s", address, hash, cookie);

	// Call the inviation-created script
	char *envp[6] = {};
	xasprintf(&envp[0], "NAME=%s", myname);
	xasprintf(&envp[1], "NETNAME=%s", netname);
	xasprintf(&envp[2], "NODE=%s", argv[1]);
	xasprintf(&envp[3], "INVITATION_FILE=%s", filename);
	xasprintf(&envp[4], "INVITATION_URL=%s", url);
	execute_script("invitation-created", envp);
	for(int i = 0; i < 6 && envp[i]; i++)
		free(envp[i]);

	puts(url);
	free(url);
	free(address);

	return 0;
}
Ejemplo n.º 4
0
Archivo: fsck.c Proyecto: xentec/tinc
int fsck(const char *argv0) {
#ifdef HAVE_MINGW
	int uid = 0;
#else
	uid_t uid = getuid();
#endif

	// Check that tinc.conf is readable.

	if(access(tinc_conf, R_OK)) {
		fprintf(stderr, "ERROR: cannot read %s: %s\n", tinc_conf, strerror(errno));
		if(errno == ENOENT) {
			fprintf(stderr, "No tinc configuration found. Create a new one with:\n\n");
			print_tinc_cmd(argv0, "init");
		} else if(errno == EACCES) {
			if(uid != 0)
				fprintf(stderr, "You are currently not running tinc as root. Use sudo?\n");
			else
				fprintf(stderr, "Check the permissions of each component of the path %s.\n", tinc_conf);
		}
		return 1;
	}

	char *name = get_my_name(true);
	if(!name) {
		fprintf(stderr, "ERROR: tinc cannot run without a valid Name.\n");
		return 1;
	}

	// Check for private keys.
	// TODO: use RSAPrivateKeyFile and Ed25519PrivateKeyFile variables if present.

	struct stat st;
	char fname[PATH_MAX];
	char dname[PATH_MAX];

	ecdsa_t *ecdsa_priv = NULL;
	snprintf(fname, sizeof fname, "%s/ed25519_key.priv", confbase);

	if(stat(fname, &st)) {
		if(errno != ENOENT) {
			// Something is seriously wrong here. If we can access the directory with tinc.conf in it, we should certainly be able to stat() an existing file.
			fprintf(stderr, "ERROR: cannot read %s: %s\n", fname, strerror(errno));
			fprintf(stderr, "Please correct this error.\n");
			return 1;
		}
	} else {
		FILE *f = fopen(fname, "r");
		if(!f) {
			fprintf(stderr, "ERROR: could not open %s: %s\n", fname, strerror(errno));
			return 1;
		}
		ecdsa_priv = ecdsa_read_pem_private_key(f);
		fclose(f);
		if(!ecdsa_priv) {
			fprintf(stderr, "ERROR: No key or unusable key found in %s.\n", fname);
			fprintf(stderr, "You can generate a new Ed25519 key with:\n\n");
			print_tinc_cmd(argv0, "generate-ed25519-keys");
			return 1;
		}

		if(st.st_mode & 077) {
			fprintf(stderr, "WARNING: unsafe file permissions on %s.\n", fname);
			if(st.st_uid != uid) {
				fprintf(stderr, "You are not running %s as the same uid as %s.\n", argv0, fname);
			} else if(ask_fix()) {
				if(chmod(fname, st.st_mode & ~077))
					fprintf(stderr, "ERROR: could not change permissions of %s: %s\n", fname, strerror(errno));
				else
					fprintf(stderr, "Fixed permissions of %s.\n", fname);
			}
		}
	}

	if(!ecdsa_priv) {
		fprintf(stderr, "ERROR: No Ed25519 private key found.\n");
		fprintf(stderr, "You can generate new keys with:\n\n");
		print_tinc_cmd(argv0, "generate-keys");
		return 1;
	}

	// Check for public keys.
	// TODO: use RSAPublicKeyFile and Ed25519PublicKeyFile variables if present.

	snprintf(fname, sizeof fname, "%s/hosts/%s", confbase, name);
	if(access(fname, R_OK))
		fprintf(stderr, "WARNING: cannot read %s\n", fname);

	FILE *f;


	// TODO: this should read the Ed25519PublicKey config variable instead.
	ecdsa_t *ecdsa_pub = NULL;

	f = fopen(fname, "r");
	if(f)
		ecdsa_pub = ecdsa_read_pem_public_key(f);
	fclose(f);

	if(ecdsa_priv) {
		if(!ecdsa_pub) {
			fprintf(stderr, "WARNING: No (usable) public Ed25519 key found.\n");
			if(ask_fix()) {
				FILE *f = fopen(fname, "a");
				if(f) {
					if(ecdsa_write_pem_public_key(ecdsa_priv, f))
						fprintf(stderr, "Wrote Ed25519 public key to %s.\n", fname);
					else
						fprintf(stderr, "ERROR: could not write Ed25519 public key to %s.\n", fname);
					fclose(f);
				} else {
					fprintf(stderr, "ERROR: could not append to %s: %s\n", fname, strerror(errno));
				}
			}
		} else {
			// TODO: suggest remedies
			char *key1 = ecdsa_get_base64_public_key(ecdsa_pub);
			if(!key1) {
				fprintf(stderr, "ERROR: public Ed25519 key does not work.\n");
				return 1;
			}
			char *key2 = ecdsa_get_base64_public_key(ecdsa_priv);
			if(!key2) {
				free(key1);
				fprintf(stderr, "ERROR: private Ed25519 key does not work.\n");
				return 1;
			}
			int result = strcmp(key1, key2);
			free(key1);
			free(key2);
			if(result) {
				fprintf(stderr, "ERROR: public and private Ed25519 keys do not match.\n");
				return 1;
			}
		}
	} else {
		if(ecdsa_pub)
			fprintf(stderr, "WARNING: A public Ed25519 key was found but no private key is known.\n");
	}

	// Check whether scripts are executable

	struct dirent *ent;
	DIR *dir = opendir(confbase);
	if(!dir) {
		fprintf(stderr, "ERROR: cannot read directory %s: %s\n", confbase, strerror(errno));
		return 1;
	}

	while((ent = readdir(dir))) {
		if(strtailcmp(ent->d_name, "-up") && strtailcmp(ent->d_name, "-down"))
			continue;

		strncpy(fname, ent->d_name, sizeof fname);
		char *dash = strrchr(fname, '-');
		if(!dash)
			continue;
		*dash = 0;

		if(strcmp(fname, "tinc") && strcmp(fname, "host") && strcmp(fname, "subnet")) {
			static bool explained = false;
			fprintf(stderr, "WARNING: Unknown script %s" SLASH "%s found.\n", confbase, ent->d_name);
			if(!explained) {
				fprintf(stderr, "The only scripts in %s executed by tinc are:\n", confbase);
				fprintf(stderr, "tinc-up, tinc-down, host-up, host-down, subnet-up and subnet-down.\n");
				explained = true;
			}
			continue;
		}

		snprintf(fname, sizeof fname, "%s" SLASH "%s", confbase, ent->d_name);
		if(access(fname, R_OK | X_OK)) {
			if(errno != EACCES) {
				fprintf(stderr, "ERROR: cannot access %s: %s\n", fname, strerror(errno));
				continue;
			}
			fprintf(stderr, "WARNING: cannot read and execute %s: %s\n", fname, strerror(errno));
			if(ask_fix()) {
				if(chmod(fname, 0755))
					fprintf(stderr, "ERROR: cannot change permissions on %s: %s\n", fname, strerror(errno));
			}
		}
	}
	closedir(dir);

	snprintf(dname, sizeof dname, "%s" SLASH "hosts", confbase);
	dir = opendir(dname);
	if(!dir) {
		fprintf(stderr, "ERROR: cannot read directory %s: %s\n", dname, strerror(errno));
		return 1;
	}

	while((ent = readdir(dir))) {
		if(strtailcmp(ent->d_name, "-up") && strtailcmp(ent->d_name, "-down"))
			continue;

		strncpy(fname, ent->d_name, sizeof fname);
		char *dash = strrchr(fname, '-');
		if(!dash)
			continue;
		*dash = 0;

		snprintf(fname, sizeof fname, "%s" SLASH "hosts" SLASH "%s", confbase, ent->d_name);
		if(access(fname, R_OK | X_OK)) {
			if(errno != EACCES) {
				fprintf(stderr, "ERROR: cannot access %s: %s\n", fname, strerror(errno));
				continue;
			}
			fprintf(stderr, "WARNING: cannot read and execute %s: %s\n", fname, strerror(errno));
			if(ask_fix()) {
				if(chmod(fname, 0755))
					fprintf(stderr, "ERROR: cannot change permissions on %s: %s\n", fname, strerror(errno));
			}
		}
	}
	closedir(dir);
	
	// Check for obsolete / unsafe / unknown configuration variables.

	check_conffile(tinc_conf, true);

	dir = opendir(dname);
	if(dir) {
		while((ent = readdir(dir))) {
			if(!check_id(ent->d_name))
				continue;

			snprintf(fname, sizeof fname, "%s" SLASH "hosts" SLASH "%s", confbase, ent->d_name);
			check_conffile(fname, false);
		}
		closedir(dir);
	}

	return 0;
}
Ejemplo n.º 5
0
int main(int argc, char *argv[]) {
	program_name = argv[0];
	bool initiator = false;
	bool datagram = false;
#ifdef HAVE_LINUX
	bool tun = false;
#endif
	int packetloss = 0;
	int r;
	int option_index = 0;
	ecdsa_t *mykey = NULL, *hiskey = NULL;
	bool quit = false;

	while((r = getopt_long(argc, argv, "dqrtwL:W:v", long_options, &option_index)) != EOF) {
		switch (r) {
			case 0:   /* long option */
				break;

			case 'd': /* datagram mode */
				datagram = true;
				break;

			case 'q': /* close connection on EOF from stdin */
				quit = true;
				break;

			case 'r': /* read only */
				readonly = true;
				break;

			case 't': /* read only */
#ifdef HAVE_LINUX
				tun = true;
#else
				fprintf(stderr, "--tun is only supported on Linux.\n");
				usage();
				return 1;
#endif
				break;

			case 'w': /* write only */
				writeonly = true;
				break;

			case 'L': /* packet loss rate */
				packetloss = atoi(optarg);
				break;

			case 'W': /* replay window size */
				sptps_replaywin = atoi(optarg);
				break;

			case 'v': /* be verbose */
				verbose = true;
				break;

			case '?': /* wrong options */
				usage();
				return 1;

			case 1: /* help */
				usage();
				return 0;

			default:
				break;
		}
	}

	argc -= optind - 1;
	argv += optind - 1;

	if(argc < 4 || argc > 5) {
		fprintf(stderr, "Wrong number of arguments.\n");
		usage();
		return 1;
	}

	if(argc > 4)
		initiator = true;

	srand(time(NULL));

#ifdef HAVE_LINUX
	if(tun) {
		in = out = open("/dev/net/tun", O_RDWR | O_NONBLOCK);
		if(in < 0) {
			fprintf(stderr, "Could not open tun device: %s\n", strerror(errno));
			return 1;
		}
		struct ifreq ifr = {
			.ifr_flags = IFF_TUN
		};
		if(ioctl(in, TUNSETIFF, &ifr)) {
			fprintf(stderr, "Could not configure tun interface: %s\n", strerror(errno));
			return 1;
		}
		ifr.ifr_name[IFNAMSIZ - 1] = 0;
		fprintf(stderr, "Using tun interface %s\n", ifr.ifr_name);
	}
#endif

#ifdef HAVE_MINGW
	static struct WSAData wsa_state;
	if(WSAStartup(MAKEWORD(2, 2), &wsa_state))
		return 1;
#endif

	struct addrinfo *ai, hint;
	memset(&hint, 0, sizeof hint);

	hint.ai_family = AF_UNSPEC;
	hint.ai_socktype = datagram ? SOCK_DGRAM : SOCK_STREAM;
	hint.ai_protocol = datagram ? IPPROTO_UDP : IPPROTO_TCP;
	hint.ai_flags = initiator ? 0 : AI_PASSIVE;

	if(getaddrinfo(initiator ? argv[3] : NULL, initiator ? argv[4] : argv[3], &hint, &ai) || !ai) {
		fprintf(stderr, "getaddrinfo() failed: %s\n", sockstrerror(sockerrno));
		return 1;
	}

	int sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
	if(sock < 0) {
		fprintf(stderr, "Could not create socket: %s\n", sockstrerror(sockerrno));
		return 1;
	}

	int one = 1;
	setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&one, sizeof one);

	if(initiator) {
		if(connect(sock, ai->ai_addr, ai->ai_addrlen)) {
			fprintf(stderr, "Could not connect to peer: %s\n", sockstrerror(sockerrno));
			return 1;
		}
		fprintf(stderr, "Connected\n");
	} else {
		if(bind(sock, ai->ai_addr, ai->ai_addrlen)) {
			fprintf(stderr, "Could not bind socket: %s\n", sockstrerror(sockerrno));
			return 1;
		}

		if(!datagram) {
			if(listen(sock, 1)) {
				fprintf(stderr, "Could not listen on socket: %s\n", sockstrerror(sockerrno));
				return 1;
			}
			fprintf(stderr, "Listening...\n");

			sock = accept(sock, NULL, NULL);
			if(sock < 0) {
				fprintf(stderr, "Could not accept connection: %s\n", sockstrerror(sockerrno));
				return 1;
			}
		} else {
			fprintf(stderr, "Listening...\n");

			char buf[65536];
			struct sockaddr addr;
			socklen_t addrlen = sizeof addr;

			if(recvfrom(sock, buf, sizeof buf, MSG_PEEK, &addr, &addrlen) <= 0) {
				fprintf(stderr, "Could not read from socket: %s\n", sockstrerror(sockerrno));
				return 1;
			}

			if(connect(sock, &addr, addrlen)) {
				fprintf(stderr, "Could not accept connection: %s\n", sockstrerror(sockerrno));
				return 1;
			}
		}

		fprintf(stderr, "Connected\n");
	}

	crypto_init();

	FILE *fp = fopen(argv[1], "r");
	if(!fp) {
		fprintf(stderr, "Could not open %s: %s\n", argv[1], strerror(errno));
		return 1;
	}
	if(!(mykey = ecdsa_read_pem_private_key(fp)))
		return 1;
	fclose(fp);

	fp = fopen(argv[2], "r");
	if(!fp) {
		fprintf(stderr, "Could not open %s: %s\n", argv[2], strerror(errno));
		return 1;
	}
	if(!(hiskey = ecdsa_read_pem_public_key(fp)))
		return 1;
	fclose(fp);

	if(verbose)
		fprintf(stderr, "Keys loaded\n");

	sptps_t s;
	if(!sptps_start(&s, &sock, initiator, datagram, mykey, hiskey, "sptps_test", 10, send_data, receive_record))
		return 1;

	while(true) {
		if(writeonly && readonly)
			break;

		char buf[65535] = "";

		fd_set fds;
		FD_ZERO(&fds);
#ifndef HAVE_MINGW
		if(!readonly && s.instate)
			FD_SET(in, &fds);
#endif
		FD_SET(sock, &fds);
		if(select(sock + 1, &fds, NULL, NULL, NULL) <= 0)
			return 1;

		if(FD_ISSET(in, &fds)) {
			ssize_t len = read(in, buf, sizeof buf);
			if(len < 0) {
				fprintf(stderr, "Could not read from stdin: %s\n", strerror(errno));
				return 1;
			}
			if(len == 0) {
				if(quit)
					break;
				readonly = true;
				continue;
			}
			if(buf[0] == '#')
				s.outseqno = atoi(buf + 1);
			if(buf[0] == '^')
				sptps_send_record(&s, SPTPS_HANDSHAKE, NULL, 0);
			else if(buf[0] == '$') {
				sptps_force_kex(&s);
				if(len > 1)
					sptps_send_record(&s, 0, buf, len);
			} else
			if(!sptps_send_record(&s, buf[0] == '!' ? 1 : 0, buf, (len == 1 && buf[0] == '\n') ? 0 : buf[0] == '*' ? sizeof buf : len))
				return 1;
		}

		if(FD_ISSET(sock, &fds)) {
			ssize_t len = recv(sock, buf, sizeof buf, 0);
			if(len < 0) {
				fprintf(stderr, "Could not read from socket: %s\n", sockstrerror(sockerrno));
				return 1;
			}
			if(len == 0) {
				fprintf(stderr, "Connection terminated by peer.\n");
				break;
			}
			if(verbose) {
				char hex[len * 2 + 1];
				bin2hex(buf, hex, len);
				fprintf(stderr, "Received %d bytes of data:\n%s\n", (int)len, hex);
			}
			if(packetloss && (rand() % 100) < packetloss) {
				if(verbose)
					fprintf(stderr, "Dropped.\n");
				continue;
			}
			char *bufp = buf;
			while(len) {
				size_t done = sptps_receive_data(&s, bufp, len);
				if(!done) {
					if(!datagram)
						return 1;
				} else {
					break;
				}

				bufp += done;
				len -= done;
			}
		}
	}

	if(!sptps_stop(&s))
		return 1;

	return 0;
}