mirror of https://github.com/cy384/ssheven.git
code cleanup/rearrangement
This commit is contained in:
parent
9a19d9695c
commit
1eb5907ac4
|
@ -1,10 +1,14 @@
|
||||||
|
# ssheven
|
||||||
|
#
|
||||||
|
# Copyright (c) 20201 by cy384 <cy384@cy384.com>
|
||||||
|
# See LICENSE file for details
|
||||||
|
|
||||||
cmake_minimum_required(VERSION 3.9)
|
cmake_minimum_required(VERSION 3.9)
|
||||||
|
|
||||||
add_application(ssheven CREATOR "SSH7" ssheven.c ssheven-console.c ssheven.r)
|
add_application(ssheven CREATOR "SSH7" ssheven.c ssheven-console.c ssheven.r ssheven-net.c ssheven-debug.c)
|
||||||
|
|
||||||
IF(CMAKE_SYSTEM_NAME MATCHES Retro68)
|
IF(CMAKE_SYSTEM_NAME MATCHES Retro68)
|
||||||
# for 68k
|
# for 68k
|
||||||
# add "-mcpu=68020" once we figure out retro68 bug
|
|
||||||
set_target_properties(ssheven PROPERTIES COMPILE_FLAGS "-ffunction-sections -mcpu=68020 -O3 -Wall -Wextra -Wno-unused-parameter")
|
set_target_properties(ssheven PROPERTIES COMPILE_FLAGS "-ffunction-sections -mcpu=68020 -O3 -Wall -Wextra -Wno-unused-parameter")
|
||||||
set_target_properties(ssheven PROPERTIES LINK_FLAGS "-Wl,-gc-sections -Wl,--mac-segments -Wl,${CMAKE_CURRENT_SOURCE_DIR}/ssheven.segmap")
|
set_target_properties(ssheven PROPERTIES LINK_FLAGS "-Wl,-gc-sections -Wl,--mac-segments -Wl,${CMAKE_CURRENT_SOURCE_DIR}/ssheven.segmap")
|
||||||
target_link_libraries(ssheven ssh2 mbedtls mbedx509 mbedcrypto OpenTransportApp OpenTransport OpenTptInet vterm retrocrt)
|
target_link_libraries(ssheven ssh2 mbedtls mbedx509 mbedcrypto OpenTransportApp OpenTransport OpenTptInet vterm retrocrt)
|
||||||
|
|
|
@ -7,6 +7,11 @@
|
||||||
|
|
||||||
#include "ssheven.h"
|
#include "ssheven.h"
|
||||||
#include "ssheven-console.h"
|
#include "ssheven-console.h"
|
||||||
|
#include "ssheven-net.h"
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <Sound.h>
|
||||||
|
|
||||||
char key_to_vterm[256] = { VTERM_KEY_NONE };
|
char key_to_vterm[256] = { VTERM_KEY_NONE };
|
||||||
|
|
||||||
|
@ -47,6 +52,19 @@ void setup_key_translation(void)
|
||||||
//key_to_vterm[0] = VTERM_KEY_KP_EQUAL;
|
//key_to_vterm[0] = VTERM_KEY_KP_EQUAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Rect cell_rect(int x, int y, Rect bounds)
|
||||||
|
{
|
||||||
|
Rect r = { (short) (bounds.top + y * con.cell_height), (short) (bounds.left + x * con.cell_width + 2),
|
||||||
|
(short) (bounds.top + (y+1) * con.cell_height), (short) (bounds.left + (x+1) * con.cell_width + 2) };
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
void print_string(const char* c)
|
||||||
|
{
|
||||||
|
vterm_input_write(con.vterm, c, strlen(c));
|
||||||
|
}
|
||||||
|
|
||||||
inline void draw_char(int x, int y, Rect* r, char c)
|
inline void draw_char(int x, int y, Rect* r, char c)
|
||||||
{
|
{
|
||||||
MoveTo(r->left + x * con.cell_width + 2, r->top + ((y+1) * con.cell_height) - 2);
|
MoveTo(r->left + x * con.cell_width + 2, r->top + ((y+1) * con.cell_height) - 2);
|
||||||
|
@ -129,26 +147,6 @@ inline int idx2qd(VTermColor c)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// closely inspired by the retro68 console library
|
|
||||||
void draw_screen(Rect* r)
|
|
||||||
{
|
|
||||||
switch (prefs.display_mode)
|
|
||||||
{
|
|
||||||
case FASTEST:
|
|
||||||
draw_screen_fast(r);
|
|
||||||
break;
|
|
||||||
case MONOCHROME:
|
|
||||||
draw_screen_mono(r);
|
|
||||||
break;
|
|
||||||
case COLOR:
|
|
||||||
draw_screen_color(r);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
draw_screen_color(r);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// p is in window local coordinates
|
// p is in window local coordinates
|
||||||
void mouse_click(Point p, bool click)
|
void mouse_click(Point p, bool click)
|
||||||
{
|
{
|
||||||
|
@ -398,6 +396,25 @@ void draw_screen_mono(Rect* r)
|
||||||
con.win->clipRgn = old;
|
con.win->clipRgn = old;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void draw_screen(Rect* r)
|
||||||
|
{
|
||||||
|
switch (prefs.display_mode)
|
||||||
|
{
|
||||||
|
case FASTEST:
|
||||||
|
draw_screen_fast(r);
|
||||||
|
break;
|
||||||
|
case MONOCHROME:
|
||||||
|
draw_screen_mono(r);
|
||||||
|
break;
|
||||||
|
case COLOR:
|
||||||
|
draw_screen_color(r);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
draw_screen_color(r);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void ruler(Rect* r)
|
void ruler(Rect* r)
|
||||||
{
|
{
|
||||||
char itoc[] = {'0','1','2','3','4','5','6','7','8','9'};
|
char itoc[] = {'0','1','2','3','4','5','6','7','8','9'};
|
||||||
|
@ -443,11 +460,6 @@ void print_int(int d)
|
||||||
print_string(buffer+i+1-negative);
|
print_string(buffer+i+1-negative);
|
||||||
}
|
}
|
||||||
|
|
||||||
void print_string(const char* c)
|
|
||||||
{
|
|
||||||
vterm_input_write(con.vterm, c, strlen(c));
|
|
||||||
}
|
|
||||||
|
|
||||||
void printf_i(const char* str, ...)
|
void printf_i(const char* str, ...)
|
||||||
{
|
{
|
||||||
va_list args;
|
va_list args;
|
||||||
|
@ -486,15 +498,6 @@ void printf_i(const char* str, ...)
|
||||||
va_end(args);
|
va_end(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_window_title(WindowPtr w, const char* c_name)
|
|
||||||
{
|
|
||||||
Str255 pascal_name;
|
|
||||||
strncpy((char *) &pascal_name[1], c_name, 254);
|
|
||||||
pascal_name[0] = strlen(c_name);
|
|
||||||
|
|
||||||
SetWTitle(w, pascal_name);
|
|
||||||
}
|
|
||||||
|
|
||||||
int bell(void* user)
|
int bell(void* user)
|
||||||
{
|
{
|
||||||
SysBeep(30);
|
SysBeep(30);
|
||||||
|
@ -517,14 +520,6 @@ int movecursor(VTermPos pos, VTermPos oldpos, int visible, void *user)
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
Rect cell_rect(int x, int y, Rect bounds)
|
|
||||||
{
|
|
||||||
Rect r = { (short) (bounds.top + y * con.cell_height), (short) (bounds.left + x * con.cell_width + 2),
|
|
||||||
(short) (bounds.top + (y+1) * con.cell_height), (short) (bounds.left + (x+1) * con.cell_width + 2) };
|
|
||||||
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
int damage(VTermRect rect, void *user)
|
int damage(VTermRect rect, void *user)
|
||||||
{
|
{
|
||||||
Rect topleft = cell_rect(rect.start_col, rect.start_row, (con.win->portRect));
|
Rect topleft = cell_rect(rect.start_col, rect.start_row, (con.win->portRect));
|
||||||
|
|
|
@ -7,35 +7,14 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "ssheven.h"
|
#include "MacTypes.h"
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
void console_setup(void);
|
void console_setup(void);
|
||||||
|
|
||||||
void draw_char(int x, int y, Rect* r, char c);
|
|
||||||
void draw_screen(Rect* r);
|
void draw_screen(Rect* r);
|
||||||
|
|
||||||
void draw_screen_fast(Rect* r);
|
|
||||||
void draw_screen_mono(Rect* r);
|
|
||||||
void draw_screen_color(Rect* r);
|
|
||||||
|
|
||||||
void bump_up_line();
|
|
||||||
|
|
||||||
int is_printable(char c);
|
|
||||||
|
|
||||||
void print_char(char c);
|
|
||||||
void print_string(const char* c);
|
|
||||||
void print_int(int d);
|
|
||||||
|
|
||||||
void printf_i(const char* c, ...);
|
void printf_i(const char* c, ...);
|
||||||
|
|
||||||
void set_window_title(WindowPtr w, const char* c_name);
|
|
||||||
|
|
||||||
void ruler(Rect* r);
|
|
||||||
|
|
||||||
Rect cell_rect(int x, int y, Rect bounds);
|
|
||||||
|
|
||||||
void toggle_cursor(void);
|
|
||||||
void check_cursor(void);
|
void check_cursor(void);
|
||||||
|
|
||||||
void mouse_click(Point p, bool click);
|
void mouse_click(Point p, bool click);
|
||||||
|
|
|
@ -1,11 +1,14 @@
|
||||||
/*
|
/*
|
||||||
* ssheven
|
* ssheven
|
||||||
*
|
*
|
||||||
* Copyright (c) 2020 by cy384 <cy384@cy384.com>
|
* Copyright (c) 2021 by cy384 <cy384@cy384.com>
|
||||||
* See LICENSE file for details
|
* See LICENSE file for details
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* handy debugging string conversions */
|
#include <OpenTransport.h>
|
||||||
|
#include <OpenTptInternet.h>
|
||||||
|
|
||||||
|
#include <libssh2.h>
|
||||||
|
|
||||||
/* convert libssh2 errors into strings */
|
/* convert libssh2 errors into strings */
|
||||||
const char* libssh2_error_string(int i)
|
const char* libssh2_error_string(int i)
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
/*
|
||||||
|
* ssheven
|
||||||
|
*
|
||||||
|
* Copyright (c) 2021 by cy384 <cy384@cy384.com>
|
||||||
|
* See LICENSE file for details
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define OT_CHECK(X) err = (X); if (err != noErr) { printf_i("" #X " failed\r\n"); return 0; };
|
||||||
|
#define SSH_CHECK(X) rc = (X); if (rc != LIBSSH2_ERROR_NONE) { printf_i("" #X " failed: %s\r\n", libssh2_error_string(rc)); return 0;};
|
||||||
|
|
||||||
|
const char* libssh2_error_string(int i);
|
||||||
|
const char* OT_event_string(int i);
|
|
@ -0,0 +1,619 @@
|
||||||
|
/*
|
||||||
|
* ssheven
|
||||||
|
*
|
||||||
|
* Copyright (c) 2021 by cy384 <cy384@cy384.com>
|
||||||
|
* See LICENSE file for details
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "ssheven.h"
|
||||||
|
#include "ssheven-net.h"
|
||||||
|
#include "ssheven-console.h"
|
||||||
|
#include "ssheven-debug.h"
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <Script.h>
|
||||||
|
#include <Threads.h>
|
||||||
|
|
||||||
|
#include <mbedtls/base64.h>
|
||||||
|
|
||||||
|
void ssh_write(char* buf, size_t len)
|
||||||
|
{
|
||||||
|
if (read_thread_state == OPEN && read_thread_command != EXIT)
|
||||||
|
{
|
||||||
|
int r = libssh2_channel_write(ssh_con.channel, buf, len);
|
||||||
|
|
||||||
|
if (r < 1)
|
||||||
|
{
|
||||||
|
printf_i("Failed to write to channel, closing connection.\r\n");
|
||||||
|
read_thread_command = EXIT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// read from the channel and print to console
|
||||||
|
void ssh_read(void)
|
||||||
|
{
|
||||||
|
ssize_t rc = libssh2_channel_read(ssh_con.channel, ssh_con.recv_buffer, SSHEVEN_BUFFER_SIZE);
|
||||||
|
|
||||||
|
if (rc == 0) return;
|
||||||
|
|
||||||
|
if (rc <= 0)
|
||||||
|
{
|
||||||
|
printf_i("channel read error: %s\r\n", libssh2_error_string(rc));
|
||||||
|
read_thread_command = EXIT;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (rc > 0)
|
||||||
|
{
|
||||||
|
rc -= vterm_input_write(con.vterm, ssh_con.recv_buffer, rc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void end_connection(void)
|
||||||
|
{
|
||||||
|
read_thread_state = CLEANUP;
|
||||||
|
|
||||||
|
OSStatus err = noErr;
|
||||||
|
|
||||||
|
if (ssh_con.channel)
|
||||||
|
{
|
||||||
|
libssh2_channel_send_eof(ssh_con.channel);
|
||||||
|
libssh2_channel_close(ssh_con.channel);
|
||||||
|
libssh2_channel_free(ssh_con.channel);
|
||||||
|
libssh2_session_disconnect(ssh_con.session, "Normal Shutdown, Thank you for playing");
|
||||||
|
libssh2_session_free(ssh_con.session);
|
||||||
|
}
|
||||||
|
|
||||||
|
libssh2_exit();
|
||||||
|
|
||||||
|
if (ssh_con.endpoint != kOTInvalidEndpointRef)
|
||||||
|
{
|
||||||
|
// request to close the TCP connection
|
||||||
|
OTSndOrderlyDisconnect(ssh_con.endpoint);
|
||||||
|
|
||||||
|
// discard remaining data so we can finish closing the connection
|
||||||
|
int rc = 1;
|
||||||
|
OTFlags ot_flags;
|
||||||
|
while (rc != kOTLookErr)
|
||||||
|
{
|
||||||
|
rc = OTRcv(ssh_con.endpoint, ssh_con.recv_buffer, 1, &ot_flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
// finish closing the TCP connection
|
||||||
|
OSStatus result = OTLook(ssh_con.endpoint);
|
||||||
|
|
||||||
|
switch (result)
|
||||||
|
{
|
||||||
|
case T_DISCONNECT:
|
||||||
|
OTRcvDisconnect(ssh_con.endpoint, nil);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case T_ORDREL:
|
||||||
|
err = OTRcvOrderlyDisconnect(ssh_con.endpoint);
|
||||||
|
if (err == noErr)
|
||||||
|
{
|
||||||
|
err = OTSndOrderlyDisconnect(ssh_con.endpoint);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
printf_i("Unexpected OTLook result while closing: %s\r\n", OT_event_string(result));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// release endpoint
|
||||||
|
err = OTUnbind(ssh_con.endpoint);
|
||||||
|
if (err != noErr) printf_i("OTUnbind failed.\r\n");
|
||||||
|
|
||||||
|
err = OTCloseProvider(ssh_con.endpoint);
|
||||||
|
if (err != noErr) printf_i("OTCloseProvider failed.\r\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
read_thread_state = DONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int check_network_events(void)
|
||||||
|
{
|
||||||
|
int ok = 1;
|
||||||
|
OSStatus err = noErr;
|
||||||
|
|
||||||
|
// check if we have any new network events
|
||||||
|
OTResult look_result = OTLook(ssh_con.endpoint);
|
||||||
|
|
||||||
|
switch (look_result)
|
||||||
|
{
|
||||||
|
case T_DATA:
|
||||||
|
case T_EXDATA:
|
||||||
|
// got data
|
||||||
|
// we always try to read, so ignore this event
|
||||||
|
break;
|
||||||
|
|
||||||
|
case T_RESET:
|
||||||
|
// connection reset? close it/give up
|
||||||
|
end_connection();
|
||||||
|
ok = 0;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case T_DISCONNECT:
|
||||||
|
// got disconnected
|
||||||
|
OTRcvDisconnect(ssh_con.endpoint, nil);
|
||||||
|
end_connection();
|
||||||
|
ok = 0;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case T_ORDREL:
|
||||||
|
// nice tcp disconnect requested by remote
|
||||||
|
err = OTRcvOrderlyDisconnect(ssh_con.endpoint);
|
||||||
|
if (err == noErr)
|
||||||
|
{
|
||||||
|
err = OTSndOrderlyDisconnect(ssh_con.endpoint);
|
||||||
|
}
|
||||||
|
end_connection();
|
||||||
|
ok = 0;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
// something weird or irrelevant: ignore it
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ssh_setup_terminal(void)
|
||||||
|
{
|
||||||
|
int rc = 0;
|
||||||
|
|
||||||
|
SSH_CHECK(libssh2_channel_request_pty_ex(ssh_con.channel, prefs.terminal_string, (strlen(prefs.terminal_string)), NULL, 0, con.size_x, con.size_y, 0, 0));
|
||||||
|
SSH_CHECK(libssh2_channel_shell(ssh_con.channel));
|
||||||
|
|
||||||
|
read_thread_state = OPEN;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns base64 sha256 hash of key as a malloc'd pascal string
|
||||||
|
char* host_hash(void)
|
||||||
|
{
|
||||||
|
size_t length = 0;
|
||||||
|
char* human_readable = malloc(66);
|
||||||
|
memset(human_readable, 0, 66);
|
||||||
|
const char* host_key_hash = NULL;
|
||||||
|
|
||||||
|
host_key_hash = libssh2_hostkey_hash(ssh_con.session, LIBSSH2_HOSTKEY_HASH_SHA256);
|
||||||
|
mbedtls_base64_encode((unsigned char*)human_readable+1, 64, &length, (unsigned const char*)host_key_hash, 32);
|
||||||
|
|
||||||
|
human_readable[0] = (unsigned char)length;
|
||||||
|
|
||||||
|
return human_readable;
|
||||||
|
}
|
||||||
|
|
||||||
|
char* known_hosts_full_path(int* found)
|
||||||
|
{
|
||||||
|
int ok = 1;
|
||||||
|
short foundVRefNum = 0;
|
||||||
|
long foundDirID = 0;
|
||||||
|
FSSpec known_hosts_file;
|
||||||
|
*found = 0;
|
||||||
|
|
||||||
|
OSType pref_type = 'SH7p';
|
||||||
|
OSType creator_type = 'SSH7';
|
||||||
|
|
||||||
|
// find the preferences folder on the system disk, create folder if needed
|
||||||
|
OSErr e = FindFolder(kOnSystemDisk, kPreferencesFolderType, kCreateFolder, &foundVRefNum, &foundDirID);
|
||||||
|
if (e != noErr) ok = 0;
|
||||||
|
|
||||||
|
// make an FSSpec for the new file we want to make
|
||||||
|
if (ok)
|
||||||
|
{
|
||||||
|
e = FSMakeFSSpec(foundVRefNum, foundDirID, "\pknown_hosts", &known_hosts_file);
|
||||||
|
|
||||||
|
// if the file exists, we found it else make an empty one
|
||||||
|
if (e == noErr) *found = 1;
|
||||||
|
else if (e == noErr) e = FSpCreate(&known_hosts_file, creator_type, pref_type, smSystemScript);
|
||||||
|
|
||||||
|
if (e != noErr) ok = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Handle full_path_handle;
|
||||||
|
int path_length;
|
||||||
|
|
||||||
|
FSpPathFromLocation(&known_hosts_file, &path_length, &full_path_handle);
|
||||||
|
char* full_path = malloc(path_length+1);
|
||||||
|
strncpy(full_path, (char*)(*full_path_handle), path_length+1);
|
||||||
|
DisposeHandle(full_path_handle);
|
||||||
|
|
||||||
|
return full_path;
|
||||||
|
}
|
||||||
|
|
||||||
|
int known_hosts(void)
|
||||||
|
{
|
||||||
|
int safe_to_connect = 1;
|
||||||
|
int recognized_key = 0;
|
||||||
|
int known_hosts_file_exists = 0;
|
||||||
|
|
||||||
|
char* known_hosts_file_path = known_hosts_full_path(&known_hosts_file_exists);
|
||||||
|
char* hash_string = NULL;
|
||||||
|
|
||||||
|
size_t key_len = 0;
|
||||||
|
int key_type = 0;
|
||||||
|
const char* host_key = libssh2_session_hostkey(ssh_con.session, &key_len, &key_type);
|
||||||
|
|
||||||
|
LIBSSH2_KNOWNHOSTS* known_hosts = libssh2_knownhost_init(ssh_con.session);
|
||||||
|
|
||||||
|
if (known_hosts_file_exists)
|
||||||
|
{
|
||||||
|
// load known hosts file
|
||||||
|
|
||||||
|
int e = libssh2_knownhost_readfile(known_hosts, known_hosts_file_path, LIBSSH2_KNOWNHOST_FILE_OPENSSH);
|
||||||
|
if (e < 0)
|
||||||
|
{
|
||||||
|
printf_i("Failed to load known hosts file: %s\r\n", libssh2_error_string(e));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
printf_i("No known hosts file found.\r\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (safe_to_connect)
|
||||||
|
{
|
||||||
|
// hostnames need to be either plain or of the format "[host]:port"
|
||||||
|
prefs.hostname[prefs.hostname[0]+1] = '\0';
|
||||||
|
int e = libssh2_knownhost_check(known_hosts, prefs.hostname+1, host_key, key_len, LIBSSH2_KNOWNHOST_TYPE_PLAIN | LIBSSH2_KNOWNHOST_KEYENC_RAW, NULL);
|
||||||
|
prefs.hostname[prefs.hostname[0]+1] = ':';
|
||||||
|
|
||||||
|
switch (e)
|
||||||
|
{
|
||||||
|
case LIBSSH2_KNOWNHOST_CHECK_FAILURE:
|
||||||
|
printf_i("Failed to check known hosts against server public key!\r\n");
|
||||||
|
safe_to_connect = 0;
|
||||||
|
break;
|
||||||
|
case LIBSSH2_KNOWNHOST_CHECK_NOTFOUND:
|
||||||
|
printf_i("No matching host found.\r\n");
|
||||||
|
break;
|
||||||
|
case LIBSSH2_KNOWNHOST_CHECK_MATCH:
|
||||||
|
//printf_i("Host and key match found in known hosts.\r\n");
|
||||||
|
recognized_key = 1;
|
||||||
|
break;
|
||||||
|
case LIBSSH2_KNOWNHOST_CHECK_MISMATCH:
|
||||||
|
printf_i("WARNING! Host found in known hosts but key doesn't match!\r\n");
|
||||||
|
safe_to_connect = 0;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
printf_i("Unexpected error while checking known-hosts: %d\r\n", e);
|
||||||
|
safe_to_connect = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
hash_string = host_hash();
|
||||||
|
//printf_i("Host key hash (SHA256): %s\r\n", hash_string+1);
|
||||||
|
|
||||||
|
// ask the user to confirm if we're seeing a new host+key combo
|
||||||
|
if (safe_to_connect && !recognized_key)
|
||||||
|
{
|
||||||
|
// ask the user if the key is OK
|
||||||
|
DialogPtr dlg = GetNewDialog(DLOG_NEW_HOST, 0, (WindowPtr)-1);
|
||||||
|
|
||||||
|
DialogItemType type;
|
||||||
|
Handle itemH;
|
||||||
|
Rect box;
|
||||||
|
|
||||||
|
// draw default button indicator around the connect button
|
||||||
|
GetDialogItem(dlg, 2, &type, &itemH, &box);
|
||||||
|
SetDialogItem(dlg, 2, type, (Handle)NewUserItemUPP(&ButtonFrameProc), &box);
|
||||||
|
|
||||||
|
// write the hash string into the dialog window
|
||||||
|
ControlHandle hash_text_box;
|
||||||
|
GetDialogItem(dlg, 4, &type, &itemH, &box);
|
||||||
|
hash_text_box = (ControlHandle)itemH;
|
||||||
|
SetDialogItemText((Handle)hash_text_box, (ConstStr255Param)hash_string);
|
||||||
|
|
||||||
|
// let the modalmanager do everything
|
||||||
|
// stop on reject or accept
|
||||||
|
short item;
|
||||||
|
do {
|
||||||
|
ModalDialog(NULL, &item);
|
||||||
|
} while(item != 1 && item != 5);
|
||||||
|
|
||||||
|
// reject if user hit reject
|
||||||
|
if (item == 1)
|
||||||
|
{
|
||||||
|
safe_to_connect = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
DisposeDialog(dlg);
|
||||||
|
FlushEvents(everyEvent, -1);
|
||||||
|
|
||||||
|
printf_i("Saving host and key... ");
|
||||||
|
|
||||||
|
int save_type = 0;
|
||||||
|
save_type |= LIBSSH2_KNOWNHOST_TYPE_PLAIN;
|
||||||
|
save_type |= LIBSSH2_KNOWNHOST_KEYENC_RAW;
|
||||||
|
|
||||||
|
switch (key_type)
|
||||||
|
{
|
||||||
|
default:
|
||||||
|
__attribute__ ((fallthrough));
|
||||||
|
case LIBSSH2_HOSTKEY_TYPE_UNKNOWN:
|
||||||
|
save_type |= LIBSSH2_KNOWNHOST_KEY_UNKNOWN;
|
||||||
|
break;
|
||||||
|
case LIBSSH2_HOSTKEY_TYPE_RSA:
|
||||||
|
save_type |= LIBSSH2_KNOWNHOST_KEY_SSHRSA;
|
||||||
|
break;
|
||||||
|
case LIBSSH2_HOSTKEY_TYPE_DSS:
|
||||||
|
save_type |= LIBSSH2_KNOWNHOST_KEY_SSHDSS;
|
||||||
|
break;
|
||||||
|
case LIBSSH2_HOSTKEY_TYPE_ECDSA_256:
|
||||||
|
save_type |= LIBSSH2_KNOWNHOST_KEY_ECDSA_256;
|
||||||
|
break;
|
||||||
|
case LIBSSH2_HOSTKEY_TYPE_ECDSA_384:
|
||||||
|
save_type |= LIBSSH2_KNOWNHOST_KEY_ECDSA_384;
|
||||||
|
break;
|
||||||
|
case LIBSSH2_HOSTKEY_TYPE_ECDSA_521:
|
||||||
|
save_type |= LIBSSH2_KNOWNHOST_KEY_ECDSA_521;
|
||||||
|
break;
|
||||||
|
case LIBSSH2_HOSTKEY_TYPE_ED25519:
|
||||||
|
save_type |= LIBSSH2_KNOWNHOST_KEY_ED25519;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// hostnames need to be either plain or of the format "[host]:port"
|
||||||
|
prefs.hostname[prefs.hostname[0]+1] = '\0';
|
||||||
|
int e = libssh2_knownhost_addc(known_hosts, prefs.hostname+1, NULL, host_key, key_len, NULL, 0, save_type, NULL);
|
||||||
|
prefs.hostname[prefs.hostname[0]+1] = ':';
|
||||||
|
|
||||||
|
if (e != 0) printf_i("failed to add to known hosts: %s\r\n", libssh2_error_string(e));
|
||||||
|
else
|
||||||
|
{
|
||||||
|
e = libssh2_knownhost_writefile(known_hosts, known_hosts_file_path, LIBSSH2_KNOWNHOST_FILE_OPENSSH);
|
||||||
|
if (e != 0) printf_i("failed to save known hosts file: %s\r\n", libssh2_error_string(e));
|
||||||
|
else printf_i("done.\r\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
free(hash_string);
|
||||||
|
|
||||||
|
libssh2_knownhost_free(known_hosts);
|
||||||
|
|
||||||
|
free(known_hosts_file_path);
|
||||||
|
|
||||||
|
return safe_to_connect;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ssize_t network_recv_callback(libssh2_socket_t sock, void *buffer,
|
||||||
|
size_t length, int flags, void **abstract)
|
||||||
|
{
|
||||||
|
OTResult ret = kOTNoDataErr;
|
||||||
|
OTFlags ot_flags = 0;
|
||||||
|
|
||||||
|
if (length == 0) return 0;
|
||||||
|
|
||||||
|
// in non-blocking mode, returns instantly always
|
||||||
|
ret = OTRcv(ssh_con.endpoint, buffer, length, &ot_flags);
|
||||||
|
|
||||||
|
// if we got bytes, return them
|
||||||
|
if (ret >= 0) return ret;
|
||||||
|
|
||||||
|
// if no data, let other threads run, then tell caller to call again
|
||||||
|
if (ret == kOTNoDataErr && read_thread_command != EXIT)
|
||||||
|
{
|
||||||
|
YieldToAnyThread();
|
||||||
|
return -EAGAIN;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if we got anything other than data or nothing, return an error
|
||||||
|
if (ret != kOTNoDataErr) return -1;
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ssize_t network_send_callback(libssh2_socket_t sock, const void *buffer,
|
||||||
|
size_t length, int flags, void **abstract)
|
||||||
|
{
|
||||||
|
int ret = -1;
|
||||||
|
|
||||||
|
ret = OTSnd(ssh_con.endpoint, (void*) buffer, length, 0);
|
||||||
|
|
||||||
|
// TODO FIXME handle cases better, i.e. translate error cases
|
||||||
|
if (ret == kOTLookErr)
|
||||||
|
{
|
||||||
|
OTResult lookresult = OTLook(ssh_con.endpoint);
|
||||||
|
//printf("kOTLookErr, reason: %ld\n", lookresult);
|
||||||
|
|
||||||
|
switch (lookresult)
|
||||||
|
{
|
||||||
|
default:
|
||||||
|
//printf("what?\n");
|
||||||
|
ret = -1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (ssize_t) ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ssh_end_msg_callback(LIBSSH2_SESSION* session, int reason, const char *message,
|
||||||
|
int message_len, const char *language, int language_len,
|
||||||
|
void **abstract)
|
||||||
|
{
|
||||||
|
printf_i("got a disconnect msg\r\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
int init_connection(char* hostname)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
// OT vars
|
||||||
|
OSStatus err = noErr;
|
||||||
|
TCall sndCall;
|
||||||
|
DNSAddress hostDNSAddress;
|
||||||
|
|
||||||
|
|
||||||
|
// open TCP endpoint
|
||||||
|
ssh_con.endpoint = OTOpenEndpoint(OTCreateConfiguration(kTCPName), 0, nil, &err);
|
||||||
|
|
||||||
|
if (err != noErr || ssh_con.endpoint == kOTInvalidEndpointRef)
|
||||||
|
{
|
||||||
|
printf_i("Failed to open Open Transport TCP endpoint.\r\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
OT_CHECK(OTSetSynchronous(ssh_con.endpoint));
|
||||||
|
OT_CHECK(OTSetBlocking(ssh_con.endpoint));
|
||||||
|
OT_CHECK(OTUseSyncIdleEvents(ssh_con.endpoint, false));
|
||||||
|
|
||||||
|
OT_CHECK(OTBind(ssh_con.endpoint, nil, nil));
|
||||||
|
|
||||||
|
OT_CHECK(OTSetNonBlocking(ssh_con.endpoint));
|
||||||
|
|
||||||
|
// set up address struct, do the DNS lookup, and connect
|
||||||
|
OTMemzero(&sndCall, sizeof(TCall));
|
||||||
|
|
||||||
|
sndCall.addr.buf = (UInt8 *) &hostDNSAddress;
|
||||||
|
sndCall.addr.len = OTInitDNSAddress(&hostDNSAddress, (char *) hostname);
|
||||||
|
|
||||||
|
printf_i("Connecting endpoint... "); YieldToAnyThread();
|
||||||
|
OT_CHECK(OTConnect(ssh_con.endpoint, &sndCall, nil));
|
||||||
|
printf_i("done.\r\n"); YieldToAnyThread();
|
||||||
|
|
||||||
|
// init libssh2
|
||||||
|
SSH_CHECK(libssh2_init(0));
|
||||||
|
YieldToAnyThread();
|
||||||
|
|
||||||
|
ssh_con.session = libssh2_session_init();
|
||||||
|
if (ssh_con.session == 0)
|
||||||
|
{
|
||||||
|
printf_i("Failed to initialize SSH session.\r\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
YieldToAnyThread();
|
||||||
|
|
||||||
|
// register callbacks
|
||||||
|
libssh2_session_callback_set(ssh_con.session, LIBSSH2_CALLBACK_SEND, network_send_callback);
|
||||||
|
libssh2_session_callback_set(ssh_con.session, LIBSSH2_CALLBACK_RECV, network_recv_callback);
|
||||||
|
libssh2_session_callback_set(ssh_con.session, LIBSSH2_CALLBACK_DISCONNECT, network_recv_callback);
|
||||||
|
|
||||||
|
long s = TickCount();
|
||||||
|
printf_i("Beginning SSH session handshake... "); YieldToAnyThread();
|
||||||
|
SSH_CHECK(libssh2_session_handshake(ssh_con.session, 0));
|
||||||
|
|
||||||
|
printf_i("done. (%d ticks)\r\n", TickCount() - s); YieldToAnyThread();
|
||||||
|
|
||||||
|
//const char* banner = libssh2_session_banner_get(ssh_con.session);
|
||||||
|
//if (banner) printf_i("Server banner: %s\r\n", banner);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void* read_thread(void* arg)
|
||||||
|
{
|
||||||
|
int ok = 1;
|
||||||
|
int rc = LIBSSH2_ERROR_NONE;
|
||||||
|
|
||||||
|
// yield until we're given a command
|
||||||
|
while (read_thread_command == WAIT) YieldToAnyThread();
|
||||||
|
|
||||||
|
if (read_thread_command == EXIT)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// connect
|
||||||
|
ok = init_connection(prefs.hostname+1);
|
||||||
|
YieldToAnyThread();
|
||||||
|
|
||||||
|
// check the server pub key vs. known hosts
|
||||||
|
if (ok)
|
||||||
|
{
|
||||||
|
ok = known_hosts();
|
||||||
|
if (!ok) printf_i("Rejected server public key!\r\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// actually log in
|
||||||
|
if (ok)
|
||||||
|
{
|
||||||
|
printf_i("Authenticating... "); YieldToAnyThread();
|
||||||
|
|
||||||
|
if (prefs.auth_type == USE_PASSWORD)
|
||||||
|
{
|
||||||
|
rc = libssh2_userauth_password(ssh_con.session, prefs.username+1, prefs.password+1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
rc = libssh2_userauth_publickey_fromfile_ex(ssh_con.session,
|
||||||
|
prefs.username+1,
|
||||||
|
prefs.username[0],
|
||||||
|
prefs.pubkey_path,
|
||||||
|
prefs.privkey_path,
|
||||||
|
prefs.password+1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rc == LIBSSH2_ERROR_NONE)
|
||||||
|
{
|
||||||
|
printf_i("done.\r\n");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
printf_i("failed!\r\n");
|
||||||
|
if (rc == LIBSSH2_ERROR_AUTHENTICATION_FAILED && prefs.auth_type == USE_PASSWORD) StopAlert(ALRT_PW_FAIL, nil);
|
||||||
|
else if (rc == LIBSSH2_ERROR_FILE) StopAlert(ALRT_FILE_FAIL, nil);
|
||||||
|
else if (rc == LIBSSH2_ERROR_PUBLICKEY_UNVERIFIED)
|
||||||
|
{
|
||||||
|
printf_i("Username/public key combination invalid!\r\n"); // TODO: have an alert for this
|
||||||
|
if (prefs.pubkey_path[0] != '\0' || prefs.privkey_path[0] != '\0')
|
||||||
|
{
|
||||||
|
prefs.pubkey_path[0] = '\0';
|
||||||
|
prefs.privkey_path[0] = '\0';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else printf_i("unexpected failure: %s\r\n", libssh2_error_string(rc));
|
||||||
|
ok = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
save_prefs();
|
||||||
|
|
||||||
|
// if we logged in, open and set up the tty
|
||||||
|
if (ok)
|
||||||
|
{
|
||||||
|
libssh2_channel_handle_extended_data2(ssh_con.channel, LIBSSH2_CHANNEL_EXTENDED_DATA_MERGE);
|
||||||
|
ssh_con.channel = libssh2_channel_open_session(ssh_con.session);
|
||||||
|
ok = ssh_setup_terminal();
|
||||||
|
YieldToAnyThread();
|
||||||
|
}
|
||||||
|
|
||||||
|
// if we failed, close everything and exit
|
||||||
|
if (!ok || (read_thread_state != OPEN))
|
||||||
|
{
|
||||||
|
end_connection();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if we connected, allow pasting
|
||||||
|
void* menu = GetMenuHandle(MENU_EDIT);
|
||||||
|
EnableItem(menu, 5);
|
||||||
|
|
||||||
|
// read until failure, command to EXIT, or remote EOF
|
||||||
|
while (read_thread_command == READ && read_thread_state == OPEN && libssh2_channel_eof(ssh_con.channel) == 0)
|
||||||
|
{
|
||||||
|
if (check_network_events()) ssh_read();
|
||||||
|
YieldToAnyThread();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (libssh2_channel_eof(ssh_con.channel))
|
||||||
|
{
|
||||||
|
printf_i("(disconnected by server)");
|
||||||
|
}
|
||||||
|
|
||||||
|
// if we still have a connection, close it
|
||||||
|
if (read_thread_state != DONE) end_connection();
|
||||||
|
|
||||||
|
// disallow pasting after connection is closed
|
||||||
|
DisableItem(menu, 5);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
/*
|
||||||
|
* ssheven
|
||||||
|
*
|
||||||
|
* Copyright (c) 2021 by cy384 <cy384@cy384.com>
|
||||||
|
* See LICENSE file for details
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
void ssh_write(char* buf, size_t len);
|
||||||
|
void* read_thread(void* arg);
|
661
ssheven.c
661
ssheven.c
|
@ -7,27 +7,45 @@
|
||||||
|
|
||||||
#include "ssheven.h"
|
#include "ssheven.h"
|
||||||
#include "ssheven-console.h"
|
#include "ssheven-console.h"
|
||||||
|
#include "ssheven-net.h"
|
||||||
|
#include "ssheven-debug.h"
|
||||||
|
|
||||||
// functions to convert error and status codes to strings
|
#include <Threads.h>
|
||||||
#include "ssheven-debug.c"
|
#include <MacMemory.h>
|
||||||
|
#include <Quickdraw.h>
|
||||||
|
#include <Fonts.h>
|
||||||
|
#include <Windows.h>
|
||||||
|
#include <Sound.h>
|
||||||
|
#include <Gestalt.h>
|
||||||
|
#include <Devices.h>
|
||||||
|
#include <Scrap.h>
|
||||||
|
#include <Controls.h>
|
||||||
|
#include <ControlDefinitions.h>
|
||||||
|
|
||||||
// error checking convenience macros
|
#include <stdio.h>
|
||||||
#define OT_CHECK(X) err = (X); if (err != noErr) { printf_i("" #X " failed\r\n"); return 0; };
|
|
||||||
#define SSH_CHECK(X) rc = (X); if (rc != LIBSSH2_ERROR_NONE) { printf_i("" #X " failed: %s\r\n", libssh2_error_string(rc)); return 0;};
|
|
||||||
|
|
||||||
// sinful globals
|
// sinful globals
|
||||||
struct ssheven_console con = { NULL, 0, 0, 0, 0, 0, 0, 0, 0, 1, NULL, NULL };
|
struct ssheven_console con = { NULL, 0, 0, 0, 0, 0, 0, 0, 0, 1, NULL, NULL };
|
||||||
struct ssheven_ssh_connection ssh_con = { NULL, NULL, kOTInvalidEndpointRef, NULL, NULL };
|
struct ssheven_ssh_connection ssh_con = { NULL, NULL, kOTInvalidEndpointRef, NULL, NULL };
|
||||||
struct preferences prefs;
|
struct preferences prefs;
|
||||||
|
|
||||||
enum { WAIT, READ, EXIT } read_thread_command = WAIT;
|
enum THREAD_COMMAND read_thread_command = WAIT;
|
||||||
enum { UNINTIALIZED, OPEN, CLEANUP, DONE } read_thread_state = UNINTIALIZED;
|
enum THREAD_STATE read_thread_state = UNINTIALIZED;
|
||||||
|
|
||||||
// TODO: put this somewhere else
|
|
||||||
// contains a mapping of apple virtual keycode to ascii control code
|
// contains a mapping of apple virtual keycode to ascii control code
|
||||||
// currently only for A-Z
|
// currently only for A-Z
|
||||||
|
// probably only works for US english keyboard layouts?
|
||||||
const uint8_t virtual_keycode_to_control_ascii[255] = {1, 19, 4, 6, 8, 7, 26, 24, 3, 22, 255, 2, 17, 23, 5, 18, 25, 20, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 15, 21, 255, 9, 16, 255, 12, 10, 255, 11, 255, 255, 255, 255, 14, 13, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255};
|
const uint8_t virtual_keycode_to_control_ascii[255] = {1, 19, 4, 6, 8, 7, 26, 24, 3, 22, 255, 2, 17, 23, 5, 18, 25, 20, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 15, 21, 255, 9, 16, 255, 12, 10, 255, 11, 255, 255, 255, 255, 14, 13, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255};
|
||||||
|
|
||||||
|
void set_window_title(WindowPtr w, const char* c_name)
|
||||||
|
{
|
||||||
|
Str255 pascal_name;
|
||||||
|
strncpy((char *) &pascal_name[1], c_name, 254);
|
||||||
|
pascal_name[0] = strlen(c_name);
|
||||||
|
|
||||||
|
SetWTitle(w, pascal_name);
|
||||||
|
}
|
||||||
|
|
||||||
void set_terminal_string(void)
|
void set_terminal_string(void)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
|
@ -61,7 +79,6 @@ int save_prefs(void)
|
||||||
short foundVRefNum = 0;
|
short foundVRefNum = 0;
|
||||||
long foundDirID = 0;
|
long foundDirID = 0;
|
||||||
FSSpec pref_file;
|
FSSpec pref_file;
|
||||||
//int create_new = 0;
|
|
||||||
short prefRefNum = 0;
|
short prefRefNum = 0;
|
||||||
|
|
||||||
OSType pref_type = 'SH7p';
|
OSType pref_type = 'SH7p';
|
||||||
|
@ -245,136 +262,6 @@ pascal void ButtonFrameProc(DialogRef dlg, DialogItemIndex itemNo)
|
||||||
FrameRoundRect(&box, 16, 16);
|
FrameRoundRect(&box, 16, 16);
|
||||||
}
|
}
|
||||||
|
|
||||||
// read from the channel and print to console
|
|
||||||
void ssh_read(void)
|
|
||||||
{
|
|
||||||
ssize_t rc = libssh2_channel_read(ssh_con.channel, ssh_con.recv_buffer, SSHEVEN_BUFFER_SIZE);
|
|
||||||
|
|
||||||
if (rc == 0) return;
|
|
||||||
|
|
||||||
if (rc <= 0)
|
|
||||||
{
|
|
||||||
printf_i("channel read error: %s\r\n", libssh2_error_string(rc));
|
|
||||||
read_thread_command = EXIT;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (rc > 0)
|
|
||||||
{
|
|
||||||
rc -= vterm_input_write(con.vterm, ssh_con.recv_buffer, rc);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void end_connection(void)
|
|
||||||
{
|
|
||||||
read_thread_state = CLEANUP;
|
|
||||||
|
|
||||||
OSStatus err = noErr;
|
|
||||||
|
|
||||||
if (ssh_con.channel)
|
|
||||||
{
|
|
||||||
libssh2_channel_send_eof(ssh_con.channel);
|
|
||||||
libssh2_channel_close(ssh_con.channel);
|
|
||||||
libssh2_channel_free(ssh_con.channel);
|
|
||||||
libssh2_session_disconnect(ssh_con.session, "Normal Shutdown, Thank you for playing");
|
|
||||||
libssh2_session_free(ssh_con.session);
|
|
||||||
}
|
|
||||||
|
|
||||||
libssh2_exit();
|
|
||||||
|
|
||||||
if (ssh_con.endpoint != kOTInvalidEndpointRef)
|
|
||||||
{
|
|
||||||
// request to close the TCP connection
|
|
||||||
OTSndOrderlyDisconnect(ssh_con.endpoint);
|
|
||||||
|
|
||||||
// discard remaining data so we can finish closing the connection
|
|
||||||
int rc = 1;
|
|
||||||
OTFlags ot_flags;
|
|
||||||
while (rc != kOTLookErr)
|
|
||||||
{
|
|
||||||
rc = OTRcv(ssh_con.endpoint, ssh_con.recv_buffer, 1, &ot_flags);
|
|
||||||
}
|
|
||||||
|
|
||||||
// finish closing the TCP connection
|
|
||||||
OSStatus result = OTLook(ssh_con.endpoint);
|
|
||||||
|
|
||||||
switch (result)
|
|
||||||
{
|
|
||||||
case T_DISCONNECT:
|
|
||||||
OTRcvDisconnect(ssh_con.endpoint, nil);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case T_ORDREL:
|
|
||||||
err = OTRcvOrderlyDisconnect(ssh_con.endpoint);
|
|
||||||
if (err == noErr)
|
|
||||||
{
|
|
||||||
err = OTSndOrderlyDisconnect(ssh_con.endpoint);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
printf_i("Unexpected OTLook result while closing: %s\r\n", OT_event_string(result));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// release endpoint
|
|
||||||
err = OTUnbind(ssh_con.endpoint);
|
|
||||||
if (err != noErr) printf_i("OTUnbind failed.\r\n");
|
|
||||||
|
|
||||||
err = OTCloseProvider(ssh_con.endpoint);
|
|
||||||
if (err != noErr) printf_i("OTCloseProvider failed.\r\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
read_thread_state = DONE;
|
|
||||||
}
|
|
||||||
|
|
||||||
int check_network_events(void)
|
|
||||||
{
|
|
||||||
int ok = 1;
|
|
||||||
OSStatus err = noErr;
|
|
||||||
|
|
||||||
// check if we have any new network events
|
|
||||||
OTResult look_result = OTLook(ssh_con.endpoint);
|
|
||||||
|
|
||||||
switch (look_result)
|
|
||||||
{
|
|
||||||
case T_DATA:
|
|
||||||
case T_EXDATA:
|
|
||||||
// got data
|
|
||||||
// we always try to read, so ignore this event
|
|
||||||
break;
|
|
||||||
|
|
||||||
case T_RESET:
|
|
||||||
// connection reset? close it/give up
|
|
||||||
end_connection();
|
|
||||||
ok = 0;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case T_DISCONNECT:
|
|
||||||
// got disconnected
|
|
||||||
OTRcvDisconnect(ssh_con.endpoint, nil);
|
|
||||||
end_connection();
|
|
||||||
ok = 0;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case T_ORDREL:
|
|
||||||
// nice tcp disconnect requested by remote
|
|
||||||
err = OTRcvOrderlyDisconnect(ssh_con.endpoint);
|
|
||||||
if (err == noErr)
|
|
||||||
{
|
|
||||||
err = OTSndOrderlyDisconnect(ssh_con.endpoint);
|
|
||||||
}
|
|
||||||
end_connection();
|
|
||||||
ok = 0;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
// something weird or irrelevant: ignore it
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ok;
|
|
||||||
}
|
|
||||||
|
|
||||||
void display_about_box(void)
|
void display_about_box(void)
|
||||||
{
|
{
|
||||||
DialogRef about = GetNewDialog(DLOG_ABOUT, 0, (WindowPtr)-1);
|
DialogRef about = GetNewDialog(DLOG_ABOUT, 0, (WindowPtr)-1);
|
||||||
|
@ -388,20 +275,6 @@ void display_about_box(void)
|
||||||
DisposeWindow(about);
|
DisposeWindow(about);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ssh_write(char* buf, size_t len)
|
|
||||||
{
|
|
||||||
if (read_thread_state == OPEN && read_thread_command != EXIT)
|
|
||||||
{
|
|
||||||
int r = libssh2_channel_write(ssh_con.channel, buf, len);
|
|
||||||
|
|
||||||
if (r < 1)
|
|
||||||
{
|
|
||||||
printf_i("Failed to write to channel, closing connection.\r\n");
|
|
||||||
read_thread_command = EXIT;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ssh_paste(void)
|
void ssh_paste(void)
|
||||||
{
|
{
|
||||||
// GetScrap requires a handle, not a raw buffer
|
// GetScrap requires a handle, not a raw buffer
|
||||||
|
@ -751,151 +624,6 @@ void event_loop(void)
|
||||||
} while (!exit_event_loop);
|
} while (!exit_event_loop);
|
||||||
}
|
}
|
||||||
|
|
||||||
#include <errno.h>
|
|
||||||
ssize_t network_recv_callback(libssh2_socket_t sock, void *buffer,
|
|
||||||
size_t length, int flags, void **abstract)
|
|
||||||
{
|
|
||||||
OTResult ret = kOTNoDataErr;
|
|
||||||
OTFlags ot_flags = 0;
|
|
||||||
|
|
||||||
if (length == 0) return 0;
|
|
||||||
|
|
||||||
// in non-blocking mode, returns instantly always
|
|
||||||
ret = OTRcv(ssh_con.endpoint, buffer, length, &ot_flags);
|
|
||||||
|
|
||||||
// if we got bytes, return them
|
|
||||||
if (ret >= 0) return ret;
|
|
||||||
|
|
||||||
// if no data, let other threads run, then tell caller to call again
|
|
||||||
if (ret == kOTNoDataErr && read_thread_command != EXIT)
|
|
||||||
{
|
|
||||||
YieldToAnyThread();
|
|
||||||
return -EAGAIN;
|
|
||||||
}
|
|
||||||
|
|
||||||
// if we got anything other than data or nothing, return an error
|
|
||||||
if (ret != kOTNoDataErr) return -1;
|
|
||||||
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
ssize_t network_send_callback(libssh2_socket_t sock, const void *buffer,
|
|
||||||
size_t length, int flags, void **abstract)
|
|
||||||
{
|
|
||||||
int ret = -1;
|
|
||||||
|
|
||||||
ret = OTSnd(ssh_con.endpoint, (void*) buffer, length, 0);
|
|
||||||
|
|
||||||
// TODO FIXME handle cases better, i.e. translate error cases
|
|
||||||
if (ret == kOTLookErr)
|
|
||||||
{
|
|
||||||
OTResult lookresult = OTLook(ssh_con.endpoint);
|
|
||||||
//printf("kOTLookErr, reason: %ld\n", lookresult);
|
|
||||||
|
|
||||||
switch (lookresult)
|
|
||||||
{
|
|
||||||
default:
|
|
||||||
//printf("what?\n");
|
|
||||||
ret = -1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (ssize_t) ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ssh_end_msg_callback(LIBSSH2_SESSION* session, int reason, const char *message,
|
|
||||||
int message_len, const char *language, int language_len,
|
|
||||||
void **abstract)
|
|
||||||
{
|
|
||||||
printf_i("got a disconnect msg\r\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
int init_connection(char* hostname)
|
|
||||||
{
|
|
||||||
int rc;
|
|
||||||
|
|
||||||
// OT vars
|
|
||||||
OSStatus err = noErr;
|
|
||||||
TCall sndCall;
|
|
||||||
DNSAddress hostDNSAddress;
|
|
||||||
|
|
||||||
printf_i("Opening and configuring endpoint... "); YieldToAnyThread();
|
|
||||||
|
|
||||||
// open TCP endpoint
|
|
||||||
ssh_con.endpoint = OTOpenEndpoint(OTCreateConfiguration(kTCPName), 0, nil, &err);
|
|
||||||
|
|
||||||
if (err != noErr || ssh_con.endpoint == kOTInvalidEndpointRef)
|
|
||||||
{
|
|
||||||
printf_i("failed to open Open Transport TCP endpoint.\r\n");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
OT_CHECK(OTSetSynchronous(ssh_con.endpoint));
|
|
||||||
OT_CHECK(OTSetBlocking(ssh_con.endpoint));
|
|
||||||
OT_CHECK(OTUseSyncIdleEvents(ssh_con.endpoint, false));
|
|
||||||
|
|
||||||
OT_CHECK(OTBind(ssh_con.endpoint, nil, nil));
|
|
||||||
|
|
||||||
OT_CHECK(OTSetNonBlocking(ssh_con.endpoint));
|
|
||||||
|
|
||||||
printf_i("done.\r\n"); YieldToAnyThread();
|
|
||||||
|
|
||||||
// set up address struct, do the DNS lookup, and connect
|
|
||||||
OTMemzero(&sndCall, sizeof(TCall));
|
|
||||||
|
|
||||||
sndCall.addr.buf = (UInt8 *) &hostDNSAddress;
|
|
||||||
sndCall.addr.len = OTInitDNSAddress(&hostDNSAddress, (char *) hostname);
|
|
||||||
|
|
||||||
printf_i("Connecting endpoint... "); YieldToAnyThread();
|
|
||||||
OT_CHECK(OTConnect(ssh_con.endpoint, &sndCall, nil));
|
|
||||||
|
|
||||||
printf_i("done.\r\n"); YieldToAnyThread();
|
|
||||||
|
|
||||||
printf_i("Initializing SSH... "); YieldToAnyThread();
|
|
||||||
// init libssh2
|
|
||||||
SSH_CHECK(libssh2_init(0));
|
|
||||||
|
|
||||||
printf_i("done.\r\n"); YieldToAnyThread();
|
|
||||||
|
|
||||||
printf_i("Opening SSH session... "); YieldToAnyThread();
|
|
||||||
ssh_con.session = libssh2_session_init();
|
|
||||||
if (ssh_con.session == 0)
|
|
||||||
{
|
|
||||||
printf_i("failed to initialize SSH session.\r\n");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
printf_i("done.\r\n"); YieldToAnyThread();
|
|
||||||
|
|
||||||
// register callbacks
|
|
||||||
libssh2_session_callback_set(ssh_con.session, LIBSSH2_CALLBACK_SEND, network_send_callback);
|
|
||||||
libssh2_session_callback_set(ssh_con.session, LIBSSH2_CALLBACK_RECV, network_recv_callback);
|
|
||||||
libssh2_session_callback_set(ssh_con.session, LIBSSH2_CALLBACK_DISCONNECT, network_recv_callback);
|
|
||||||
|
|
||||||
long s = TickCount();
|
|
||||||
printf_i("Beginning SSH session handshake... "); YieldToAnyThread();
|
|
||||||
SSH_CHECK(libssh2_session_handshake(ssh_con.session, 0));
|
|
||||||
|
|
||||||
printf_i("done. (%d ticks)\r\n", TickCount() - s); YieldToAnyThread();
|
|
||||||
|
|
||||||
const char* banner = libssh2_session_banner_get(ssh_con.session);
|
|
||||||
if (banner) printf_i("Server banner: %s\r\n", banner);
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int ssh_setup_terminal(void)
|
|
||||||
{
|
|
||||||
int rc = 0;
|
|
||||||
|
|
||||||
SSH_CHECK(libssh2_channel_request_pty_ex(ssh_con.channel, prefs.terminal_string, (strlen(prefs.terminal_string)), NULL, 0, con.size_x, con.size_y, 0, 0));
|
|
||||||
SSH_CHECK(libssh2_channel_shell(ssh_con.channel));
|
|
||||||
|
|
||||||
read_thread_state = OPEN;
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// from the ATS password sample code
|
// from the ATS password sample code
|
||||||
pascal Boolean TwoItemFilter(DialogPtr dlog, EventRecord *event, short *itemHit)
|
pascal Boolean TwoItemFilter(DialogPtr dlog, EventRecord *event, short *itemHit)
|
||||||
{
|
{
|
||||||
|
@ -970,8 +698,7 @@ pascal Boolean TwoItemFilter(DialogPtr dlog, EventRecord *event, short *itemHit)
|
||||||
int password_dialog(int dialog_resource)
|
int password_dialog(int dialog_resource)
|
||||||
{
|
{
|
||||||
int ret = 1;
|
int ret = 1;
|
||||||
// n.b. dialog strings can't be longer than this, so no overflow risk
|
|
||||||
//static char password[256];
|
|
||||||
DialogPtr dlog;
|
DialogPtr dlog;
|
||||||
Handle itemH;
|
Handle itemH;
|
||||||
short item;
|
short item;
|
||||||
|
@ -1251,325 +978,6 @@ int intro_dialog(void)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// returns base64 sha256 hash of key as a malloc'd pascal string
|
|
||||||
char* host_hash(void)
|
|
||||||
{
|
|
||||||
size_t length = 0;
|
|
||||||
char* human_readable = malloc(66);
|
|
||||||
memset(human_readable, 0, 66);
|
|
||||||
const char* host_key_hash = NULL;
|
|
||||||
|
|
||||||
host_key_hash = libssh2_hostkey_hash(ssh_con.session, LIBSSH2_HOSTKEY_HASH_SHA256);
|
|
||||||
mbedtls_base64_encode((unsigned char*)human_readable+1, 64, &length, (unsigned const char*)host_key_hash, 32);
|
|
||||||
|
|
||||||
human_readable[0] = (unsigned char)length;
|
|
||||||
|
|
||||||
return human_readable;
|
|
||||||
}
|
|
||||||
|
|
||||||
char* known_hosts_full_path(int* found)
|
|
||||||
{
|
|
||||||
int ok = 1;
|
|
||||||
short foundVRefNum = 0;
|
|
||||||
long foundDirID = 0;
|
|
||||||
FSSpec known_hosts_file;
|
|
||||||
*found = 0;
|
|
||||||
|
|
||||||
OSType pref_type = 'SH7p';
|
|
||||||
OSType creator_type = 'SSH7';
|
|
||||||
|
|
||||||
// find the preferences folder on the system disk, create folder if needed
|
|
||||||
OSErr e = FindFolder(kOnSystemDisk, kPreferencesFolderType, kCreateFolder, &foundVRefNum, &foundDirID);
|
|
||||||
if (e != noErr) ok = 0;
|
|
||||||
|
|
||||||
// make an FSSpec for the new file we want to make
|
|
||||||
if (ok)
|
|
||||||
{
|
|
||||||
e = FSMakeFSSpec(foundVRefNum, foundDirID, "\pknown_hosts", &known_hosts_file);
|
|
||||||
|
|
||||||
// if the file exists, we found it else make an empty one
|
|
||||||
if (e == noErr) *found = 1;
|
|
||||||
else if (e == noErr) e = FSpCreate(&known_hosts_file, creator_type, pref_type, smSystemScript);
|
|
||||||
|
|
||||||
if (e != noErr) ok = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
Handle full_path_handle;
|
|
||||||
int path_length;
|
|
||||||
|
|
||||||
FSpPathFromLocation(&known_hosts_file, &path_length, &full_path_handle);
|
|
||||||
char* full_path = malloc(path_length+1);
|
|
||||||
strncpy(full_path, (char*)(*full_path_handle), path_length+1);
|
|
||||||
DisposeHandle(full_path_handle);
|
|
||||||
|
|
||||||
return full_path;
|
|
||||||
}
|
|
||||||
|
|
||||||
int known_hosts(void)
|
|
||||||
{
|
|
||||||
int safe_to_connect = 1;
|
|
||||||
int recognized_key = 0;
|
|
||||||
int known_hosts_file_exists = 0;
|
|
||||||
|
|
||||||
char* known_hosts_file_path = known_hosts_full_path(&known_hosts_file_exists);
|
|
||||||
char* hash_string = NULL;
|
|
||||||
|
|
||||||
size_t key_len = 0;
|
|
||||||
int key_type = 0;
|
|
||||||
const char* host_key = libssh2_session_hostkey(ssh_con.session, &key_len, &key_type);
|
|
||||||
|
|
||||||
LIBSSH2_KNOWNHOSTS* known_hosts = libssh2_knownhost_init(ssh_con.session);
|
|
||||||
|
|
||||||
if (known_hosts_file_exists)
|
|
||||||
{
|
|
||||||
// load known hosts file
|
|
||||||
printf_i("Loading known hosts... ");
|
|
||||||
|
|
||||||
int e = libssh2_knownhost_readfile(known_hosts, known_hosts_file_path, LIBSSH2_KNOWNHOST_FILE_OPENSSH);
|
|
||||||
if (e >= 0)
|
|
||||||
{
|
|
||||||
printf_i("got %d.\r\n", e);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
printf_i("failed: %s\r\n", libssh2_error_string(e));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
printf_i("No known hosts file found.\r\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (safe_to_connect)
|
|
||||||
{
|
|
||||||
// hostnames need to be either plain or of the format "[host]:port"
|
|
||||||
prefs.hostname[prefs.hostname[0]+1] = '\0';
|
|
||||||
int e = libssh2_knownhost_check(known_hosts, prefs.hostname+1, host_key, key_len, LIBSSH2_KNOWNHOST_TYPE_PLAIN | LIBSSH2_KNOWNHOST_KEYENC_RAW, NULL);
|
|
||||||
prefs.hostname[prefs.hostname[0]+1] = ':';
|
|
||||||
|
|
||||||
switch (e)
|
|
||||||
{
|
|
||||||
case LIBSSH2_KNOWNHOST_CHECK_FAILURE:
|
|
||||||
printf_i("Failed to check known hosts against server public key!\r\n");
|
|
||||||
safe_to_connect = 0;
|
|
||||||
break;
|
|
||||||
case LIBSSH2_KNOWNHOST_CHECK_NOTFOUND:
|
|
||||||
printf_i("No matching host found.\r\n");
|
|
||||||
break;
|
|
||||||
case LIBSSH2_KNOWNHOST_CHECK_MATCH:
|
|
||||||
printf_i("Host and key match found in known hosts.\r\n");
|
|
||||||
recognized_key = 1;
|
|
||||||
break;
|
|
||||||
case LIBSSH2_KNOWNHOST_CHECK_MISMATCH:
|
|
||||||
printf_i("Host found in known hosts but key doesn't match!\r\n");
|
|
||||||
safe_to_connect = 0;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
printf_i("Unexpected error while checking known-hosts: %d\r\n", e);
|
|
||||||
safe_to_connect = 0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
hash_string = host_hash();
|
|
||||||
//printf_i("Host key hash (SHA256): %s\r\n", hash_string+1);
|
|
||||||
|
|
||||||
// ask the user to confirm if we're seeing a new host+key combo
|
|
||||||
if (safe_to_connect && !recognized_key)
|
|
||||||
{
|
|
||||||
// ask the user if the key is OK
|
|
||||||
DialogPtr dlg = GetNewDialog(DLOG_NEW_HOST, 0, (WindowPtr)-1);
|
|
||||||
|
|
||||||
DialogItemType type;
|
|
||||||
Handle itemH;
|
|
||||||
Rect box;
|
|
||||||
|
|
||||||
// draw default button indicator around the connect button
|
|
||||||
GetDialogItem(dlg, 2, &type, &itemH, &box);
|
|
||||||
SetDialogItem(dlg, 2, type, (Handle)NewUserItemUPP(&ButtonFrameProc), &box);
|
|
||||||
|
|
||||||
// write the hash string into the dialog window
|
|
||||||
ControlHandle hash_text_box;
|
|
||||||
GetDialogItem(dlg, 4, &type, &itemH, &box);
|
|
||||||
hash_text_box = (ControlHandle)itemH;
|
|
||||||
SetDialogItemText((Handle)hash_text_box, (ConstStr255Param)hash_string);
|
|
||||||
|
|
||||||
// let the modalmanager do everything
|
|
||||||
// stop on reject or accept
|
|
||||||
short item;
|
|
||||||
do {
|
|
||||||
ModalDialog(NULL, &item);
|
|
||||||
} while(item != 1 && item != 5);
|
|
||||||
|
|
||||||
// reject if user hit reject
|
|
||||||
if (item == 1)
|
|
||||||
{
|
|
||||||
safe_to_connect = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
DisposeDialog(dlg);
|
|
||||||
FlushEvents(everyEvent, -1);
|
|
||||||
|
|
||||||
printf_i("Saving host and key... ");
|
|
||||||
|
|
||||||
int save_type = 0;
|
|
||||||
save_type |= LIBSSH2_KNOWNHOST_TYPE_PLAIN;
|
|
||||||
save_type |= LIBSSH2_KNOWNHOST_KEYENC_RAW;
|
|
||||||
|
|
||||||
switch (key_type)
|
|
||||||
{
|
|
||||||
default:
|
|
||||||
__attribute__ ((fallthrough));
|
|
||||||
case LIBSSH2_HOSTKEY_TYPE_UNKNOWN:
|
|
||||||
save_type |= LIBSSH2_KNOWNHOST_KEY_UNKNOWN;
|
|
||||||
break;
|
|
||||||
case LIBSSH2_HOSTKEY_TYPE_RSA:
|
|
||||||
save_type |= LIBSSH2_KNOWNHOST_KEY_SSHRSA;
|
|
||||||
break;
|
|
||||||
case LIBSSH2_HOSTKEY_TYPE_DSS:
|
|
||||||
save_type |= LIBSSH2_KNOWNHOST_KEY_SSHDSS;
|
|
||||||
break;
|
|
||||||
case LIBSSH2_HOSTKEY_TYPE_ECDSA_256:
|
|
||||||
save_type |= LIBSSH2_KNOWNHOST_KEY_ECDSA_256;
|
|
||||||
break;
|
|
||||||
case LIBSSH2_HOSTKEY_TYPE_ECDSA_384:
|
|
||||||
save_type |= LIBSSH2_KNOWNHOST_KEY_ECDSA_384;
|
|
||||||
break;
|
|
||||||
case LIBSSH2_HOSTKEY_TYPE_ECDSA_521:
|
|
||||||
save_type |= LIBSSH2_KNOWNHOST_KEY_ECDSA_521;
|
|
||||||
break;
|
|
||||||
case LIBSSH2_HOSTKEY_TYPE_ED25519:
|
|
||||||
save_type |= LIBSSH2_KNOWNHOST_KEY_ED25519;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// hostnames need to be either plain or of the format "[host]:port"
|
|
||||||
prefs.hostname[prefs.hostname[0]+1] = '\0';
|
|
||||||
int e = libssh2_knownhost_addc(known_hosts, prefs.hostname+1, NULL, host_key, key_len, NULL, 0, save_type, NULL);
|
|
||||||
prefs.hostname[prefs.hostname[0]+1] = ':';
|
|
||||||
|
|
||||||
if (e != 0) printf_i("failed to add to known hosts: %s\r\n", libssh2_error_string(e));
|
|
||||||
else
|
|
||||||
{
|
|
||||||
e = libssh2_knownhost_writefile(known_hosts, known_hosts_file_path, LIBSSH2_KNOWNHOST_FILE_OPENSSH);
|
|
||||||
if (e != 0) printf_i("failed to save known hosts file: %s\r\n", libssh2_error_string(e));
|
|
||||||
else printf_i("done.\r\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
free(hash_string);
|
|
||||||
|
|
||||||
libssh2_knownhost_free(known_hosts);
|
|
||||||
|
|
||||||
free(known_hosts_file_path);
|
|
||||||
|
|
||||||
return safe_to_connect;
|
|
||||||
}
|
|
||||||
|
|
||||||
void* read_thread(void* arg)
|
|
||||||
{
|
|
||||||
int ok = 1;
|
|
||||||
int rc = LIBSSH2_ERROR_NONE;
|
|
||||||
|
|
||||||
// yield until we're given a command
|
|
||||||
while (read_thread_command == WAIT) YieldToAnyThread();
|
|
||||||
|
|
||||||
if (read_thread_command == EXIT)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// connect
|
|
||||||
ok = init_connection(prefs.hostname+1);
|
|
||||||
YieldToAnyThread();
|
|
||||||
|
|
||||||
// check the server pub key vs. known hosts
|
|
||||||
if (ok)
|
|
||||||
{
|
|
||||||
ok = known_hosts();
|
|
||||||
if (!ok) printf_i("Rejected server public key!\r\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
// actually log in
|
|
||||||
if (ok)
|
|
||||||
{
|
|
||||||
printf_i("Authenticating... "); YieldToAnyThread();
|
|
||||||
|
|
||||||
if (prefs.auth_type == USE_PASSWORD)
|
|
||||||
{
|
|
||||||
rc = libssh2_userauth_password(ssh_con.session, prefs.username+1, prefs.password+1);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
rc = libssh2_userauth_publickey_fromfile_ex(ssh_con.session,
|
|
||||||
prefs.username+1,
|
|
||||||
prefs.username[0],
|
|
||||||
prefs.pubkey_path,
|
|
||||||
prefs.privkey_path,
|
|
||||||
prefs.password+1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rc == LIBSSH2_ERROR_NONE)
|
|
||||||
{
|
|
||||||
printf_i("done.\r\n");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
printf_i("failed!\r\n");
|
|
||||||
if (rc == LIBSSH2_ERROR_AUTHENTICATION_FAILED && prefs.auth_type == USE_PASSWORD) StopAlert(ALRT_PW_FAIL, nil);
|
|
||||||
else if (rc == LIBSSH2_ERROR_FILE) StopAlert(ALRT_FILE_FAIL, nil);
|
|
||||||
else if (rc == LIBSSH2_ERROR_PUBLICKEY_UNVERIFIED)
|
|
||||||
{
|
|
||||||
printf_i("Username/public key combination invalid!\r\n"); // TODO: have an alert for this
|
|
||||||
if (prefs.pubkey_path[0] != '\0' || prefs.privkey_path[0] != '\0')
|
|
||||||
{
|
|
||||||
prefs.pubkey_path[0] = '\0';
|
|
||||||
prefs.privkey_path[0] = '\0';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else printf_i("unexpected failure: %s\r\n", libssh2_error_string(rc));
|
|
||||||
ok = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
save_prefs();
|
|
||||||
|
|
||||||
// if we logged in, open and set up the tty
|
|
||||||
if (ok)
|
|
||||||
{
|
|
||||||
libssh2_channel_handle_extended_data2(ssh_con.channel, LIBSSH2_CHANNEL_EXTENDED_DATA_MERGE);
|
|
||||||
ssh_con.channel = libssh2_channel_open_session(ssh_con.session);
|
|
||||||
ok = ssh_setup_terminal();
|
|
||||||
YieldToAnyThread();
|
|
||||||
}
|
|
||||||
|
|
||||||
// if we failed, close everything and exit
|
|
||||||
if (!ok || (read_thread_state != OPEN))
|
|
||||||
{
|
|
||||||
end_connection();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// if we connected, allow pasting
|
|
||||||
void* menu = GetMenuHandle(MENU_EDIT);
|
|
||||||
EnableItem(menu, 5);
|
|
||||||
|
|
||||||
// read until failure, command to EXIT, or remote EOF
|
|
||||||
while (read_thread_command == READ && read_thread_state == OPEN && libssh2_channel_eof(ssh_con.channel) == 0)
|
|
||||||
{
|
|
||||||
if (check_network_events()) ssh_read();
|
|
||||||
YieldToAnyThread();
|
|
||||||
}
|
|
||||||
|
|
||||||
// if we still have a connection, close it
|
|
||||||
if (read_thread_state != DONE) end_connection();
|
|
||||||
|
|
||||||
// disallow pasting after connection is closed
|
|
||||||
DisableItem(menu, 5);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int safety_checks(void)
|
int safety_checks(void)
|
||||||
{
|
{
|
||||||
OSStatus err = noErr;
|
OSStatus err = noErr;
|
||||||
|
@ -1623,12 +1031,12 @@ int safety_checks(void)
|
||||||
printf_i(" Attempting to continue anyway.\r\n");
|
printf_i(" Attempting to continue anyway.\r\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
NumVersion* ot_version = (NumVersion*) &open_transport_new_version;
|
/*NumVersion* ot_version = (NumVersion*) &open_transport_new_version;
|
||||||
|
|
||||||
printf_i("Detected Open Transport version: %d.%d.%d\r\n",
|
printf_i("Detected Open Transport version: %d.%d.%d\r\n",
|
||||||
(int)ot_version->majorRev,
|
(int)ot_version->majorRev,
|
||||||
(int)((ot_version->minorAndBugRev & 0xF0) >> 4),
|
(int)((ot_version->minorAndBugRev & 0xF0) >> 4),
|
||||||
(int)(ot_version->minorAndBugRev & 0x0F));
|
(int)(ot_version->minorAndBugRev & 0x0F));*/
|
||||||
|
|
||||||
// check for CPU type and display warning if it's going to be too slow
|
// check for CPU type and display warning if it's going to be too slow
|
||||||
long int cpu_type = 0;
|
long int cpu_type = 0;
|
||||||
|
@ -1729,22 +1137,13 @@ int main(int argc, char** argv)
|
||||||
printf_i("Running in 68k mode.\r\n");
|
printf_i("Running in 68k mode.\r\n");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (prefs.loaded_from_file)
|
|
||||||
{
|
|
||||||
printf_i("Loaded preferences file.\r\n");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
printf_i("Could not load from preferences file.\r\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
BeginUpdate(con.win);
|
BeginUpdate(con.win);
|
||||||
draw_screen(&(con.win->portRect));
|
draw_screen(&(con.win->portRect));
|
||||||
EndUpdate(con.win);
|
EndUpdate(con.win);
|
||||||
|
|
||||||
int ok = 1;
|
int ok = 1;
|
||||||
|
|
||||||
if (!safety_checks()) return 0;
|
ok = safety_checks();
|
||||||
|
|
||||||
BeginUpdate(con.win);
|
BeginUpdate(con.win);
|
||||||
draw_screen(&(con.win->portRect));
|
draw_screen(&(con.win->portRect));
|
||||||
|
|
42
ssheven.h
42
ssheven.h
|
@ -7,38 +7,17 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
// open transport
|
|
||||||
#include <OpenTransport.h>
|
#include <OpenTransport.h>
|
||||||
#include <OpenTptInternet.h>
|
#include <OpenTptInternet.h>
|
||||||
|
|
||||||
// mac os stuff
|
|
||||||
#include <Threads.h>
|
|
||||||
#include <MacMemory.h>
|
|
||||||
#include <Quickdraw.h>
|
|
||||||
#include <Fonts.h>
|
|
||||||
#include <Windows.h>
|
|
||||||
#include <Sound.h>
|
|
||||||
#include <Gestalt.h>
|
|
||||||
#include <Devices.h>
|
|
||||||
#include <Scrap.h>
|
|
||||||
#include <Controls.h>
|
|
||||||
#include <ControlDefinitions.h>
|
|
||||||
#include <StandardFile.h>
|
#include <StandardFile.h>
|
||||||
#include <Folders.h>
|
#include <Folders.h>
|
||||||
|
|
||||||
// libssh2
|
|
||||||
#include <libssh2.h>
|
#include <libssh2.h>
|
||||||
|
|
||||||
// ssheven constants
|
|
||||||
#include "ssheven-constants.r"
|
|
||||||
|
|
||||||
// libvterm
|
|
||||||
#include <vterm.h>
|
#include <vterm.h>
|
||||||
#include <vterm_keycodes.h>
|
#include <vterm_keycodes.h>
|
||||||
|
|
||||||
#include <stdio.h>
|
#include "ssheven-constants.r"
|
||||||
|
|
||||||
#include <mbedtls/base64.h>
|
|
||||||
|
|
||||||
// sinful globals
|
// sinful globals
|
||||||
struct ssheven_console
|
struct ssheven_console
|
||||||
|
@ -77,10 +56,6 @@ struct ssheven_ssh_connection
|
||||||
|
|
||||||
extern struct ssheven_ssh_connection ssh_con;
|
extern struct ssheven_ssh_connection ssh_con;
|
||||||
|
|
||||||
extern char key_to_vterm[256];
|
|
||||||
|
|
||||||
void ssh_write(char* buf, size_t len);
|
|
||||||
|
|
||||||
struct preferences
|
struct preferences
|
||||||
{
|
{
|
||||||
int major_version;
|
int major_version;
|
||||||
|
@ -110,3 +85,18 @@ struct preferences
|
||||||
};
|
};
|
||||||
|
|
||||||
extern struct preferences prefs;
|
extern struct preferences prefs;
|
||||||
|
|
||||||
|
extern char key_to_vterm[256];
|
||||||
|
|
||||||
|
enum THREAD_COMMAND { WAIT, READ, EXIT };
|
||||||
|
enum THREAD_STATE { UNINTIALIZED, OPEN, CLEANUP, DONE };
|
||||||
|
|
||||||
|
extern enum THREAD_COMMAND read_thread_command;
|
||||||
|
extern enum THREAD_STATE read_thread_state;
|
||||||
|
|
||||||
|
int save_prefs(void);
|
||||||
|
void set_window_title(WindowPtr w, const char* c_name);
|
||||||
|
|
||||||
|
OSErr FSpPathFromLocation(FSSpec* spec, int* length, Handle* fullPath);
|
||||||
|
|
||||||
|
pascal void ButtonFrameProc(DialogRef dlg, DialogItemIndex itemNo);
|
||||||
|
|
Loading…
Reference in New Issue