Giter VIP home page Giter VIP logo

libssh2-hs's Introduction

libssh2-hs README

Build Status

This repository contains two closely related packages.

libssh2

This package provides FFI bindings for SSH2 client library named libssh2.

As of version 0.2 all blocking is handled in Haskell code rather than in C code. This means that all calls are now interruptable using Haskell asynchronous exceptions; for instance, it is now possible to use System.Timeout in combination with "libssh2".

Note on usage on Windows: On Windows you MUST compile your executable with -threaded or libssh2 will NOT work. We have tested libssh2 on Windows using http://mingw.org/, with http://www.openssl.org/ and http://libssh2.org/ compiled from source (be sure to pass the shared option to the configure script for openssl to enable the shared libraries).

libssh2-conduit

This package provides Conduit interface (see conduit package) for libssh2 FFI bindings (see libssh2 package). This allows one to receive data from SSH channels lazily, without need to read all channel output to the memory.

libssh2-hs's People

Contributors

anacrolix avatar blackheaven avatar bsummer4 avatar chessai avatar defanor avatar define-null avatar edsko avatar eperederey avatar hjdskes avatar kvanbere avatar linuxuser404 avatar portnov avatar qnikst avatar samprotas avatar tristancacqueray avatar wldhx avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

libssh2-hs's Issues

Compile errors

I'm trying to build libssh2, I get these errors:

Resolving dependencies...
Configuring libssh2-0.2.0.1...
Building libssh2-0.2.0.1...
Preprocessing library libssh2-0.2.0.1...
[1 of 5] Compiling Network.SSH.Client.LibSSH2.WaitSocket ( src/Network/SSH/Client/LibSSH2/WaitSocket.hs, dist/build/Network/SSH/Client/LibSSH2/WaitSocket.o )
[2 of 5] Compiling Network.SSH.Client.LibSSH2.Types ( dist/build/Network/SSH/Client/LibSSH2/Types.hs, dist/build/Network/SSH/Client/LibSSH2/Types.o )
[3 of 5] Compiling Network.SSH.Client.LibSSH2.Errors ( dist/build/Network/SSH/Client/LibSSH2/Errors.hs, dist/build/Network/SSH/Client/LibSSH2/Errors.o )

src/Network/SSH/Client/LibSSH2/Errors.chs:120:18:
    Illegal type signature: `(Session)
                             -> (Ptr Int) -> (Int) -> IO (Int, String) getLastError_ a1 a3 a4'
      Perhaps you intended to use -XScopedTypeVariables
    In a pattern type-signature

src/Network/SSH/Client/LibSSH2/Errors.chs:122:25:
    Not in scope: `a1'
    Perhaps you meant a1' (line 122)

src/Network/SSH/Client/LibSSH2/Errors.chs:124:23:
    Not in scope: `a3'
    Perhaps you meant a3' (line 124)

src/Network/SSH/Client/LibSSH2/Errors.chs:125:28:
    Not in scope: `a4'
    Perhaps you meant a4' (line 125)

src/Network/SSH/Client/LibSSH2/Errors.chs:164:22:
    Illegal type signature: `(Session)
                             -> IO (([Direction])) blockedDirections a1'
      Perhaps you intended to use -XScopedTypeVariables
    In a pattern type-signature

src/Network/SSH/Client/LibSSH2/Errors.chs:166:25:
    Not in scope: `a1'
    Perhaps you meant a1' (line 166)
Failed to install libssh2-0.2.0.1
cabal: Error: some packages failed to install:
libssh2-0.2.0.1 failed during the building phase. The exception was:
ExitFailure 1

This is on Fedora19, GHC 7.6.3, libssh2-devel version 1.4.3-4. I'm happy to help,test, or whatever.

Thanks in advance.

Does not close socket, remains in CLOSE_WAIT, leading to running out of file descriptors.

If you run the following file:

