pipe2me-client 0.2.8 → 0.2.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +29 -29
- data/lib/pipe2me/bin/natpmpc +337 -0
- data/lib/pipe2me/cli-foreman.rb +7 -3
- data/lib/pipe2me/cli-monit.rb +18 -12
- data/lib/pipe2me/cli.rb +14 -14
- data/lib/pipe2me/config.rb +1 -1
- data/lib/pipe2me/ext/http.rb +2 -2
- data/lib/pipe2me/ext/sys.rb +7 -7
- data/lib/pipe2me/tunnel/commands.rb +17 -8
- data/lib/pipe2me/tunnel/echo/http +1 -1
- data/lib/pipe2me/tunnel.rb +1 -1
- data/lib/pipe2me/version.rb +2 -2
- data/test/env-test.sh +1 -1
- data/test/monitrc-test.sh +1 -1
- data/test/opensslkey-test.sh +1 -1
- data/test/redirection-test.sh +1 -1
- data/test/setup-test.sh +5 -3
- data/test/sshkey-test.sh +1 -1
- data/test/testhelper.inc +1 -1
- data/test/testhelper.release +1 -1
- data/test/verify-test.sh +3 -3
- metadata +5 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 8eceaa5c69187b15f7b43b0fe2cdbe1dd9b7281f
|
|
4
|
+
data.tar.gz: ca812510cb4937691876f1fc8b5642b50cea853d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ee65b0e72ff5c66bb7bca14a7c2724a0e7f1717b33696d0b9aaf1aa1f7a87b1a8ea78cffab3f4d08df416a6dcf17c1f72d13db195a3be7037da69194193dedfe
|
|
7
|
+
data.tar.gz: 7fa92f1490c22365e4a8ca87f0ed4480429923694ba24d6c2ac897db6f37162ca64b1fc7493b67698342951d881364386aa6761d67afcc97cd958f127247dd90
|
data/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# pipe2me client
|
|
2
2
|
|
|
3
|
-
This is the ruby client for the pipe2me server package.
|
|
3
|
+
This is the ruby client for the pipe2me server package.
|
|
4
4
|
The pipe2me client lets you publish local services to the public internet
|
|
5
5
|
with the help and orchestration of a pipe2me server. For more details
|
|
6
6
|
see [pipe2me](https://github.com/kinko/pipe2me)
|
|
@@ -15,7 +15,7 @@ To install it run
|
|
|
15
15
|
sudo cp doc/pipe2me.1 /usr/local/share/man/man1
|
|
16
16
|
|
|
17
17
|
If you are managing a box with a system-wide ruby installation you must install
|
|
18
|
-
it via
|
|
18
|
+
it via
|
|
19
19
|
|
|
20
20
|
sudo gem install pipe2me-client
|
|
21
21
|
|
|
@@ -25,13 +25,13 @@ Verify the installation with
|
|
|
25
25
|
|
|
26
26
|
## Usage
|
|
27
27
|
|
|
28
|
-
Mini-example: This registers two tunnels with a single hostname for
|
|
29
|
-
two services on localhost (http on port 9090, https on port 9091).
|
|
28
|
+
Mini-example: This registers two tunnels with a single hostname for
|
|
29
|
+
two services on localhost (http on port 9090, https on port 9091).
|
|
30
30
|
|
|
31
31
|
<pre>
|
|
32
32
|
# Setup tunnels. This responds with the domain name
|
|
33
33
|
> <b>pipe2me setup --protocols http,https \
|
|
34
|
-
--server http://test.pipe2.me
|
|
34
|
+
--server http://test.pipe2.me \
|
|
35
35
|
--token review@pipe2me --ports 9090,9091</b>
|
|
36
36
|
pretty-ivory-horse.test.pipe2.me
|
|
37
37
|
|
|
@@ -44,31 +44,31 @@ PIPE2ME_URLS_1=https://pretty-ivory-horse.test.pipe2.me:10004
|
|
|
44
44
|
> <b>pipe2me start</b>
|
|
45
45
|
</pre>
|
|
46
46
|
|
|
47
|
-
See also the [example session](http://test.pipe2.me/example_session.html)
|
|
47
|
+
See also the [example session](http://test.pipe2.me/example_session.html)
|
|
48
48
|
and the [man page](http://test.pipe2.me/pipe2me.1.html).
|
|
49
49
|
|
|
50
50
|
## Testing
|
|
51
51
|
|
|
52
|
-
Tests are implemented using
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
52
|
+
Tests are implemented using roundup.
|
|
53
|
+
To install roundup on OSX, run `brew install roundup`. Other systems are
|
|
54
|
+
supported as well, [compare roundup's documentation](https://github.com/bmizerany/roundup/blob/master/INSTALLING#files)
|
|
55
|
+
for details.
|
|
56
56
|
|
|
57
|
-
The implemented tests are *integration tests* in the sense, that they test the
|
|
58
|
-
behaviour of the *pipe2me-client* package in connection to an external pipe2me
|
|
59
|
-
server. That means you should run a pipe2me server on your local machine. Note
|
|
57
|
+
The implemented tests are *integration tests* in the sense, that they test the
|
|
58
|
+
behaviour of the *pipe2me-client* package in connection to an external pipe2me
|
|
59
|
+
server. That means you should run a pipe2me server on your local machine. Note
|
|
60
60
|
that the server must be configured to support test mode. (In test mode a pipe2me
|
|
61
|
-
server accepts test auth tokens, that create short lived tunnels
|
|
62
|
-
with self-signed certificates.)
|
|
61
|
+
server accepts test auth tokens, that create short lived tunnels
|
|
62
|
+
with self-signed certificates.)
|
|
63
63
|
|
|
64
64
|
To run the tests against a locally installed test server run `rake`. Note that
|
|
65
65
|
the local test server is expected at "pipe2.dev:8080". You might have to adjust
|
|
66
|
-
the `/etc/hosts` file to add an entry
|
|
66
|
+
the `/etc/hosts` file to add an entry
|
|
67
67
|
|
|
68
68
|
127.0.0.1 pipe2.dev
|
|
69
69
|
|
|
70
|
-
Before submitting a pull request you should also run a test against the
|
|
71
|
-
test.pipe2.me instance, which is available most of the time for that purpose.
|
|
70
|
+
Before submitting a pull request you should also run a test against the
|
|
71
|
+
test.pipe2.me instance, which is available most of the time for that purpose.
|
|
72
72
|
To do that, run `rake test:release`.
|
|
73
73
|
|
|
74
74
|
## Tunnel tokens
|
|
@@ -76,9 +76,9 @@ To do that, run `rake test:release`.
|
|
|
76
76
|
As ports and domain names are sparse resources the pipe2me server API
|
|
77
77
|
requires the use of authorization tokens when requesting a tunnel. A
|
|
78
78
|
token is similar to a 'currency' in that it describes which tunnels are
|
|
79
|
-
supported. A token could limit the number of ports that can be tunnelled,
|
|
80
|
-
the amount of traffic for those ports, whether or not a certificate is
|
|
81
|
-
self-signed or signed by a regular CA, etc.
|
|
79
|
+
supported. A token could limit the number of ports that can be tunnelled,
|
|
80
|
+
the amount of traffic for those ports, whether or not a certificate is
|
|
81
|
+
self-signed or signed by a regular CA, etc.
|
|
82
82
|
|
|
83
83
|
|
|
84
84
|
The features of a token are not defined within this protocol. However,
|
|
@@ -87,24 +87,24 @@ to know more.
|
|
|
87
87
|
|
|
88
88
|
A freshly installed pipe2me server comes with a number of preconfigured tokens.
|
|
89
89
|
Of course, a server admin should (and probably would) change those tokens.
|
|
90
|
-
However, as a test target, the pipe2me test server at http://test.pipe2.me
|
|
91
|
-
supports these tokens:
|
|
90
|
+
However, as a test target, the pipe2me test server at http://test.pipe2.me
|
|
91
|
+
supports these tokens:
|
|
92
92
|
|
|
93
93
|
- `test@pipe2me`: this builds tunnels that are available for 5 minutes.
|
|
94
94
|
The test token is intended for use with automated test scenarios.
|
|
95
|
-
- `review@pipe2me`: this builds tunnels that are available for up to
|
|
95
|
+
- `review@pipe2me`: this builds tunnels that are available for up to
|
|
96
96
|
one day. A review token should help you get a feel for the pipe2me
|
|
97
|
-
package.
|
|
97
|
+
package.
|
|
98
98
|
|
|
99
|
-
If you need a longer lived token for development, review and/or test
|
|
100
|
-
feel free to contact us at contact@kinko.me.
|
|
99
|
+
If you need a longer lived token for development, review and/or test
|
|
100
|
+
feel free to contact us at contact@kinko.me.
|
|
101
101
|
|
|
102
102
|
## Licensing
|
|
103
103
|
|
|
104
|
-
**The pipe2me client software** is (c) The Kinko Team, 2014 and released to you
|
|
104
|
+
**The pipe2me client software** is (c) The Kinko Team, 2014 and released to you
|
|
105
105
|
under the terms of the MIT License (MIT), see COPYING.MIT for details.
|
|
106
106
|
|
|
107
107
|
The subdirectory lib/vendor contains third-party code, which is subject to its own copyrights.
|
|
108
108
|
Please see the respective source files for copyright information.
|
|
109
|
-
|
|
109
|
+
|
|
110
110
|
(c) The kinko team, 2014
|
|
@@ -0,0 +1,337 @@
|
|
|
1
|
+
#!/usr/bin/env jit cc -g -Werror -Wall -lnatpmp --
|
|
2
|
+
|
|
3
|
+
/* $Id: natpmpc.c,v 1.13 2012/08/21 17:23:38 nanard Exp $ */
|
|
4
|
+
/* libnatpmp
|
|
5
|
+
Copyright (c) 2007-2011, Thomas BERNARD
|
|
6
|
+
All rights reserved.
|
|
7
|
+
|
|
8
|
+
Redistribution and use in source and binary forms, with or without
|
|
9
|
+
modification, are permitted provided that the following conditions are met:
|
|
10
|
+
|
|
11
|
+
* Redistributions of source code must retain the above copyright notice,
|
|
12
|
+
this list of conditions and the following disclaimer.
|
|
13
|
+
* Redistributions in binary form must reproduce the above copyright notice,
|
|
14
|
+
this list of conditions and the following disclaimer in the documentation
|
|
15
|
+
and/or other materials provided with the distribution.
|
|
16
|
+
* The name of the author may not be used to endorse or promote products
|
|
17
|
+
derived from this software without specific prior written permission.
|
|
18
|
+
|
|
19
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
20
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
21
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
22
|
+
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
23
|
+
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
24
|
+
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
25
|
+
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
26
|
+
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
27
|
+
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
28
|
+
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
29
|
+
POSSIBILITY OF SUCH DAMAGE.
|
|
30
|
+
*/
|
|
31
|
+
#include <stdio.h>
|
|
32
|
+
#include <stdlib.h>
|
|
33
|
+
#include <errno.h>
|
|
34
|
+
#include <string.h>
|
|
35
|
+
#if defined(_MSC_VER)
|
|
36
|
+
#if _MSC_VER >= 1400
|
|
37
|
+
#define strcasecmp _stricmp
|
|
38
|
+
#else
|
|
39
|
+
#define strcasecmp stricmp
|
|
40
|
+
#endif
|
|
41
|
+
#else
|
|
42
|
+
#include <unistd.h>
|
|
43
|
+
#endif
|
|
44
|
+
#ifdef WIN32
|
|
45
|
+
#include <winsock2.h>
|
|
46
|
+
#else
|
|
47
|
+
#include <netinet/in.h>
|
|
48
|
+
#include <arpa/inet.h>
|
|
49
|
+
#endif
|
|
50
|
+
|
|
51
|
+
// #define ENABLE_STRNATPMPERR
|
|
52
|
+
|
|
53
|
+
#include "natpmp.h"
|
|
54
|
+
|
|
55
|
+
static const char * argv0;
|
|
56
|
+
static FILE* debug;
|
|
57
|
+
|
|
58
|
+
static void usage()
|
|
59
|
+
{
|
|
60
|
+
fprintf(stderr, "Usage :\n");
|
|
61
|
+
fprintf(stderr, " %s [options]\n", argv0);
|
|
62
|
+
fprintf(stderr, "\tdisplay the public IP address.\n");
|
|
63
|
+
fprintf(stderr, " %s [options] [protcol:]<public port>[:<private port>] <public port>[:<private port>] ... \n", argv0);
|
|
64
|
+
fprintf(stderr, "\tadd map one or more ports. protocol is 'tcp' or 'udp'.\n");
|
|
65
|
+
fprintf(stderr, "\nOption available :\n");
|
|
66
|
+
fprintf(stderr, " -g ipv4address\n\tforce the gateway to be used as destination for NAT-PMP commands.\n");
|
|
67
|
+
fprintf(stderr, " -l lifetime\n\tset lifetime for port mapping.\n");
|
|
68
|
+
fprintf(stderr, " -v\n\tbe verbose.\n");
|
|
69
|
+
fprintf(stderr, "\n In order to remove a mapping, set it with a lifetime of 0 seconds.\n");
|
|
70
|
+
fprintf(stderr, " To remove all mappings for your machine, use 0 as private port and lifetime.\n");
|
|
71
|
+
|
|
72
|
+
exit(1);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
#define NO 0
|
|
76
|
+
#define YES 1
|
|
77
|
+
|
|
78
|
+
typedef struct {
|
|
79
|
+
int protocol;
|
|
80
|
+
uint16_t privateport;
|
|
81
|
+
uint16_t publicport;
|
|
82
|
+
uint32_t lifetime;
|
|
83
|
+
} port_mapping_t;
|
|
84
|
+
|
|
85
|
+
#define MAX_PORT_MAPPINGS 32
|
|
86
|
+
|
|
87
|
+
typedef struct {
|
|
88
|
+
int verbose;
|
|
89
|
+
int gateway;
|
|
90
|
+
port_mapping_t port_mappings[MAX_PORT_MAPPINGS];
|
|
91
|
+
unsigned port_mappings_count;
|
|
92
|
+
} arguments_t;
|
|
93
|
+
|
|
94
|
+
/*
|
|
95
|
+
* == Argument parsing ========================================================
|
|
96
|
+
*/
|
|
97
|
+
|
|
98
|
+
static void parse_port_mapping(const char* s, port_mapping_t* p_port_mapping) {
|
|
99
|
+
p_port_mapping->publicport = 0;
|
|
100
|
+
p_port_mapping->privateport = 0;
|
|
101
|
+
p_port_mapping->protocol = NATPMP_PROTOCOL_TCP;
|
|
102
|
+
|
|
103
|
+
if(0 == strncasecmp(s, "tcp:", 4)) {
|
|
104
|
+
s += 4;
|
|
105
|
+
}
|
|
106
|
+
else if(0 == strncasecmp(s, "udp:", 4)) {
|
|
107
|
+
p_port_mapping->protocol = NATPMP_PROTOCOL_UDP;
|
|
108
|
+
s += 4;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if(2 == sscanf(s, "%hu:%hu", &p_port_mapping->publicport, &p_port_mapping->privateport)) {
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if(1 == sscanf(s, "%hu", &p_port_mapping->publicport)) {
|
|
116
|
+
p_port_mapping->privateport = p_port_mapping->publicport;
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
fprintf(stderr, "Cannot parse port mapping '%s'\n", s);
|
|
121
|
+
exit(1);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
static void parse_args(const char** argv, arguments_t* args) {
|
|
125
|
+
in_addr_t lifetime = 3600;
|
|
126
|
+
|
|
127
|
+
args->verbose = NO;
|
|
128
|
+
args->gateway = 0;
|
|
129
|
+
args->port_mappings_count = 0;
|
|
130
|
+
|
|
131
|
+
/* argument parsing */
|
|
132
|
+
while(*++argv) {
|
|
133
|
+
if(*argv[0] == '-') {
|
|
134
|
+
switch((*argv)[1]) {
|
|
135
|
+
case 'g':
|
|
136
|
+
if(!*++argv) usage();
|
|
137
|
+
args->gateway = inet_addr(*argv);
|
|
138
|
+
break;
|
|
139
|
+
case 'l':
|
|
140
|
+
if(!*++argv) usage();
|
|
141
|
+
lifetime = (unsigned)strtoul(*argv, (char **)NULL, 10);
|
|
142
|
+
break;
|
|
143
|
+
case 'v':
|
|
144
|
+
args->verbose = YES;
|
|
145
|
+
break;
|
|
146
|
+
default:
|
|
147
|
+
usage();
|
|
148
|
+
};
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
if(args->port_mappings_count == MAX_PORT_MAPPINGS) {
|
|
153
|
+
fprintf(stderr, "Cannot map more than %d ports at once.\n", MAX_PORT_MAPPINGS);
|
|
154
|
+
exit(1);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
port_mapping_t* port_mapping = args->port_mappings + args->port_mappings_count;
|
|
158
|
+
parse_port_mapping(*argv, port_mapping);
|
|
159
|
+
port_mapping->lifetime = lifetime;
|
|
160
|
+
args->port_mappings_count++;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
#define MAX_TRIES 6
|
|
165
|
+
|
|
166
|
+
/*
|
|
167
|
+
* == NAT-PMP high level interface ============================================
|
|
168
|
+
*/
|
|
169
|
+
|
|
170
|
+
static int natpmp_init(natpmp_t* natpmp, arguments_t* args)
|
|
171
|
+
{
|
|
172
|
+
int r = initnatpmp(natpmp, args->gateway != 0, args->gateway);
|
|
173
|
+
fprintf(debug, "initnatpmp() returned %d (%s)\n", r, r?"FAILED":"SUCCESS");
|
|
174
|
+
if(r < 0) return NO;
|
|
175
|
+
|
|
176
|
+
/* log gateway */
|
|
177
|
+
struct in_addr gateway_in_use;
|
|
178
|
+
gateway_in_use.s_addr = natpmp->gateway;
|
|
179
|
+
fprintf(debug, "using NAT-PMP gateway : %s\n", inet_ntoa(gateway_in_use));
|
|
180
|
+
|
|
181
|
+
return YES;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
static void natpmp_close(natpmp_t* natpmp)
|
|
185
|
+
{
|
|
186
|
+
int r = closenatpmp(natpmp);
|
|
187
|
+
fprintf(debug, "closenatpmp() returned %d (%s)\n", r, r==0?"SUCCESS":"FAILED");
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
static natpmpresp_t* natpmp_wait_for_success_response(natpmp_t* natpmp, natpmpresp_t* response) {
|
|
191
|
+
int remaining_tries = MAX_TRIES;
|
|
192
|
+
|
|
193
|
+
while(remaining_tries > 0) {
|
|
194
|
+
fd_set fds;
|
|
195
|
+
struct timeval timeout;
|
|
196
|
+
|
|
197
|
+
FD_ZERO(&fds);
|
|
198
|
+
FD_SET(natpmp->s, &fds);
|
|
199
|
+
getnatpmprequesttimeout(natpmp, &timeout);
|
|
200
|
+
|
|
201
|
+
if(0 > select(FD_SETSIZE, &fds, NULL, NULL, &timeout)) {
|
|
202
|
+
perror("select()");
|
|
203
|
+
return 0;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
int r = readnatpmpresponseorretry(natpmp, response);
|
|
207
|
+
int sav_errno = errno;
|
|
208
|
+
fprintf(debug, "readnatpmpresponseorretry returned %d (%s)\n", r, r==0?"OK":(r==NATPMP_TRYAGAIN?"TRY AGAIN":"FAILED"));
|
|
209
|
+
if(r == 0) {
|
|
210
|
+
fprintf(debug, "epoch = %u\n", response->epoch);
|
|
211
|
+
return response;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
if(r != NATPMP_TRYAGAIN) {
|
|
215
|
+
#ifdef ENABLE_STRNATPMPERR
|
|
216
|
+
fprintf(debug, "readnatpmpresponseorretry() failed : %s\n", strnatpmperr(r));
|
|
217
|
+
#endif
|
|
218
|
+
fprintf(debug, " errno=%d '%s'\n", sav_errno, strerror(sav_errno));
|
|
219
|
+
return 0;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
fprintf(stderr, "Giving up after %d tries.\n", MAX_TRIES);
|
|
224
|
+
return 0;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
static const char* natpmp_public_address(natpmp_t* natpmp, char* publicaddress)
|
|
228
|
+
{
|
|
229
|
+
int r = sendpublicaddressrequest(natpmp);
|
|
230
|
+
fprintf(debug, "sendpublicaddressrequest returned %d (%s)\n", r, r==2?"SUCCESS":"FAILED");
|
|
231
|
+
if(r<0) return 0;
|
|
232
|
+
|
|
233
|
+
natpmpresp_t response_buf;
|
|
234
|
+
natpmpresp_t *response = natpmp_wait_for_success_response(natpmp, &response_buf);
|
|
235
|
+
if(!response)
|
|
236
|
+
return 0;
|
|
237
|
+
|
|
238
|
+
/* TODO : check that response.type == 0 */
|
|
239
|
+
fprintf(debug, "Public IP address : %s\n", inet_ntoa(response->pnu.publicaddress.addr));
|
|
240
|
+
sprintf(publicaddress, "%s", inet_ntoa(response->pnu.publicaddress.addr));
|
|
241
|
+
|
|
242
|
+
return publicaddress;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
static int natpmp_map_port(natpmp_t* natpmp, port_mapping_t* port_mapping, unsigned* lifetime) {
|
|
246
|
+
int r = sendnewportmappingrequest(natpmp, port_mapping->protocol,
|
|
247
|
+
port_mapping->privateport, port_mapping->publicport,
|
|
248
|
+
port_mapping->lifetime);
|
|
249
|
+
fprintf(debug, "sendnewportmappingrequest returned %d (%s)\n",
|
|
250
|
+
r, r==12?"SUCCESS":"FAILED");
|
|
251
|
+
if(r < 0)
|
|
252
|
+
return NO;
|
|
253
|
+
|
|
254
|
+
natpmpresp_t response_buf;
|
|
255
|
+
natpmpresp_t *response = natpmp_wait_for_success_response(natpmp, &response_buf);
|
|
256
|
+
if(!response) return NO;
|
|
257
|
+
|
|
258
|
+
*lifetime = response->pnu.newportmapping.lifetime;
|
|
259
|
+
return YES;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/*
|
|
263
|
+
* == main ====================================================================
|
|
264
|
+
*/
|
|
265
|
+
|
|
266
|
+
int main(int argc, const char ** argv)
|
|
267
|
+
{
|
|
268
|
+
|
|
269
|
+
#ifdef WIN32
|
|
270
|
+
WSADATA wsaData;
|
|
271
|
+
int nResult = WSAStartup(MAKEWORD(2,2), &wsaData);
|
|
272
|
+
if(nResult != NO_ERROR)
|
|
273
|
+
{
|
|
274
|
+
fprintf(stderr, "WSAStartup() failed.\n");
|
|
275
|
+
return -1;
|
|
276
|
+
}
|
|
277
|
+
#endif
|
|
278
|
+
|
|
279
|
+
argv0 = argv[0];
|
|
280
|
+
arguments_t args;
|
|
281
|
+
|
|
282
|
+
parse_args(argv, &args);
|
|
283
|
+
|
|
284
|
+
debug = args.verbose ? stderr : fopen("/dev/null", "w");
|
|
285
|
+
|
|
286
|
+
// if(args.verbose) {
|
|
287
|
+
// unsigned i = 0;
|
|
288
|
+
// for(i = 0; i<args.port_mappings_count; ++i) {
|
|
289
|
+
// port_mapping_t* mapping = args.port_mappings + i;
|
|
290
|
+
//
|
|
291
|
+
// const char* protocol = mapping->protocol == NATPMP_PROTOCOL_UDP ? "udp" : "tcp";
|
|
292
|
+
// fprintf(stderr, "%s: public:%hu -> localhost:%hu: pending (lifetime %u)\n",
|
|
293
|
+
// protocol, mapping->publicport, mapping->privateport, mapping->lifetime);
|
|
294
|
+
// }
|
|
295
|
+
// }
|
|
296
|
+
|
|
297
|
+
/*
|
|
298
|
+
* Try to get NAT-PMP public IP. We use that to decide whether we are
|
|
299
|
+
* going NAT-PMP or UPnP.
|
|
300
|
+
*/
|
|
301
|
+
|
|
302
|
+
natpmp_t natpmp;
|
|
303
|
+
|
|
304
|
+
if(!natpmp_init(&natpmp, &args)) {
|
|
305
|
+
fprintf(stderr, "Cannot initialize NAT-PMP.\n");
|
|
306
|
+
return 1;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
char publicaddress_buf[32];
|
|
310
|
+
const char* publicaddress = natpmp_public_address(&natpmp, publicaddress_buf);
|
|
311
|
+
if(!publicaddress) {
|
|
312
|
+
fprintf(stderr, "NAT-PMP: Cannot find public IP address. NAT-PMP is not available.\n");
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
if(publicaddress) {
|
|
316
|
+
if(args.port_mappings_count == 0)
|
|
317
|
+
printf("%s\n", publicaddress);
|
|
318
|
+
|
|
319
|
+
unsigned i = 0;
|
|
320
|
+
for(i = 0; i<args.port_mappings_count; ++i) {
|
|
321
|
+
port_mapping_t* mapping = args.port_mappings + i;
|
|
322
|
+
|
|
323
|
+
unsigned lifetime = 0;
|
|
324
|
+
int r = natpmp_map_port(&natpmp, mapping, &lifetime);
|
|
325
|
+
|
|
326
|
+
const char* protocol = mapping->protocol == NATPMP_PROTOCOL_UDP ? "udp" : "tcp";
|
|
327
|
+
fprintf(stderr, "NAT-PMP: %s: %s:%hu -> localhost:%hu: %s (lifetime %u)\n",
|
|
328
|
+
protocol, publicaddress, mapping->publicport, mapping->privateport, (r ? "OK" : "FAIL"), lifetime);
|
|
329
|
+
|
|
330
|
+
if(!r) break;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
natpmp_close(&natpmp);
|
|
335
|
+
return 0;
|
|
336
|
+
}
|
|
337
|
+
|
data/lib/pipe2me/cli-foreman.rb
CHANGED
|
@@ -3,16 +3,20 @@ class Pipe2me::CLI < Thor
|
|
|
3
3
|
option :echo, :type => :boolean, :banner => "Also run echo servers"
|
|
4
4
|
def start
|
|
5
5
|
handle_global_options
|
|
6
|
-
|
|
6
|
+
|
|
7
7
|
procfile = options[:echo] ? "pipe2me.procfile.echo" : "pipe2me.procfile"
|
|
8
8
|
|
|
9
9
|
File.open procfile, "w" do |io|
|
|
10
10
|
Pipe2me::Tunnel.tunnel_commands.each do |name, cmd|
|
|
11
11
|
io.write "#{name}: #{cmd}\n"
|
|
12
12
|
end
|
|
13
|
-
|
|
13
|
+
|
|
14
|
+
# Pipe2me::Tunnel.mapping_commands.each do |name, cmd|
|
|
15
|
+
# io.write "#{name}: #{cmd}\n"
|
|
16
|
+
# end
|
|
17
|
+
|
|
14
18
|
next unless options[:echo]
|
|
15
|
-
|
|
19
|
+
|
|
16
20
|
Pipe2me::Tunnel.echo_commands.each do |name, cmd|
|
|
17
21
|
io.write "#{name}: #{cmd}\n"
|
|
18
22
|
end
|
data/lib/pipe2me/cli-monit.rb
CHANGED
|
@@ -4,32 +4,37 @@ class Pipe2me::CLI < Thor
|
|
|
4
4
|
option :echo, :type => :boolean, :banner => "Also run echo servers"
|
|
5
5
|
def monit(*args)
|
|
6
6
|
handle_global_options
|
|
7
|
-
|
|
7
|
+
|
|
8
8
|
monitrc_file = create_monitrc
|
|
9
|
-
|
|
9
|
+
|
|
10
10
|
UI.warn "Running: monit -c #{monitrc_file} #{args.join(" ")}"
|
|
11
11
|
Kernel.exec "monit", "-c", monitrc_file, *args
|
|
12
12
|
end
|
|
13
|
-
|
|
13
|
+
|
|
14
14
|
desc "monitrc", "Create monitrc file"
|
|
15
15
|
option :port, :default => 5555, :banner => "control port"
|
|
16
16
|
option :echo, :type => :boolean, :banner => "Also run echo servers"
|
|
17
17
|
def monitrc
|
|
18
18
|
handle_global_options
|
|
19
|
-
|
|
19
|
+
|
|
20
20
|
monitrc_file = create_monitrc
|
|
21
21
|
Kernel.exec "monit", "-c", monitrc_file, "-t"
|
|
22
22
|
end
|
|
23
23
|
|
|
24
24
|
private
|
|
25
|
-
|
|
25
|
+
|
|
26
|
+
def which!(cmd)
|
|
27
|
+
path = `which #{cmd}`.chomp
|
|
28
|
+
raise "Cannot find #{cmd} in your $PATH. Is it installed?" if path == ""
|
|
29
|
+
path
|
|
30
|
+
end
|
|
31
|
+
|
|
26
32
|
def create_monitrc
|
|
27
33
|
path = options[:echo] ? "pipe2me.monitrc.echo" : "pipe2me.monitrc"
|
|
28
34
|
|
|
29
35
|
# The daemon binary
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
36
|
+
daemon = "#{which! :daemon} -D #{File.expand_path(Dir.getwd)}"
|
|
37
|
+
|
|
33
38
|
port = options[:port]
|
|
34
39
|
|
|
35
40
|
logfile = File.expand_path "pipe2me.monit.log"
|
|
@@ -37,22 +42,23 @@ class Pipe2me::CLI < Thor
|
|
|
37
42
|
FileUtils.mkdir_p piddir
|
|
38
43
|
|
|
39
44
|
commands = Pipe2me::Tunnel.tunnel_commands
|
|
45
|
+
# commands += Pipe2me::Tunnel.mapping_commands
|
|
40
46
|
commands += Pipe2me::Tunnel.echo_commands if options[:echo]
|
|
41
|
-
|
|
47
|
+
|
|
42
48
|
File.open path, "w", 0600 do |io|
|
|
43
49
|
require "erb"
|
|
44
50
|
|
|
45
51
|
erb = ERB.new MONITRC_ERB
|
|
46
52
|
io.write erb.result(self.send(:binding))
|
|
47
53
|
end
|
|
48
|
-
|
|
54
|
+
|
|
49
55
|
UI.success "Created #{path}"
|
|
50
|
-
|
|
56
|
+
|
|
51
57
|
path
|
|
52
58
|
end
|
|
53
59
|
|
|
54
60
|
MONITRC_ERB = %q{
|
|
55
|
-
set daemon 10
|
|
61
|
+
set daemon 10
|
|
56
62
|
set httpd port <%= port %> and use address localhost allow localhost
|
|
57
63
|
|
|
58
64
|
<% commands.each do |name, cmd| %>
|
data/lib/pipe2me/cli.rb
CHANGED
|
@@ -2,12 +2,12 @@ require "thor"
|
|
|
2
2
|
|
|
3
3
|
class Pipe2me::CLI < Thor
|
|
4
4
|
class_option :dir, :type => :string
|
|
5
|
-
class_option :verbose, :type => :boolean, :aliases =>
|
|
6
|
-
class_option :quiet, :type => :boolean, :aliases =>
|
|
5
|
+
class_option :verbose, :type => :boolean, :aliases => "-v"
|
|
6
|
+
class_option :quiet, :type => :boolean, :aliases => "-q"
|
|
7
7
|
class_option :silent, :type => :boolean
|
|
8
|
-
|
|
8
|
+
|
|
9
9
|
private
|
|
10
|
-
|
|
10
|
+
|
|
11
11
|
def handle_global_options
|
|
12
12
|
UI.verbosity = 2
|
|
13
13
|
if options[:verbose]
|
|
@@ -25,24 +25,24 @@ class Pipe2me::CLI < Thor
|
|
|
25
25
|
end
|
|
26
26
|
|
|
27
27
|
public
|
|
28
|
-
|
|
28
|
+
|
|
29
29
|
def self.exit_on_failure?; true; end
|
|
30
|
-
|
|
30
|
+
|
|
31
31
|
desc "version", "print version information"
|
|
32
32
|
def version
|
|
33
33
|
handle_global_options
|
|
34
|
-
|
|
34
|
+
|
|
35
35
|
puts Pipe2me::VERSION
|
|
36
36
|
end
|
|
37
37
|
|
|
38
38
|
desc "setup", "fetch a new tunnel setup"
|
|
39
|
-
option :server, :aliases =>
|
|
39
|
+
option :server, :aliases => "-s", :default => "http://test.pipe2.me"
|
|
40
40
|
option :token, :required => true # "tunnel token"
|
|
41
41
|
option :protocols, :default => "https" # "protocol names, e.g. 'http,https,imap'"
|
|
42
42
|
option :ports, :type => :string # "local ports, one per protocol"
|
|
43
43
|
def setup
|
|
44
44
|
handle_global_options
|
|
45
|
-
|
|
45
|
+
|
|
46
46
|
Pipe2me::Config.server = options[:server]
|
|
47
47
|
server_info = Pipe2me::Tunnel.setup options
|
|
48
48
|
|
|
@@ -53,7 +53,7 @@ class Pipe2me::CLI < Thor
|
|
|
53
53
|
desc "env", "show tunnel configuration"
|
|
54
54
|
def env(*args)
|
|
55
55
|
handle_global_options
|
|
56
|
-
|
|
56
|
+
|
|
57
57
|
puts File.read("pipe2me.local.inc")
|
|
58
58
|
puts File.read("pipe2me.info.inc")
|
|
59
59
|
end
|
|
@@ -61,21 +61,21 @@ class Pipe2me::CLI < Thor
|
|
|
61
61
|
desc "verify", "Verify the tunnel"
|
|
62
62
|
def verify
|
|
63
63
|
handle_global_options
|
|
64
|
-
|
|
64
|
+
|
|
65
65
|
Pipe2me::Tunnel.verify
|
|
66
66
|
end
|
|
67
67
|
|
|
68
68
|
desc "update", "Updates configuration"
|
|
69
69
|
def update
|
|
70
70
|
handle_global_options
|
|
71
|
-
|
|
71
|
+
|
|
72
72
|
Pipe2me::Tunnel.update
|
|
73
73
|
end
|
|
74
|
-
|
|
74
|
+
|
|
75
75
|
desc "check", "check online status"
|
|
76
76
|
def check
|
|
77
77
|
handle_global_options
|
|
78
|
-
|
|
78
|
+
|
|
79
79
|
Pipe2me::Tunnel.check
|
|
80
80
|
end
|
|
81
81
|
end
|
data/lib/pipe2me/config.rb
CHANGED
data/lib/pipe2me/ext/http.rb
CHANGED
|
@@ -21,10 +21,10 @@ module Pipe2me::HTTP
|
|
|
21
21
|
end
|
|
22
22
|
|
|
23
23
|
extend Forwardable
|
|
24
|
-
delegate [ :code, :url ] => :@response
|
|
24
|
+
delegate [ :code, :url ] => :@response
|
|
25
25
|
|
|
26
26
|
def message #:nodoc:
|
|
27
|
-
"#{url}: #{code} #{@response[0..120]} (#{@response.response.class})"
|
|
27
|
+
"#{url}: #{code} #{@response[0..120]} (#{@response.response.class})"
|
|
28
28
|
end
|
|
29
29
|
end
|
|
30
30
|
|
data/lib/pipe2me/ext/sys.rb
CHANGED
|
@@ -2,14 +2,14 @@ require "shellwords"
|
|
|
2
2
|
|
|
3
3
|
module Pipe2me::Sys
|
|
4
4
|
extend self
|
|
5
|
-
|
|
5
|
+
|
|
6
6
|
class ExitError < RuntimeError; end
|
|
7
|
-
|
|
7
|
+
|
|
8
8
|
def sys(*args)
|
|
9
9
|
cmd, stdout = _sys(*args)
|
|
10
10
|
return stdout if $?.exitstatus == 0
|
|
11
11
|
end
|
|
12
|
-
|
|
12
|
+
|
|
13
13
|
def sys!(*args)
|
|
14
14
|
cmd, stdout = _sys(*args)
|
|
15
15
|
return stdout if $?.exitstatus == 0
|
|
@@ -19,18 +19,18 @@ module Pipe2me::Sys
|
|
|
19
19
|
def sh(*args)
|
|
20
20
|
sys "sh", "-c", *args
|
|
21
21
|
end
|
|
22
|
-
|
|
22
|
+
|
|
23
23
|
def sh!(*args)
|
|
24
24
|
sys! "sh", "-c", *args
|
|
25
25
|
end
|
|
26
|
-
|
|
26
|
+
|
|
27
27
|
private
|
|
28
|
-
|
|
28
|
+
|
|
29
29
|
def _sys(*args)
|
|
30
30
|
cmd = args.
|
|
31
31
|
map { |arg| Shellwords.escape arg.to_s }.
|
|
32
32
|
join(" ")
|
|
33
|
-
|
|
33
|
+
|
|
34
34
|
UI.debug cmd
|
|
35
35
|
stdout = IO.popen(cmd, &:read)
|
|
36
36
|
[ cmd, stdout ]
|
|
@@ -7,7 +7,7 @@ module Pipe2me::Tunnel::Commands
|
|
|
7
7
|
def tunnels
|
|
8
8
|
@tunnels ||= begin
|
|
9
9
|
urls, ports = config.urls, config.ports.to_s.split(",")
|
|
10
|
-
|
|
10
|
+
|
|
11
11
|
urls.zip(ports).map do |url, local_port|
|
|
12
12
|
uri = URI.parse(url)
|
|
13
13
|
[ uri.scheme, uri.port, local_port || uri.port ]
|
|
@@ -16,17 +16,25 @@ module Pipe2me::Tunnel::Commands
|
|
|
16
16
|
end
|
|
17
17
|
|
|
18
18
|
public
|
|
19
|
-
|
|
19
|
+
|
|
20
20
|
# return an arry [ [name, command ], [name, command ], .. ]
|
|
21
21
|
def tunnel_commands
|
|
22
22
|
tunnel_uri = URI.parse config.tunnel
|
|
23
|
-
|
|
23
|
+
|
|
24
24
|
tunnels.map do |protocol, remote_port, local_port|
|
|
25
25
|
next unless cmd = port_tunnel_command(tunnel_uri, protocol, remote_port, local_port)
|
|
26
26
|
[ "#{protocol}_#{remote_port}", cmd ]
|
|
27
27
|
end.compact
|
|
28
28
|
end
|
|
29
|
-
|
|
29
|
+
|
|
30
|
+
# def mapping_commands
|
|
31
|
+
# natpmpc = File.expand_path "#{File.dirname(__FILE__)}/../bin/natpmpc"
|
|
32
|
+
# mappings = tunnels.map do |_, remote_port, local_port|
|
|
33
|
+
# "tcp:#{remote_port}:#{local_port}"
|
|
34
|
+
# end
|
|
35
|
+
# [ [ "natpmpc", "#{natpmpc} #{mappings.join(" ")} && sleep 10000" ] ]
|
|
36
|
+
# end
|
|
37
|
+
|
|
30
38
|
def echo_commands
|
|
31
39
|
tunnels.map do |protocol, remote_port, local_port|
|
|
32
40
|
next unless cmd = echo_server_command(protocol, local_port)
|
|
@@ -41,10 +49,10 @@ module Pipe2me::Tunnel::Commands
|
|
|
41
49
|
raise "Cannot find #{cmd} in your $PATH. Is it installed?" if path == ""
|
|
42
50
|
path
|
|
43
51
|
end
|
|
44
|
-
|
|
52
|
+
|
|
45
53
|
def port_tunnel_command(tunnel_uri, protocol, remote_port, local_port)
|
|
46
54
|
autossh = which! :autossh
|
|
47
|
-
|
|
55
|
+
|
|
48
56
|
cmd = <<-SHELL
|
|
49
57
|
env AUTOSSH_GATETIME=0 # comments work here..
|
|
50
58
|
#{autossh}
|
|
@@ -55,12 +63,13 @@ module Pipe2me::Tunnel::Commands
|
|
|
55
63
|
-i #{T::SSH_PRIVKEY}
|
|
56
64
|
-o StrictHostKeyChecking=no
|
|
57
65
|
-o UserKnownHostsFile=pipe2me.known_hosts
|
|
66
|
+
-o PasswordAuthentication=no
|
|
58
67
|
-N
|
|
59
68
|
SHELL
|
|
60
|
-
|
|
69
|
+
|
|
61
70
|
# remove comments and newlines from commands
|
|
62
71
|
cmd.gsub(/( *#.*|\s+)/, " ").gsub(/(^ )|( $)/, "")
|
|
63
|
-
end
|
|
72
|
+
end
|
|
64
73
|
|
|
65
74
|
def echo_server_command(protocol, port)
|
|
66
75
|
binary = File.dirname(__FILE__) + "/echo/#{protocol}"
|
|
@@ -44,7 +44,7 @@ require "webrick/https"
|
|
|
44
44
|
|
|
45
45
|
RACK_SERVER_OPTIONS = {
|
|
46
46
|
:app => Application,
|
|
47
|
-
:BindAddress => "localhost",
|
|
47
|
+
# :BindAddress => "localhost",
|
|
48
48
|
:Port => ENV["PORT"] || 8080,
|
|
49
49
|
:server => "webrick",
|
|
50
50
|
:Logger => WEBrick::BasicLog.new($stderr, WEBrick::BasicLog::WARN)
|
data/lib/pipe2me/tunnel.rb
CHANGED
data/lib/pipe2me/version.rb
CHANGED
data/test/env-test.sh
CHANGED
data/test/monitrc-test.sh
CHANGED
data/test/opensslkey-test.sh
CHANGED
|
@@ -7,7 +7,7 @@ describe "openssl tests"
|
|
|
7
7
|
it_sets_up_openssl_certs() {
|
|
8
8
|
fqdn=$($pipe2me setup --server $pipe2me_server --token $pipe2me_token)
|
|
9
9
|
test -f pipe2me.openssl.priv
|
|
10
|
-
cat pipe2me.openssl.priv | grep "BEGIN
|
|
10
|
+
cat pipe2me.openssl.priv | grep -E "BEGIN.*PRIVATE KEY"
|
|
11
11
|
|
|
12
12
|
test -f pipe2me.openssl.cert
|
|
13
13
|
cat pipe2me.openssl.cert | grep "BEGIN CERTIFICATE"
|
data/test/redirection-test.sh
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env roundup
|
|
2
|
-
describe 'a HTTP(s) connection to subdomain.$pipe2me_server redirects to subdomain.$pipe2me_server:port'
|
|
2
|
+
describe 'a HTTP(s) connection to subdomain.$pipe2me_server redirects to subdomain.$pipe2me_server:port'
|
|
3
3
|
|
|
4
4
|
. $(dirname $1)/testhelper.inc
|
|
5
5
|
|
data/test/setup-test.sh
CHANGED
|
@@ -6,12 +6,14 @@ describe "tunnel setup"
|
|
|
6
6
|
# setup a tunnel
|
|
7
7
|
it_sets_up_tunnels() {
|
|
8
8
|
fqdn=$($pipe2me setup --server $pipe2me_server --token $pipe2me_token --ports 8100,8101 --protocols http,https)
|
|
9
|
-
|
|
9
|
+
|
|
10
|
+
test "$fqdn" != ""
|
|
11
|
+
|
|
10
12
|
# pipe2me setup --server $pipe2me_server returns the fqdn of the subdomain and nothing else
|
|
11
|
-
test 1 -eq $(echo $fqdn | wc -l)
|
|
13
|
+
test 1 -eq $(echo $fqdn | wc -l)
|
|
12
14
|
|
|
13
15
|
# The subdomain is actually a subdomain.
|
|
14
|
-
echo $fqdn | grep \.pipe2\.
|
|
16
|
+
echo $fqdn | grep \.pipe2\.
|
|
15
17
|
|
|
16
18
|
# Cannot setup a second tunnel in the same directory.
|
|
17
19
|
! $pipe2me setup --server $pipe2me_server --token $pipe2me_token
|
data/test/sshkey-test.sh
CHANGED
data/test/testhelper.inc
CHANGED
data/test/testhelper.release
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
pipe2me_server=http://pipe2.
|
|
1
|
+
pipe2me_server=http://test.pipe2.me
|
|
2
2
|
pipe2me_token=test@pipe2me
|
data/test/verify-test.sh
CHANGED
|
@@ -8,11 +8,11 @@ it_sets_up_tunnels_and_verifies() {
|
|
|
8
8
|
fqdn=$($pipe2me setup --server $pipe2me_server --token short@pipe2me)
|
|
9
9
|
|
|
10
10
|
# pipe2me setup --server $pipe2me_server returns the fqdn of the subdomain and nothing else
|
|
11
|
-
test 1 -eq $(echo $fqdn | wc -l)
|
|
11
|
+
test 1 -eq $(echo $fqdn | wc -l)
|
|
12
12
|
|
|
13
13
|
verified=$($pipe2me verify)
|
|
14
|
-
test "$fqdn"
|
|
15
|
-
|
|
14
|
+
test "$fqdn" = "$verified"
|
|
15
|
+
|
|
16
16
|
# sleep 4 seconds. The tunnel lives for 3 seconds
|
|
17
17
|
sleep 4
|
|
18
18
|
! $pipe2me verify
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: pipe2me-client
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.2.
|
|
4
|
+
version: 0.2.9
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- radiospiel
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2014-
|
|
11
|
+
date: 2014-02-12 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: rack
|
|
@@ -66,7 +66,7 @@ dependencies:
|
|
|
66
66
|
- - '>='
|
|
67
67
|
- !ruby/object:Gem::Version
|
|
68
68
|
version: '0'
|
|
69
|
-
description: pipe2me command line client V0.2.
|
|
69
|
+
description: pipe2me command line client V0.2.9; (c) The kinko team, 2013, 2014.
|
|
70
70
|
email: contact@kinko.me
|
|
71
71
|
executables:
|
|
72
72
|
- pipe2me
|
|
@@ -77,6 +77,7 @@ files:
|
|
|
77
77
|
- bin/pipe2me
|
|
78
78
|
- bin/pipe2med
|
|
79
79
|
- lib/pipe2me.rb
|
|
80
|
+
- lib/pipe2me/bin/natpmpc
|
|
80
81
|
- lib/pipe2me/cli-foreman.rb
|
|
81
82
|
- lib/pipe2me/cli-monit.rb
|
|
82
83
|
- lib/pipe2me/cli.rb
|
|
@@ -131,5 +132,5 @@ rubyforge_project:
|
|
|
131
132
|
rubygems_version: 2.2.1
|
|
132
133
|
signing_key:
|
|
133
134
|
specification_version: 4
|
|
134
|
-
summary: pipe2me command line client V0.2.
|
|
135
|
+
summary: pipe2me command line client V0.2.9; (c) The kinko team, 2013, 2014.
|
|
135
136
|
test_files: []
|