Advertisement
Extreemhost

libssh lua ffi binding

Jul 13th, 2015
405
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 9.06 KB | None | 0 0
  1. -- LuaJIT FFI wrapper over libssh (http://libssh.org/).
  2. -- It's licensed as LGPLv2.1 (see http://git.libssh.org/projects/libssh.git/tree/COPYING).
  3.  
  4. local ffi = require("ffi")
  5.  
  6. ffi.cdef[[
  7. typedef struct ssh_session_struct* ssh_session;
  8. typedef struct ssh_channel_struct* ssh_channel;
  9. ssh_session ssh_new(void);
  10. int ssh_options_set(ssh_session session, int type, const void* value);
  11. void ssh_free (ssh_session session);
  12. int ssh_connect (ssh_session session);
  13. void ssh_disconnect (ssh_session session);
  14. int ssh_is_server_known (ssh_session session);
  15. int ssh_is_connected (ssh_session session);
  16. int ssh_write_knownhost (ssh_session session);
  17. int ssh_userauth_password (ssh_session session, const char *username, const char *password);
  18. char* ssh_get_issue_banner(ssh_session session);
  19. ssh_channel ssh_channel_new(ssh_session session);
  20. int ssh_channel_open_session(ssh_channel channel);
  21. int ssh_channel_close(ssh_channel channel);
  22. void ssh_channel_free(ssh_channel channel);
  23. int ssh_channel_request_exec (ssh_channel channel, const char * cmd);
  24. int ssh_channel_read(ssh_channel channel, void *dest, uint32_t count, int is_stderr);
  25. int ssh_channel_read_nonblocking(ssh_channel channel, void *dest, uint32_t count, int is_stderr);
  26. int ssh_channel_send_eof(ssh_channel channel);
  27. const char* ssh_get_error (void * error);
  28. typedef struct ssh_private_key_struct* ssh_private_key;
  29. typedef struct ssh_public_key_struct* ssh_public_key;
  30. typedef struct ssh_key_struct* ssh_key;
  31. typedef struct ssh_string_struct* ssh_string;
  32. int ssh_userauth_autopubkey(ssh_session session, const char *passphrase);
  33. int ssh_userauth_pubkey(ssh_session session, const char *username, ssh_string publickey, ssh_private_key privatekey);
  34. int ssh_userauth_privatekey_file(ssh_session session, const char *username, const char *filename, const char *passphrase);
  35. const char *ssh_version(int req_version);
  36. ]]
  37.  
  38. -- seems like dll does not load
  39. local libssh = ffi.load("libssh.dll")
  40.  
  41. local low_version = 5 * 2^8 + 2
  42. assert(libssh.ssh_version(ffi.new("int", low_version)), "Your libssh is too old! At least 0.5.2 is required.")
  43.  
  44. local function int_to_num(i)
  45.    return ffi.new("int[1]", i)
  46. end
  47.  
  48. -- Debug stub.
  49. local function debug(s)
  50.    print (s)
  51. end
  52.  
  53. local c = {
  54.    -- enum ssh_auth_e
  55.    SSH_AUTH_SUCCESS = 0,
  56.    SSH_AUTH_DENIED = 1,
  57.    SSH_AUTH_PARTIAL = 2,
  58.    SSH_AUTH_INFO = 3,
  59.    SSH_AUTH_AGAIN = 4,
  60.    SSH_AUTH_ERROR = -1,
  61.  
  62.    -- enum ssh_server_known_e
  63.    SSH_SERVER_ERROR = -1,
  64.    SSH_SERVER_NOT_KNOWN = 0,
  65.    SSH_SERVER_KNOWN_OK = 1,
  66.    SSH_SERVER_KNOWN_CHANGED = 2,
  67.    SSH_SERVER_FOUND_OTHER = 3,
  68.    SSH_SERVER_FILE_NOT_FOUND = 4,
  69. }
  70.  
  71. local function check_auth_status(ssh_status)
  72.    if ssh_status == c.SSH_AUTH_SUCCESS then
  73.       return true, "ok"
  74.    elseif ssh_status == c.SSH_AUTH_PARTIAL then
  75.       return true, "partial"
  76.    else
  77.       -- local err = libssh.ssh_get_error(session)
  78.       -- debug (ffi.string(err))
  79.       return nil, string.format("Authentication failed with status %d.", ssh_status)
  80.    end
  81. end
  82.  
  83. -- Creates session.
  84. -- @return session libssh session
  85. local function new()
  86.    -- Initialize session
  87.    local session = libssh.ssh_new()
  88.    -- Check if everything is ok
  89.    if session == nil then
  90.       return nil, "Cannot create session"
  91.    end
  92.    return session
  93. end
  94.  
  95. -- Connects to the server.
  96. -- @param session
  97. -- @param strict_host_key_checking
  98. local function connect(session, strict_host_key_checking)
  99.    assert(session ~= nil, "Session shouldn't be nil")
  100.    -- Connecting to the server
  101.    if libssh.ssh_connect(session) ~= c.SSH_AUTH_SUCCESS then
  102.       return nil, "Unable to connect"
  103.    end
  104.    -- Verifying hosts file
  105.    known_status = libssh.ssh_is_server_known(session)
  106.    if known_status ~= c.SSH_SERVER_KNOWN_OK then
  107.       if (strict_host_key_checking) then
  108.          return nil, "Host is unknown"
  109.       elseif (known_status == c.SSH_SERVER_NOT_KNOWN or
  110.               known_status == c.SSH_SERVER_FILE_NOT_FOUND) then
  111.          debug("unknown host: adding...")
  112.          libssh.ssh_write_knownhost(session)
  113.       else
  114.          -- TODO: return proper human-readable explaination
  115.          return nil, "Failed checking known hosts!"
  116.       end
  117.    end
  118.    return true
  119. end
  120.  
  121.  
  122. -- Performs password authentication.
  123. -- @param session
  124. -- @param password
  125. local function auth_password(session, password)
  126.    assert(session ~= nil, "Session shouldn't be nil")
  127.    if (type(password) ~= "string") then
  128.       return nil, "Password should be string"
  129.    end
  130.    local auth_status = libssh.ssh_userauth_password(session, nil, password)
  131.    return check_auth_status(auth_status)
  132. end
  133.  
  134. -- Performs authentication by public keys in ~/.ssh directory.
  135. -- @param session
  136. -- @param password public key password
  137. local function auth_autopubkey(session, password)
  138.    assert(session ~= nil, "Session shouldn't be nil")
  139.    if (type(password) ~= "string" and password ~= nil) then
  140.       return nil, "Password should be string"
  141.    end
  142.    local auth_status = libssh.ssh_userauth_autopubkey(session, password)
  143.    return check_auth_status(auth_status)
  144. end
  145.  
  146. -- TODO: auth_publickey_file and auth_gssapi
  147.  
  148. -- Sets particular option.
  149. -- @param session libssh session
  150. -- @param option option to set
  151. -- @param value option value
  152. -- @return ok, reason
  153. local function set_option(session, option, value)
  154.    assert(session ~= nil, "Session shouldn't be nil")
  155.  
  156.  
  157.    if option == "host" then
  158.       assert(type(value) == "string", "host must be string")
  159.       return (libssh.ssh_options_set(session, 0, value) >= 0)
  160.    elseif option == "port" then
  161.       assert(type(value) == "number", "port must be integer")
  162.       return (libssh.ssh_options_set(session, 1, int_to_num(value)) >= 0)
  163.    elseif option == "user" then
  164.       assert(type(value) == "string", "user must be string")
  165.       return (libssh.ssh_options_set(session, 4, value) >= 0)
  166.    elseif option == "ssh_dir" then
  167.       assert(type(value) == "string", "ssh_dir must be string")
  168.       return (libssh.ssh_options_set(session, 5, value) >= 0)
  169.    elseif option == "identity" then
  170.       assert(type(value) == "string", "identity must be string")
  171.       return (libssh.ssh_options_set(session, 6, value) >= 0)
  172.    elseif option == "known_hosts" then
  173.       assert(type(value) == "string", "known_hosts must be string")
  174.       return (libssh.ssh_options_set(session, 8, value) >= 0)
  175.    elseif option == "timeout" then
  176.       assert(type(value) == "number", "Timeout must be number")
  177.       return (libssh.ssh_options_set(session, 9, ffi.new("int", value)) >= 0)
  178.    elseif option == "ssh1" then
  179.       assert(type(value) == "boolean", "ssh1 must be boolean")
  180.       return (libssh.ssh_options_set(session, 11, ffi.new("int", value and 1 or 0)) >= 0)
  181.    elseif option == "ssh2" then
  182.       assert(type(value) == "boolean", "ssh2 must be boolean")
  183.       return (libssh.ssh_options_set(session, 12, ffi.new("int", value and 1 or 0)) >= 0)
  184.    else
  185.       return nil, "Not implemented"
  186.    end
  187.    return nil, "Unknown option"
  188. end
  189.  
  190. -- close the connection and free memory
  191. local function close(session)
  192.    assert(session ~= nil, "Session shouldn't be nil")
  193.    if libssh.ssh_is_connected(session) then
  194.       libssh.ssh_disconnect(session)
  195.    end
  196.    libssh.ssh_free(session)
  197.    return true
  198. end
  199.  
  200. local function get_issue_banner(session)
  201.    assert(session ~= nil, "Session shouldn't be nil")
  202.    return libssh.ssh_get_issue_banner(session)
  203. end
  204.  
  205. local function request_exec(session, cmd, callback)
  206.    assert(session ~= nil, "Session shouldn't be nil")
  207.    assert(type(cmd) == "string")
  208.    local channel = libssh.ssh_channel_new(session)
  209.    if channel == nil then
  210.       return nil, "Unable to create channel"
  211.    end
  212.    local rc = libssh.ssh_channel_open_session(channel);
  213.    if rc ~= 0 then
  214.       libssh.ssh_channel_free(channel);
  215.       return nil, "Unable to open channel session"
  216.    end
  217.  
  218.    -- TODO: retreive exit code
  219.    local rc = libssh.ssh_channel_request_exec(channel, cmd)
  220.    if rc ~= 0 then
  221.       -- err = libssh.ssh_get_error(session)
  222.       -- debug (ffi.string(err))
  223.       libssh.ssh_channel_close(channel);
  224.       libssh.ssh_channel_free(channel);
  225.       return nil, "Unable to perform request."
  226.    end
  227.    local chunks = {}
  228.    local buffer = ffi.new("char[1024]", {})
  229.    local nbytes = libssh.ssh_channel_read(channel, buffer, 1024, 0);
  230.    while nbytes > 0 do
  231.       local s = ffi.string(buffer, nbytes)
  232.       if callback then
  233.          callback(s)
  234.       else
  235.          table.insert(chunks, s)
  236.       end
  237.       nbytes = libssh.ssh_channel_read(channel, buffer, 1024, 0);
  238.    end
  239.  
  240.    if nbytes < 0 then
  241.       libssh.ssh_channel_close(channel);
  242.       libssh.ssh_channel_free(channel);
  243.       return nil
  244.    end
  245.    libssh.ssh_channel_send_eof(channel)
  246.    libssh.ssh_channel_close(channel)
  247.    libssh.ssh_channel_free(channel)
  248.  
  249.    if callback then
  250.       return true
  251.    else
  252.       return table.concat(chunks)
  253.    end
  254. end
  255.  
  256. return {
  257.    new = new,
  258.    connect = connect,
  259.    auth_password = auth_password,
  260.    auth_autopubkey = auth_autopubkey,
  261.    set_option = set_option,
  262.    close = close,
  263.    get_issue_banner = get_issue_banner,
  264.    request_exec = request_exec,
  265. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement