mallcop 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2010 Carl Lerche, Aaron Patterson
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,48 @@
1
+ = mall_cop
2
+
3
+ * FIX (url)
4
+
5
+ == DESCRIPTION:
6
+
7
+ FIX (describe your package)
8
+
9
+ == FEATURES/PROBLEMS:
10
+
11
+ * FIX (list of features or problems)
12
+
13
+ == SYNOPSIS:
14
+
15
+ FIX (code sample of usage)
16
+
17
+ == REQUIREMENTS:
18
+
19
+ * FIX (list of requirements)
20
+
21
+ == INSTALL:
22
+
23
+ * FIX (sudo gem install, anything else)
24
+
25
+ == LICENSE:
26
+
27
+ (The MIT License)
28
+
29
+ Copyright (c) 2010 FIX
30
+
31
+ Permission is hereby granted, free of charge, to any person obtaining
32
+ a copy of this software and associated documentation files (the
33
+ 'Software'), to deal in the Software without restriction, including
34
+ without limitation the rights to use, copy, modify, merge, publish,
35
+ distribute, sublicense, and/or sell copies of the Software, and to
36
+ permit persons to whom the Software is furnished to do so, subject to
37
+ the following conditions:
38
+
39
+ The above copyright notice and this permission notice shall be
40
+ included in all copies or substantial portions of the Software.
41
+
42
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
43
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
44
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
45
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
46
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
47
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
48
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,222 @@
1
+ #include <mallcop.h>
2
+
3
+ VALUE rb_cMallCopChannel;
4
+
5
+ static void dealloc(MallCopChannel *m_channel)
6
+ {
7
+ BLOCK(libssh2_channel_free( m_channel->libssh2_channel ));
8
+ mallcop_session_release( m_channel->m_session );
9
+ free( m_channel );
10
+ }
11
+
12
+ static VALUE allocate(VALUE klass)
13
+ {
14
+ MallCopChannel *m_channel = calloc( 1, sizeof(MallCopChannel) );
15
+ return Data_Wrap_Struct(klass, 0, dealloc, m_channel);
16
+ }
17
+
18
+ static VALUE initialize(VALUE self)
19
+ {
20
+ MallCopChannel *m_channel;
21
+ MallCopSession *m_session;
22
+ VALUE session;
23
+
24
+ Data_Get_Struct(self, MallCopChannel, m_channel);
25
+
26
+ if ( m_channel->libssh2_channel ) {
27
+ rb_raise(rb_eRuntimeError, "the libssh2 channel is already initialized");
28
+ return Qnil;
29
+ }
30
+
31
+ session = rb_iv_get(self, "@session");
32
+
33
+ if ( NIL_P(session) ) {
34
+ rb_raise(rb_eRuntimeError, "@session is nil");
35
+ return Qfalse;
36
+ }
37
+
38
+ Data_Get_Struct(session, MallCopSession, m_session);
39
+
40
+ m_channel->libssh2_channel = libssh2_channel_open_session(m_session->libssh2_session);
41
+
42
+ if ( !m_channel->libssh2_channel ) {
43
+ if ( libssh2_session_last_errno(m_session->libssh2_session) == LIBSSH2_ERROR_EAGAIN ) {
44
+ return INT2FIX(LIBSSH2_ERROR_EAGAIN);
45
+ }
46
+
47
+ mallcop_raise_last_error(m_session, rb_eRuntimeError);
48
+ return Qnil;
49
+ }
50
+
51
+ // Initializing the libssh2 channel is successful, so we can associate
52
+ // the session object with the channel object.
53
+ m_channel->m_session = m_session;
54
+ mallcop_session_retain(m_session);
55
+
56
+ return Qtrue;
57
+ }
58
+
59
+ static VALUE request_pty(VALUE self, VALUE term)
60
+ {
61
+ MallCopChannel *m_channel;
62
+ int ret;
63
+
64
+ Data_Get_Struct(self, MallCopChannel, m_channel);
65
+
66
+ ret = libssh2_channel_request_pty(m_channel->libssh2_channel, StringValuePtr(term));
67
+
68
+ if ( ret == 0 || ret == LIBSSH2_ERROR_EAGAIN ) {
69
+ return INT2FIX(ret);
70
+ }
71
+
72
+ mallcop_raise_last_error(m_channel->m_session, rb_eRuntimeError);
73
+ return Qnil;
74
+ }
75
+
76
+ static VALUE shell(VALUE self)
77
+ {
78
+ MallCopChannel *m_channel;
79
+ int ret;
80
+
81
+ Data_Get_Struct(self, MallCopChannel, m_channel);
82
+
83
+ ret = libssh2_channel_shell(m_channel->libssh2_channel);
84
+
85
+ if ( ret == 0 || ret == LIBSSH2_ERROR_EAGAIN ) {
86
+ return INT2FIX(ret);
87
+ }
88
+
89
+ mallcop_raise_last_error(m_channel->m_session, rb_eRuntimeError);
90
+ return Qnil;
91
+ }
92
+
93
+ static VALUE channel_exec(VALUE self, VALUE command)
94
+ {
95
+ MallCopChannel *m_channel;
96
+ int ret;
97
+
98
+ Data_Get_Struct(self, MallCopChannel, m_channel);
99
+
100
+ ret = libssh2_channel_exec(m_channel->libssh2_channel, StringValuePtr(command));
101
+
102
+ if ( ret == 0 || ret == LIBSSH2_ERROR_EAGAIN ) {
103
+ return INT2FIX(ret);
104
+ }
105
+
106
+ mallcop_raise_last_error(m_channel->m_session, rb_eRuntimeError);
107
+ return Qnil;
108
+ }
109
+
110
+ static VALUE channel_read(VALUE self)
111
+ {
112
+ MallCopChannel *m_channel;
113
+ char buffer[0x4000];
114
+ int count;
115
+
116
+ Data_Get_Struct(self, MallCopChannel, m_channel);
117
+
118
+ count = libssh2_channel_read(m_channel->libssh2_channel, buffer, sizeof(buffer));
119
+
120
+ if ( count >= 0 ) {
121
+ return rb_str_new((const char *) &buffer, count);
122
+ } else if ( count == LIBSSH2_ERROR_EAGAIN ) {
123
+ return INT2FIX(LIBSSH2_ERROR_EAGAIN);
124
+ }
125
+
126
+ mallcop_raise_last_error(m_channel->m_session, rb_eRuntimeError);
127
+ return Qnil;
128
+ }
129
+
130
+ static VALUE channel_write(VALUE self, VALUE string)
131
+ {
132
+ MallCopChannel *m_channel;
133
+ ssize_t ret;
134
+
135
+ Data_Get_Struct(self, MallCopChannel, m_channel);
136
+
137
+ ret = libssh2_channel_write(m_channel->libssh2_channel, StringValuePtr(string), RSTRING_LEN(string));
138
+
139
+ if ( ret >= 0 || ret == LIBSSH2_ERROR_EAGAIN ) {
140
+ return INT2FIX(ret);
141
+ }
142
+
143
+ mallcop_raise_last_error(m_channel->m_session, rb_eRuntimeError);
144
+ return Qnil;
145
+ }
146
+
147
+ static VALUE channel_close(VALUE self)
148
+ {
149
+ MallCopChannel *m_channel;
150
+ int ret;
151
+
152
+ Data_Get_Struct(self, MallCopChannel, m_channel);
153
+
154
+ ret = libssh2_channel_close(m_channel->libssh2_channel);
155
+
156
+ if ( ret >= 0 || ret == LIBSSH2_ERROR_EAGAIN ) {
157
+ return INT2FIX(ret);
158
+ }
159
+
160
+ mallcop_raise_last_error(m_channel->m_session, rb_eRuntimeError);
161
+ return Qnil;
162
+ }
163
+
164
+ static VALUE eof(VALUE self)
165
+ {
166
+ MallCopChannel *m_channel;
167
+
168
+ Data_Get_Struct(self, MallCopChannel, m_channel);
169
+
170
+ return libssh2_channel_eof(m_channel->libssh2_channel) ? Qtrue : Qfalse;
171
+ }
172
+
173
+ static VALUE send_eof(VALUE self)
174
+ {
175
+ MallCopChannel *m_channel;
176
+ int ret;
177
+
178
+ Data_Get_Struct(self, MallCopChannel, m_channel);
179
+
180
+ ret = libssh2_channel_send_eof(m_channel->libssh2_channel);
181
+
182
+ if ( ret == 0 || ret == LIBSSH2_ERROR_EAGAIN ) {
183
+ return INT2FIX(ret);
184
+ }
185
+
186
+ mallcop_raise_last_error(m_channel->m_session, rb_eRuntimeError);
187
+ return Qnil;
188
+ }
189
+
190
+ static VALUE flush(VALUE self)
191
+ {
192
+ MallCopChannel *m_channel;
193
+ int ret;
194
+
195
+ Data_Get_Struct(self, MallCopChannel, m_channel);
196
+
197
+ ret = libssh2_channel_flush(m_channel->libssh2_channel);
198
+
199
+ if ( ret == 0 || ret == LIBSSH2_ERROR_EAGAIN ) {
200
+ return INT2FIX(ret);
201
+ }
202
+
203
+ mallcop_raise_last_error(m_channel->m_session, rb_eRuntimeError);
204
+ return Qnil;
205
+ }
206
+
207
+ void init_mallcop_channel()
208
+ {
209
+ rb_cMallCopChannel = rb_define_class_under(rb_mMallCop, "Channel", rb_cObject);
210
+ rb_define_alloc_func(rb_cMallCopChannel, allocate);
211
+
212
+ rb_define_private_method(rb_cMallCopChannel, "native_eof?", eof, 0);
213
+ rb_define_private_method(rb_cMallCopChannel, "native_initialize", initialize, 0);
214
+ rb_define_private_method(rb_cMallCopChannel, "native_channel_close", channel_close, 0);
215
+ rb_define_private_method(rb_cMallCopChannel, "native_channel_read", channel_read, 0);
216
+ rb_define_private_method(rb_cMallCopChannel, "native_channel_write", channel_write, 1);
217
+ rb_define_private_method(rb_cMallCopChannel, "native_request_pty", request_pty, 1);
218
+ rb_define_private_method(rb_cMallCopChannel, "native_shell", shell, 0);
219
+ rb_define_private_method(rb_cMallCopChannel, "native_channel_exec", channel_exec, 1);
220
+ rb_define_private_method(rb_cMallCopChannel, "native_send_eof", send_eof, 0);
221
+ rb_define_private_method(rb_cMallCopChannel, "native_flush", flush, 0);
222
+ }
@@ -0,0 +1,16 @@
1
+ require 'mkmf'
2
+
3
+ # :stopdoc:
4
+
5
+ dir_config 'libssh2', '/opt/local/include', '/opt/local/lib'
6
+
7
+ def asplode missing
8
+ abort "#{missing} is missing. Please install libssh2."
9
+ end
10
+
11
+ asplode('libssh2.h') unless find_header 'libssh2.h'
12
+ asplode('libssh2') unless find_library 'ssh2', 'libssh2_session_init_ex'
13
+
14
+ create_makefile 'mallcop/mallcop'
15
+
16
+ # :startdoc:
@@ -0,0 +1,33 @@
1
+ #include <mallcop.h>
2
+
3
+ VALUE rb_mMallCop;
4
+
5
+ void mallcop_raise_last_error(MallCopSession *m_session, VALUE klass)
6
+ {
7
+ char *errmsg;
8
+
9
+ libssh2_session_last_error(m_session->libssh2_session, &errmsg, NULL, 0);
10
+
11
+ rb_raise(klass, errmsg);
12
+ }
13
+
14
+ void Init_mallcop()
15
+ {
16
+ int rc;
17
+
18
+ rb_mMallCop = rb_define_module("MallCop");
19
+
20
+ rb_define_const(rb_mMallCop, "ERROR_ALLOC", INT2FIX(LIBSSH2_ERROR_ALLOC));
21
+ rb_define_const(rb_mMallCop, "ERROR_BANNER_SEND", INT2FIX(LIBSSH2_ERROR_BANNER_SEND));
22
+ rb_define_const(rb_mMallCop, "ERROR_CHANNEL_FAILURE", INT2FIX(LIBSSH2_ERROR_CHANNEL_FAILURE));
23
+ rb_define_const(rb_mMallCop, "ERROR_EAGAIN", INT2FIX(LIBSSH2_ERROR_EAGAIN));
24
+ rb_define_const(rb_mMallCop, "ERROR_KEX_FAILURE", INT2FIX(LIBSSH2_ERROR_KEX_FAILURE));
25
+ rb_define_const(rb_mMallCop, "ERROR_SOCKET_DISCONNECT", INT2FIX(LIBSSH2_ERROR_SOCKET_DISCONNECT));
26
+ rb_define_const(rb_mMallCop, "ERROR_SOCKET_NONE", INT2FIX(LIBSSH2_ERROR_SOCKET_NONE));
27
+ rb_define_const(rb_mMallCop, "ERROR_SOCKET_SEND", INT2FIX(LIBSSH2_ERROR_SOCKET_SEND));
28
+ rb_define_const(rb_mMallCop, "ERROR_PROTO", INT2FIX(LIBSSH2_ERROR_PROTO));
29
+ rb_define_const(rb_mMallCop, "HASH_SHA1", INT2FIX(LIBSSH2_HOSTKEY_HASH_SHA1));
30
+
31
+ init_mallcop_session();
32
+ init_mallcop_channel();
33
+ }
@@ -0,0 +1,31 @@
1
+ #ifndef MALLCOP_H
2
+ #define MALLCOP_H
3
+
4
+ #include <ruby.h>
5
+ #include <libssh2.h>
6
+
7
+ #define BLOCK(stmt) while ((stmt) == LIBSSH2_ERROR_EAGAIN)
8
+
9
+ extern VALUE rb_mMallCop;
10
+ extern VALUE rb_cMallCopSession;
11
+ extern VALUE rb_cMallCopChannel;
12
+
13
+ typedef struct {
14
+ LIBSSH2_SESSION *libssh2_session;
15
+ int count;
16
+ } MallCopSession;
17
+
18
+ typedef struct {
19
+ LIBSSH2_CHANNEL *libssh2_channel;
20
+ MallCopSession *m_session;
21
+ } MallCopChannel;
22
+
23
+ void init_mallcop_channel();
24
+ void init_mallcop_session();
25
+
26
+ void mallcop_session_retain(MallCopSession *session);
27
+ void mallcop_session_release(MallCopSession *session);
28
+
29
+ void mallcop_raise_last_error(MallCopSession *m_session, VALUE klass);
30
+
31
+ #endif
@@ -0,0 +1,228 @@
1
+ #include <mallcop.h>
2
+
3
+ VALUE rb_cMallCopSession;
4
+
5
+ void mallcop_session_retain(MallCopSession *m_session)
6
+ {
7
+ ++m_session->count;
8
+ }
9
+
10
+ void mallcop_session_release(MallCopSession *m_session)
11
+ {
12
+ if ( --m_session->count == 0 ) {
13
+ BLOCK(libssh2_session_free( m_session->libssh2_session ));
14
+ free( m_session );
15
+ }
16
+ }
17
+
18
+ static void dealloc(MallCopSession *m_session)
19
+ {
20
+ mallcop_session_release( m_session );
21
+ }
22
+
23
+ static VALUE allocate(VALUE klass)
24
+ {
25
+ MallCopSession *m_session = calloc( 1, sizeof(MallCopSession) );
26
+ mallcop_session_retain(m_session);
27
+
28
+ return Data_Wrap_Struct(klass, 0, dealloc, m_session);
29
+ }
30
+
31
+ static VALUE initialize(VALUE self)
32
+ {
33
+ MallCopSession *m_session;
34
+
35
+ Data_Get_Struct(self, MallCopSession, m_session);
36
+
37
+ if ( m_session->libssh2_session ) {
38
+ rb_raise(rb_eRuntimeError, "the libssh2 session is already initialized");
39
+ return Qnil;
40
+ }
41
+
42
+ m_session->libssh2_session = libssh2_session_init();
43
+
44
+ if ( !m_session->libssh2_session ) {
45
+ rb_raise(rb_eRuntimeError, "session init failed");
46
+ return Qnil;
47
+ }
48
+
49
+ return Qtrue;
50
+ }
51
+
52
+ static VALUE set_blocking(VALUE self, VALUE blocking)
53
+ {
54
+ MallCopSession *m_session;
55
+
56
+ Data_Get_Struct(self, MallCopSession, m_session);
57
+
58
+ if ( !m_session->libssh2_session ) {
59
+ rb_raise(rb_eRuntimeError, "session not initialized yet");
60
+ return Qnil;
61
+ }
62
+
63
+ libssh2_session_set_blocking( m_session->libssh2_session, NUM2INT(blocking) );
64
+
65
+ return Qnil;
66
+ }
67
+
68
+ static VALUE start(VALUE self, VALUE r_fd)
69
+ {
70
+ MallCopSession *m_session;
71
+ int ret;
72
+ int fd = NUM2INT(r_fd);
73
+
74
+ Data_Get_Struct(self, MallCopSession, m_session);
75
+
76
+ ret = libssh2_session_startup(m_session->libssh2_session, fd);
77
+
78
+ if ( ret == 0 || ret == LIBSSH2_ERROR_EAGAIN ) {
79
+ return INT2FIX(ret);
80
+ }
81
+
82
+ mallcop_raise_last_error(m_session, rb_eRuntimeError);
83
+ return Qnil;
84
+ }
85
+
86
+ static VALUE hostkey_hash(VALUE self, VALUE hashtype)
87
+ {
88
+ MallCopSession *m_session;
89
+ const char *key;
90
+ int keytype = NUM2INT(hashtype);
91
+
92
+ Data_Get_Struct(self, MallCopSession, m_session);
93
+
94
+ key = libssh2_hostkey_hash(m_session->libssh2_session, keytype);
95
+
96
+ if ( !key ) {
97
+ return Qnil;
98
+ }
99
+
100
+ if ( keytype == LIBSSH2_HOSTKEY_HASH_SHA1 ) {
101
+ return rb_str_new(key, 20);
102
+ }
103
+
104
+ return rb_str_new(key, 16);
105
+ }
106
+
107
+ static VALUE userauth_list(VALUE self, VALUE user)
108
+ {
109
+ MallCopSession *m_session;
110
+ char * list;
111
+
112
+ Data_Get_Struct(self, MallCopSession, m_session);
113
+
114
+ do {
115
+ list = libssh2_userauth_list(m_session->libssh2_session,
116
+ StringValuePtr(user), RSTRING_LEN(user));
117
+ } while ( !list && libssh2_session_last_errno(m_session->libssh2_session) == LIBSSH2_ERROR_EAGAIN );
118
+
119
+ return rb_str_new2(list);
120
+ }
121
+
122
+ static VALUE userauth_password(VALUE self, VALUE user, VALUE password)
123
+ {
124
+ MallCopSession *m_session;
125
+ int ret;
126
+
127
+ Data_Get_Struct(self, MallCopSession, m_session);
128
+
129
+ ret = libssh2_userauth_password(m_session->libssh2_session,
130
+ StringValuePtr(user), StringValuePtr(password));
131
+
132
+ if ( ret == 0 || ret == LIBSSH2_ERROR_EAGAIN ) {
133
+ return INT2FIX(ret);
134
+ }
135
+
136
+ ret = libssh2_session_last_errno(m_session->libssh2_session);
137
+
138
+ switch (ret) {
139
+ case LIBSSH2_ERROR_PASSWORD_EXPIRED:
140
+ case LIBSSH2_ERROR_AUTHENTICATION_FAILED:
141
+ return INT2FIX(ret);
142
+ break;
143
+ }
144
+
145
+ mallcop_raise_last_error(m_session, rb_eRuntimeError);
146
+ return Qnil;
147
+ }
148
+
149
+ static VALUE userauth_publickey_fromfile(VALUE self, VALUE user, VALUE public_key, VALUE private_key, VALUE password)
150
+ {
151
+ MallCopSession *m_session;
152
+ int ret;
153
+
154
+ Data_Get_Struct(self, MallCopSession, m_session);
155
+
156
+ BLOCK(ret = libssh2_userauth_publickey_fromfile(m_session->libssh2_session,
157
+ StringValuePtr(user), StringValuePtr(public_key),
158
+ StringValuePtr(private_key), StringValuePtr(password)));
159
+
160
+ if(ret) {
161
+ switch(ret) {
162
+ case LIBSSH2_ERROR_AUTHENTICATION_FAILED:
163
+ case LIBSSH2_ERROR_PUBLICKEY_UNVERIFIED:
164
+ return Qfalse;
165
+ break;
166
+ default:
167
+ rb_raise(rb_eRuntimeError, "authentication failed (%d)", ret);
168
+ break;
169
+ }
170
+ }
171
+
172
+ return Qtrue;
173
+ }
174
+
175
+ static VALUE disconnect(VALUE self, VALUE description)
176
+ {
177
+ MallCopSession *m_session;
178
+ int ret;
179
+
180
+ Data_Get_Struct(self, MallCopSession, m_session);
181
+
182
+ BLOCK(ret = libssh2_session_disconnect(m_session->libssh2_session,
183
+ StringValuePtr(description)));
184
+
185
+ return INT2NUM(ret);
186
+ }
187
+
188
+ static VALUE last_errno(VALUE self)
189
+ {
190
+ MallCopSession *m_session;
191
+ int ret;
192
+
193
+ Data_Get_Struct(self, MallCopSession, m_session);
194
+
195
+ ret = libssh2_session_last_errno(m_session->libssh2_session);
196
+
197
+ return INT2FIX(ret);
198
+ }
199
+
200
+ static VALUE last_errmsg(VALUE self)
201
+ {
202
+ MallCopSession *m_session;
203
+ char *errmsg;
204
+ int errlen;
205
+
206
+ Data_Get_Struct(self, MallCopSession, m_session);
207
+
208
+ libssh2_session_last_error(m_session->libssh2_session, &errmsg, &errlen, 0);
209
+
210
+ return rb_str_new(errmsg, (long) errlen);
211
+ }
212
+
213
+ void init_mallcop_session()
214
+ {
215
+ rb_cMallCopSession = rb_define_class_under(rb_mMallCop, "Session", rb_cObject);
216
+ rb_define_alloc_func(rb_cMallCopSession, allocate);
217
+
218
+ rb_define_private_method(rb_cMallCopSession, "native_initialize", initialize, 0);
219
+ rb_define_private_method(rb_cMallCopSession, "native_start", start, 1);
220
+ rb_define_private_method(rb_cMallCopSession, "native_set_blocking", set_blocking, 1);
221
+ rb_define_private_method(rb_cMallCopSession, "native_userauth_list", userauth_list, 1);
222
+ rb_define_private_method(rb_cMallCopSession, "native_last_errno", last_errno, 0);
223
+ rb_define_private_method(rb_cMallCopSession, "native_last_errmsg", last_errmsg, 0);
224
+ rb_define_private_method(rb_cMallCopSession, "native_userauth_password", userauth_password, 2);
225
+ rb_define_private_method(rb_cMallCopSession, "native_hostkey_hash", hostkey_hash, 1);
226
+ rb_define_private_method(rb_cMallCopSession, "native_userauth_publickey_fromfile", userauth_publickey_fromfile, 4);
227
+ rb_define_private_method(rb_cMallCopSession, "native_disconnect", disconnect, 1);
228
+ }
@@ -0,0 +1,48 @@
1
+ require 'mallcop/mallcop'
2
+ require 'mallcop/channel'
3
+ require 'mallcop/session'
4
+ require 'mallcop/shell'
5
+
6
+ module MallCop
7
+ VERSION = '1.0.0'
8
+
9
+ # Raised when something happens with connecting to the server
10
+ class RuntimeError < ::RuntimeError; end
11
+ class ConnectionError < StandardError ; end
12
+ class SessionError < StandardError ; end
13
+ class ChannelError < StandardError ; end
14
+
15
+ # Establishes a connection to the SSH server.
16
+ #
17
+ # Arguments
18
+ # =====
19
+ # +host+ The host to connect to
20
+ # +username+ The username to connect under.
21
+ #
22
+ # Optional:
23
+ # +options+
24
+ # :port The port to connect on (defaults to 22)
25
+ # :password Use a password based authentication strategy.
26
+ def self.connect(host, username, options = {})
27
+ session = Session.start(host, options[:port] || 22)
28
+
29
+ unless session.authenticate username, options
30
+ raise ChannelError, "Could not authenticate user '#{username}'"
31
+ end
32
+
33
+ if block_given?
34
+ yield session
35
+ session.disconnect("Closing session")
36
+ end
37
+
38
+ session
39
+ end
40
+
41
+ def self.shell(host, username, options = {})
42
+ connect(host, username, options) do |session|
43
+ shell = Shell.new(session)
44
+ yield shell
45
+ shell.cleanup
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,93 @@
1
+ module MallCop
2
+ class Channel
3
+
4
+ def initialize(session)
5
+ @session, @open = session, true
6
+
7
+ block { native_initialize }
8
+ end
9
+
10
+ def read
11
+ retval = ERROR_EAGAIN
12
+
13
+ until retval != ERROR_EAGAIN
14
+ @session.io_select
15
+ retval = read_nonblock
16
+ end
17
+
18
+ retval
19
+ end
20
+
21
+ def read_nonblock
22
+ raise ChannelError, "The channel has been closed" unless @open
23
+ native_channel_read
24
+ end
25
+
26
+ def write(cmd)
27
+ retval = ERROR_EAGAIN
28
+
29
+ until retval != ERROR_EAGAIN
30
+ @session.io_select(true)
31
+ retval = write_nonblock(cmd)
32
+ end
33
+
34
+ retval
35
+ end
36
+
37
+ def write_nonblock(cmd)
38
+ raise ChannelError, "The channel has been closed" unless @open
39
+ native_channel_write(cmd)
40
+ end
41
+
42
+ def eof?
43
+ native_eof?
44
+ end
45
+
46
+ def close
47
+ return true unless @open
48
+ block { native_channel_close }
49
+ true
50
+ ensure
51
+ @open = false
52
+ end
53
+
54
+ def request_pty(term)
55
+ block { native_request_pty(term) }
56
+ true
57
+ end
58
+
59
+ def shell
60
+ block { native_shell }
61
+ true
62
+ end
63
+
64
+ def channel_exec(cmd)
65
+ block { native_channel_exec(cmd) }
66
+ true
67
+ end
68
+
69
+ def send_eof
70
+ block { native_send_eof }
71
+ true
72
+ end
73
+
74
+ def flush
75
+ block { native_flush }
76
+ true
77
+ end
78
+
79
+ private
80
+
81
+ def block
82
+ retval = yield
83
+
84
+ until retval != ERROR_EAGAIN
85
+ @session.io_select
86
+ retval = yield
87
+ end
88
+
89
+ retval
90
+ end
91
+
92
+ end
93
+ end
@@ -0,0 +1,107 @@
1
+ require 'socket'
2
+
3
+ module MallCop
4
+ class Session
5
+
6
+ def self.start(host, port)
7
+ session = new(host, port)
8
+ session.start
9
+ session
10
+ end
11
+
12
+ def initialize(host, port)
13
+ native_initialize
14
+ native_set_blocking(0)
15
+ @host = host
16
+ @port = port
17
+ end
18
+
19
+ def start
20
+ block { native_start(socket.fileno) }
21
+ true
22
+ end
23
+
24
+ def authenticate(username, options)
25
+ allowed = authlist_for username
26
+
27
+ attempted = []
28
+
29
+ auth_methods.each do |method|
30
+ next unless allowed.include?(method)
31
+
32
+ attempted << method
33
+
34
+ if send "authenticate_via_#{method.downcase}", username, options
35
+ return true
36
+ end
37
+ end
38
+
39
+ false
40
+ end
41
+
42
+ def open_channel
43
+ Channel.new(self)
44
+ end
45
+
46
+ def hostkey_hash(hashtype)
47
+ native_hostkey_hash(hashtype)
48
+ end
49
+
50
+ def userauth_publickey_fromfile(user, public_key, private_key, password)
51
+ native_userauth_publickey_fromfile(user, public_key, private_key, password)
52
+ end
53
+
54
+ def disconnect(description)
55
+ native_disconnect(description)
56
+ end
57
+
58
+ def io_select(write = false)
59
+ write ?
60
+ select([], [socket], nil, nil) :
61
+ select([socket], nil, nil, nil)
62
+ end
63
+
64
+ private
65
+
66
+ def socket
67
+ @socket ||= Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0).tap do |s|
68
+ s.connect Socket.sockaddr_in(@port, @host)
69
+ end
70
+ rescue Errno::ECONNREFUSED => e
71
+ raise ConnectionError, "Cannot establish a connection to #{@host} on port #{@port}"
72
+ end
73
+
74
+ def auth_methods
75
+ %w(publickey hostbased password)
76
+ end
77
+
78
+ def authlist_for(username)
79
+ native_userauth_list(username).split ','
80
+ end
81
+
82
+ def authenticate_via_password(username, options)
83
+ return unless options[:password]
84
+ block { native_userauth_password(username, options[:password]) } == 0
85
+ end
86
+
87
+ def authenticate_via_publickey(username, options)
88
+ return unless options[:keys] && options[:keys].any?
89
+ # Not implemented yet
90
+ end
91
+
92
+ def authenticate_via_hostbased(username, options)
93
+ # Not implemented yet
94
+ end
95
+
96
+ def block
97
+ retval = yield
98
+
99
+ until retval != ERROR_EAGAIN
100
+ io_select
101
+ retval = yield
102
+ end
103
+
104
+ retval
105
+ end
106
+ end
107
+ end
@@ -0,0 +1,49 @@
1
+ require 'digest/sha1'
2
+
3
+ module MallCop
4
+ class Shell
5
+ attr_reader :channel
6
+
7
+ def initialize(session)
8
+ @session = session
9
+ @channel = session.open_channel
10
+
11
+ @channel.shell
12
+ end
13
+
14
+ def sh(cmd)
15
+ res, code = exec_with_status(cmd)
16
+ res
17
+ end
18
+
19
+ def cleanup
20
+ @channel.close
21
+ end
22
+
23
+ private
24
+
25
+ def exec_with_status(cmd)
26
+ separator = generate_separator
27
+ # An `echo ''` is appended for now, mostly because I don't know of
28
+ # a better way to do this.
29
+ cmd = cmd.dup.strip
30
+ cmd << ";" if cmd !~ /[;&]$/
31
+ cmd << %[ echo #{separator} $?\n]
32
+
33
+ @channel.write(cmd)
34
+
35
+ res = ""
36
+ until res =~ /#{separator} (\d+)\Z/m
37
+ res << @channel.read
38
+ end
39
+
40
+ return $`.strip, $1.to_i
41
+ end
42
+
43
+ def generate_separator
44
+ s = Digest::SHA1.hexdigest([channel.object_id, object_id, Time.now.to_i, Time.now.usec, rand(0xFFFFFFFF)].join(":"))
45
+ s << Digest::SHA1.hexdigest(s)
46
+ end
47
+
48
+ end
49
+ end
metadata ADDED
@@ -0,0 +1,81 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mallcop
3
+ version: !ruby/object:Gem::Version
4
+ hash: 29
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 1
10
+ version: 0.0.1
11
+ platform: ruby
12
+ authors:
13
+ - Carl Lerche
14
+ - Aaron Patterson
15
+ autorequire:
16
+ bindir: bin
17
+ cert_chain: []
18
+
19
+ date: 2010-08-18 00:00:00 -07:00
20
+ default_executable:
21
+ dependencies: []
22
+
23
+ description: Don't reinvent the wheel.
24
+ email:
25
+ - me@carllerche.com
26
+ executables: []
27
+
28
+ extensions:
29
+ - ext/mallcop/extconf.rb
30
+ extra_rdoc_files: []
31
+
32
+ files:
33
+ - ext/mallcop/extconf.rb
34
+ - ext/mallcop/channel.c
35
+ - ext/mallcop/mallcop.c
36
+ - ext/mallcop/session.c
37
+ - ext/mallcop/mallcop.h
38
+ - lib/mallcop.rb
39
+ - lib/mallcop/channel.rb
40
+ - lib/mallcop/session.rb
41
+ - lib/mallcop/shell.rb
42
+ - README.md
43
+ - LICENSE
44
+ has_rdoc: true
45
+ homepage: http://github.com/carllerche/mallcop
46
+ licenses: []
47
+
48
+ post_install_message:
49
+ rdoc_options: []
50
+
51
+ require_paths:
52
+ - lib
53
+ required_ruby_version: !ruby/object:Gem::Requirement
54
+ none: false
55
+ requirements:
56
+ - - ">="
57
+ - !ruby/object:Gem::Version
58
+ hash: 3
59
+ segments:
60
+ - 0
61
+ version: "0"
62
+ required_rubygems_version: !ruby/object:Gem::Requirement
63
+ none: false
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ hash: 23
68
+ segments:
69
+ - 1
70
+ - 3
71
+ - 6
72
+ version: 1.3.6
73
+ requirements: []
74
+
75
+ rubyforge_project: mallcop
76
+ rubygems_version: 1.3.7
77
+ signing_key:
78
+ specification_version: 3
79
+ summary: Bindings to libssh2
80
+ test_files: []
81
+