{-# LANGUAGE OverloadedStrings #-}

import Network.SSH.Client.LibSSH2
import System.Process
import System.Directory
import System.FilePath
import System.Environment
import qualified Data.ByteString.Lazy as BSL

main :: IO ()
main = do
    system "stty -echo"
    putStr "password: "
    passwd <- getLine
    putStrLn ""
    system "stty echo"
    homeDir <- getHomeDirectory
    username <- getEnv "USER"
    output <- withSSH2User (homeDir </> ".ssh/known_hosts") username passwd "localhost" 22 $ \ session ->
      BSL.concat . snd <$> execCommands session ["echo hi"]
    BSL.putStrLn $ "output is: " `BSL.append` output
    putStr "press Enter to quit"
    getLine
    return ()

and, before "[pressing] Enter to quit", with $SSH_TEST_PID being the pid of the process, you check the file descriptors with:

$ lsof -p $SSH_TEST_PID 2>/dev/null | awk '$8 ~ /TCP/'
ghc     18501  jol   11u     IPv4 1256441       0t0      TCP localhost.localdomain:37004->localhost.localdomain:ssh (CLOSE_WAIT)

You'll see the file descriptor hasn't been closed.

Set up travis CI for macos builds

There are some settings in the "macos_test" branch, but they are not working.
It seems I will not be able to configure it just that easy.

libssh2 not building on Mavericks

I'm running on OS X 10.9.4, and c2hs seems to choke on installation.

Resolving dependencies...
Notice: installing into a sandbox located at
/Users/ian/Code/better-builder/.cabal-sandbox
Configuring libssh2-0.2.0.2...
Building libssh2-0.2.0.2...
Failed to install libssh2-0.2.0.2
Last 10 lines of the build log ( /Users/ian/Code/better-builder/.cabal-sandbox/logs/libssh2-0.2.0.2.log ):
Configuring libssh2-0.2.0.2...
Building libssh2-0.2.0.2...
Preprocessing library libssh2-0.2.0.2...
c2hs: C header contains errors:

/usr/include/string.h:131: (column 90) [ERROR]  >>> Syntax error !
  The symbol `=' does not fit here.

cabal: Error: some packages failed to install:
libssh2-0.2.0.2 failed during the building phase. The exception was:
ExitFailure 1

I noticed that you merged in some fixes that are supposed to make this work in Mavericks. Any chance you could release the fix to Hackage?

Unknown direction: 0

Hello!

I am working on a prototype SFTP client to send a ByteString rather than a FilePath or a Handle. My code follows below.

In some cases, my tests error out on the message

Unknown direction: 0
 ┃ │ CallStack (from HasCallStack):
 ┃ │   error, called at src/Network/SSH/Client/LibSSH2/Types.chs:146:13 in libssh2-0.2.0.8-KCLfOufevvC4tAj3aDE2y8:Network.SSH.Client.LibSSH2.Types

Checking the documentation for libssh2_session_block_directions, apparently this function is returning zero. I am not familiar with the internals of libssh2, so I am not sure how to proceed. My first thought would be to change int2dirto account for 0:

int2dir :: (Eq a, Num a, Show a) => a -> [Direction]
int2dir 0 = []
int2dir 1 = [INBOUND]
int2dir 2 = [OUTBOUND]
int2dir 3 = [INBOUND, OUTBOUND]
int2dir x = error $ "Unknown direction: " ++ show x

and accordingly, modify threadWaitSession:

threadWaitSession :: (SshCtx ctx) => Maybe ctx -> IO ()
threadWaitSession Nothing = error "EAGAIN thrown without session present"
threadWaitSession (Just ctx) = do
  let s = getSession ctx
  mSocket <- sessionGetSocket s
  case mSocket of
    Nothing -> error "EAGAIN thrown on session without socket"
    Just socket -> do
      dirs <- blockedDirections s
      if (OUTBOUND `elem` dirs)
        then threadWaitWrite socket
        else
          if (INBOUND `elem` dirs)
            then threadWaitRead socket
            else pure ()

This way we maintain semantics in case int2dir is called with 1, 2 or 3 but we don't error out (essentially no-op) in the case that int2dir is called with 0.

From the Haskell side of things that would work (some combinators would clean up the nested if/else), but as I said I don't know if this is correct from the libssh2 perspective. Can you shed some light on this?


My rough implementation to send a ByteString:

useAsCStringLen :: MonadUnliftIO m => ByteString -> (CStringLen -> m a) -> m a
useAsCStringLen bs inner = withRunInIO $ \unliftIO -> B.useAsCStringLen bs (unliftIO . inner)

bufferSize :: Int
bufferSize = 0x100000

send :: MonadIO m => SftpHandle -> CLong -> Ptr CChar -> m ()
send = go 0
  where
    go :: MonadIO m => Int -> SftpHandle -> CLong -> Ptr CChar -> m ()
    go _ _ 0 _ = pure ()
    go written sftpHandle size buf = do
      sent <- liftIO $ handleInt (Just sftpHandle) $
        {# call libssh2_sftp_write #} (toPointer sftpHandle) buf (fromIntegral size)
      go (written + fromIntegral sent) sftpHandle (size - fromIntegral sent) (advancePtr buf written)

sftpWriteFileFromBytes :: MonadUnliftIO m => PrimMonad m => SftpHandle -> ByteString -> m Integer
sftpWriteFileFromBytes sftpHandle bs = useAsCStringLen bs (uncurry (go 0))
  where
    go :: MonadUnliftIO m => PrimMonad m => Integer -> Ptr CChar -> Int -> m Integer
    go done _ 0 = pure done
    go done src len = do
      let nElements = min len (bufferSize `div` sizeOf (undefined :: CChar))
      send sftpHandle (fromIntegral nElements) src
      go (done + fromIntegral nElements) (advancePtr src nElements) (len - nElements)

execCommands with more than one command throws BAD_USE

I am getting BAD_USE if I try to pass more than one command to execCommands. For instance, this works (in ghci):

> withSSH2 "/home/alex/.ssh/known_hosts" "/home/alex/.ssh/id_rsa.pub" "/home/alex/.ssh/id_rsa" "" "alex" "myserver" 22 $ flip execCommands ["whoami"]
(0,["alex\n"])

and this doesn't:

> withSSH2 "/home/alex/.ssh/known_hosts" "/home/alex/.ssh/id_rsa.pub" "/home/alex/.ssh/id_rsa" "" "alex" "myserver" 22 $ flip execCommands ["whoami", "whoami"]
*** Exception: BAD_USE

Am I doing anything wrong?

Use "unsafe" where appropriate

Many of the foreign exports are not declared "unsafe", which means that are less efficient than they could be. They should be marked as "unsafe" where appropriate (which I think is more or less everywhere).

This is low priority, just recorded it here so that it's not forgotten.

Problem with multithreaded programs

There is a situation in when libssh2-hs will be unusable in -threaded environment:
it can be happened if libssh2 itself is built against gcrypt (--with-gcrypt). If so user should explicitly set gcrypt threading algorithm.

ssh2-client: ath.c:193: _gcry_ath_mutex_lock: Assertion `*lock == ((ath_mutex_t) 0)' failed.
Aborted

"stack install libssh2-0.2.0.7" for lts-16.4

I'd like to use the library in my project that uses stack lts-16.4 (ghc-8.8.3).

Here's what I get when trying to install it (I am on macos 10.15.5):

$ brew install openssl libssh2
...
$ export PKG_CONFIG_PATH="/usr/local/opt/[email protected]/lib/pkgconfig"

$ stack install libssh2-0.2.0.7
libssh2> configure
libssh2> Configuring libssh2-0.2.0.7...
libssh2> build
libssh2> Preprocessing library for libssh2-0.2.0.7..
libssh2> Building library for libssh2-0.2.0.7..
libssh2> [1 of 5] Compiling Network.SSH.Client.LibSSH2.Types
libssh2> 
libssh2> /private/var/folders/qk/vw0nqkz90cz52plb04b416fh0000gn/T/stack-008b142a03477e0c/libssh2-0.2.0.7/src/Network/SSH/Client/LibSSH2/Types.chs:46:1: warning: [-Wunused-imports]
libssh2>     The import of ‘Foreign.C.Types’ is redundant
libssh2>       except perhaps to import instances from ‘Foreign.C.Types’
libssh2>     To import instances alone, use: import Foreign.C.Types()
libssh2>    |
libssh2> 46 | import Network.Socket
libssh2>    | ^^^^^^^^^^^^^^^^^^^^^^
libssh2> [2 of 5] Compiling Network.SSH.Client.LibSSH2.WaitSocket
libssh2> 
libssh2> /private/var/folders/qk/vw0nqkz90cz52plb04b416fh0000gn/T/stack-008b142a03477e0c/libssh2-0.2.0.7/src/Network/SSH/Client/LibSSH2/WaitSocket.hs:29:41: error:
libssh2>     • Couldn't match type ‘IO Foreign.C.Types.CInt’
libssh2>                      with ‘Foreign.C.Types.CInt’
libssh2>       Expected type: Socket -> Foreign.C.Types.CInt
libssh2>         Actual type: Socket -> IO Foreign.C.Types.CInt
libssh2>     • In the second argument of ‘(.)’, namely ‘fdSocket’
libssh2>       In the second argument of ‘(.)’, namely ‘Fd . fdSocket’
libssh2>       In the expression: threadWaitRead_ . Fd . fdSocket
libssh2>    |
libssh2> 29 | threadWaitRead = threadWaitRead_ . Fd . fdSocket
libssh2>    |                                         ^^^^^^^^
libssh2> 
libssh2> /private/var/folders/qk/vw0nqkz90cz52plb04b416fh0000gn/T/stack-008b142a03477e0c/libssh2-0.2.0.7/src/Network/SSH/Client/LibSSH2/WaitSocket.hs:32:43: error:
libssh2>     • Couldn't match type ‘IO Foreign.C.Types.CInt’
libssh2>                      with ‘Foreign.C.Types.CInt’
libssh2>       Expected type: Socket -> Foreign.C.Types.CInt
libssh2>         Actual type: Socket -> IO Foreign.C.Types.CInt
libssh2>     • In the second argument of ‘(.)’, namely ‘fdSocket’
libssh2>       In the second argument of ‘(.)’, namely ‘Fd . fdSocket’
libssh2>       In the expression: threadWaitWrite_ . Fd . fdSocket
libssh2>    |
libssh2> 32 | threadWaitWrite = threadWaitWrite_ . Fd . fdSocket
libssh2>    |                                           ^^^^^^^^

--  While building package libssh2-0.2.0.7 using:
      /Users/oshyshko/.stack/setup-exe-cache/x86_64-osx/Cabal-simple_mPHDZzAJ_3.0.1.0_ghc-8.8.3 --builddir=.stack-work/dist/x86_64-osx/Cabal-3.0.1.0 build --ghc-options ""
    Process exited with code: ExitFailure 1

How do I get it work?

Password/private key argument for ssh-client.hs

In libssh2/ssh-client.hs,

main = do
  args <- getArgs
  case args of
    ["command", user, host, port, cmd]  -> runCommand user host (read port) cmd

There is no type information available for runCommand. It is not clear to the users, how username can be passed along with password/private key file.

getHostKey returns an empty string

Hello,

I am running into the case where getHostKey always returns an empty string. This happens on two very distinct SFTP servers, one of which is an AWS Transfer Family SFTP server; the other is an old mainframe-like SFTP server.

In both cases, the host key type returned is 1, which I assume to be TYPE_PLAIN as per

Below is the code I use to initialise my SFTP session:

withSftpUser :: forall m a
  .  MonadLog m
  => MonadMetrics m
  => MonadThrow m
  => MonadUnliftIO m
  => SftpConfigF Text
  -> (Sftp -> m a)
  -> m a
withSftpUser config action = withSession (host config) (port config) $ \session -> do
  checkHost session (host config) (port config) (knownHosts config) >>= \case
    MISMATCH -> logError "Host key mismatch" >> throwM ERROR_KNOWN_HOSTS
    FAILURE -> logError "Failed to verify host key" >> throwM ERROR_KNOWN_HOSTS
    NOTFOUND -> logError "Host key not found" >> throwM ERROR_KNOWN_HOSTS
    MATCH -> logInfo "Verified host key"
  case credentials config of
    MkUserPass (UserPass (Username username) password) ->
      liftIO $ usernamePasswordAuth session (Text.unpack username) (Text.unpack password)
  withSftpSession session action

withSftpSession :: forall m a
  .  MonadUnliftIO m
  => Session
  -> (Sftp -> m a)
  -> m a
withSftpSession session = bracket (liftIO $ sftpInit session) (liftIO . sftpShutdown)

withSession :: forall m a
  .  MonadUnliftIO m
  => Text
  -> Int
  -> (Session -> m a)
  -> m a
withSession hostname port = bracket
  (liftIO $ initialize True >> sessionInit (Text.unpack hostname) port)
  (liftIO . sessionClose)

As far as I can see, the FFI in libssh2-hs for getHostKey is correct. Am I missing something?

Allow skipping known_hosts step

Honestly, it's hard to provide a cross platform path to a known hosts file.

Can we just skip it? In my use case it makes zero sense to enforce checking known hosts.

when calling channelShell, ghc throws SOCKET_DISCONNECT

When i use the channelShell function in my program, the ssh server sends SOCKET_DISCONNECT error when i try to connect to it.

I've looked into the ssh server logs,and it says:

Nov 19 09:00:10 varch sshd[13210]: Accepted password for dwat3r from 127.0.0.1 port 44237 ssh2
Nov 19 09:00:10 varch sshd[13210]: pam_unix(sshd:session): session opened for user dwat3r by (uid=0)
Nov 19 09:00:10 varch sshd[13212]: Packet integrity error (4 bytes remaining) at session.c:2196

what is the problem?

runShellCommands hangs on reading output

Hi,

runShellCommands did not work for me, it hung on reading the output on the first command.

After reading the connection protocol specs and your implementation I'm almost convinced that the implementation cannot work for the following reasons:

  1. A shell is requested
  2. Some data is written to the channel as the "command"
  3. readAllChannel tries to read in 1KiB chunks until it reads an empty string
    3.1. For each chunkreadChannelEx calls into libssh2_channel_read_ex attempting to read 1KiB
    3.2 This ends up eventually calling _libssh2_channel_read which returns the number of bytes read
    3.3 That function will only return 0 bytes only if the remote sent an EOF or the channel was closed, otherwise it will either read something or produce EAGAIN.

This loop would terminate only if an EOF was sent or the channel was closed.

Am I looking at this wrong?

Port forwarding

Are there analogues to channel_direct_tcpip exposed in the API? Is there any technical reason this couldn't be exposed if I've missed it, or was it just overlooked?

Errors building on Windows

Hi, I get these errors building on windows using GHC 7.8.3:

src\Network\SSH\Client\LibSSH2\Foreign.chs:183:20:
    Couldn't match expected type `CULLong' with actual type `CUInt'
    In the second argument of `handshake_'_', namely a2'
    In the first argument of `(>>=)', namely handshake_'_ a1' a2'

src\Network\SSH\Client\LibSSH2\Foreign.chs:383:15:
    No instance for (IntResult CLLong)
      arising from a use of `handleInt'
    In the expression: handleInt (Just $ channelSession ch)
    In a stmt of a 'do' block:
      sent <- handleInt (Just $ channelSession ch)
              $ libssh2_channel_write_ex
                  (toPointer ch) 0 (plusPtr buffer written) (fromIntegral size)
    In the expression:
      do { sent <- handleInt (Just $ channelSession ch)
                   $ libssh2_channel_write_ex
                       (toPointer ch) 0 (plusPtr buffer written) (fromIntegral size);
           send
             (written + fromIntegral sent) (size - fromIntegral sent) buffer }

src\Network\SSH\Client\LibSSH2\Foreign.chs:411:15:
    Could not deduce (IntResult CLLong)
      arising from a use of `handleInt'
    from the context (Integral a)
      bound by the inferred type of go :: Integral a => a -> IO Integer
      at src\Network\SSH\Client\LibSSH2\Foreign.chs:(409,7)-(427,34)
    In the expression: handleInt (Just $ channelSession ch)
    In a stmt of a 'do' block:
      sz <- handleInt (Just $ channelSession ch)
            $ libssh2_channel_read_ex
                (toPointer ch) 0 buffer (fromIntegral toRead)
    In the expression:
      do { let toRead
                 = min (fromIntegral fileSize - got) (fromIntegral bufferSize);
           sz <- handleInt (Just $ channelSession ch)
                 $ libssh2_channel_read_ex
                     (toPointer ch) 0 buffer (fromIntegral toRead);
           let isz :: Integer
               isz = fromIntegral sz;
           callback buffer (fromIntegral sz);
           .... }

These are Haskell errors, so it's pretty strange. Is nobody else getting them ... ?

@edsko @portnov

checkHost returns FAILURE instead of MISMATCH, MATCH or NOTFOUND

I noticed that withSSH2User doesn't error when known_hosts doesn't have the hosts key. I tested in GHCi and checkHost returned FAILURE instead of NOTFOUND or MATCH. I tested with a host which is in known_hosts and a host which was not in known_hosts. I would expect checkHost to return NOTFOUND and MATCH instead of FAILURE for the two cases.

I am running Arch Linux and version 1.8.2 of libssh2 and version 0.2.0.7 of libssh2-hs library.

Is the intention for withSSH2User to only fail if MISMATCH is found? I would also expect NOTFOUND to cause an error too.

Specify minimum version of the C library

It seems that libssh2 supports pkgconfig, so

pkgconfig-depends: libssh2 >= 1.4

might to do the trick (I'm not actually sure that we need 1.4; certainly 1.4 works, and certainly 1.2.7 does not; haven't tried with 1.3). However, we need to test if this pkgconfig thing works on Windows too.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.