]>
pere.pagekite.me Git - exim4-smtorp.git/blob - tor-smtp/tor-smtp.c
4 * Copyright (c) 2006 Johannes Berg <johannes@sipsolutions.net>
6 * The bidicopy() routine is from OpenBSD's netcat rewrite, plus patches
7 * from the fedora project.
9 * Copyright (c) 2001 Eric Jackson <ericj@monkey.org>
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
15 * 1. Redistributions of source code must retain the above copyright
16 * notice, this list of conditions and the following disclaimer.
17 * 2. Redistributions in binary form must reproduce the above copyright
18 * notice, this list of conditions and the following disclaimer in the
19 * documentation and/or other materials provided with the distribution.
20 * 3. The name of the author may not be used to endorse or promote products
21 * derived from this software without specific prior written permission.
23 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR
24 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
25 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
26 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
27 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
28 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
29 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
30 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
31 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
32 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 #include <sys/types.h>
35 #include <sys/socket.h>
45 /* for BSD: htons is in there: */
46 #include <arpa/inet.h>
49 //#define DEBUG(str,args...) fprintf(stderr, str, ##args);
50 #define DEBUG(str,args...) do {} while(0)
52 int read_bytes_timeout(int fd
, char *buf
, int bytes
, int timeout
)
54 # define DEBUG_RETURN(str,args...) do { DEBUG(str, ##args); return 0; } while (0)
55 struct pollfd pollfd
= { .fd
= fd
, .events
= POLLIN
};
56 struct timeval tv_start
, tv_now
;
59 gettimeofday(&tv_start
, NULL
);
61 while (got
< bytes
&& timeout
>= 0) {
62 if (poll(&pollfd
, 1, timeout
*1000) <= 0)
63 DEBUG_RETURN("poll failed: %d (got %d)\n", errno
, got
);
64 gettimeofday(&tv_now
, NULL
);
65 timeout
-= tv_now
.tv_sec
- tv_start
.tv_sec
;
68 tmp
= read(fd
, buf
+got
, bytes
-got
);
70 DEBUG_RETURN("read failed: %d\n", errno
);
77 int connect_via_tor(const char *tor_server
, const char *tor_port
,
78 const char *remote_server
, short remote_port
)
81 struct addrinfo
*res
= NULL
;
82 char socks_init
[] = { 5, 1, 0 };
83 char buf
[255+7]; /* must be less than 255+7+1 bytes long */
84 int remote_server_len
= strlen(remote_server
);
86 if (remote_server_len
> sizeof(buf
)-7/*5 leading bytes and port */)
89 fd
= socket(PF_INET
, SOCK_STREAM
, 0);
91 DEBUG("failed to create socket\n");
95 #define DEBUG_OUT(str, args...) do { DEBUG(str,##args); goto out_close; } while (0)
97 if (getaddrinfo(tor_server
, tor_port
, NULL
, &res
))
98 DEBUG_OUT("getaddrinfo\n");
100 DEBUG_OUT("getaddrinfo returned empty set\n");
102 if (connect(fd
, res
->ai_addr
, res
->ai_addrlen
))
103 DEBUG_OUT("failed to connect\n");
105 /* now that we are connected, set up SOCKS */
106 if (write(fd
, &socks_init
, sizeof(socks_init
)) != sizeof(socks_init
))
108 if (!read_bytes_timeout(fd
, &buf
[0], 2, 120))
110 if (buf
[0] != 5 || buf
[1] != 0)
117 buf
[4] = remote_server_len
;
118 strcpy(buf
+5, remote_server
);
119 *(short*) (buf
+5+remote_server_len
) = htons(remote_port
);
120 if (write(fd
, buf
, 5+remote_server_len
+2) != 5+remote_server_len
+2)
122 if (!read_bytes_timeout(fd
, buf
, 4, 120))
124 if (buf
[0] != 5 || buf
[1] != 0)
126 /* read rest of the response */
129 if (!read_bytes_timeout(fd
, buf
, 6, 120))
132 /* TODO handle other responses */
142 /* read at most buflen characters into buffer,
143 * until linefeed is encountered.
144 * NB: if more characters are received they are discarded!
146 int readline(int fd
, char *buf
, int buflen
, int timeout
)
148 # define DEBUG_RETURN(str,args...) do { DEBUG(str, ##args); return 0; } while (0)
149 struct pollfd pollfd
= { .fd
= fd
, .events
= POLLIN
};
150 struct timeval tv_start
, tv_now
;
154 gettimeofday(&tv_start
, NULL
);
156 while (got
< buflen
&& timeout
>= 0) {
157 if (poll(&pollfd
, 1, timeout
*1000) <= 0)
158 DEBUG_RETURN("poll failed: %d (got %d)\n", errno
, got
);
159 gettimeofday(&tv_now
, NULL
);
160 timeout
-= tv_now
.tv_sec
- tv_start
.tv_sec
;
163 tmp
= read(fd
, buf
+got
, buflen
-got
);
165 DEBUG_RETURN("read failed: %d\n", errno
);
166 match
= memchr(buf
+got
, '\n', tmp
);
168 if (*(match
-1) == '\r')
181 * Loop that polls on the network file descriptor and stdin.
186 struct pollfd pfd
[2];
187 unsigned char buf
[8192];
188 int n
, wfd
= fileno(stdin
);
189 int lfd
= fileno(stdout
);
194 /* Setup Network FD */
196 pfd
[0].events
= POLLIN
;
198 /* Set up STDIN FD. */
200 pfd
[1].events
= POLLIN
;
202 while (pfd
[0].fd
!= -1) {
203 if ((n
= poll(pfd
, 2, -1)) < 0) {
211 if (pfd
[0].revents
& POLLIN
) {
212 if ((n
= read(nfd
, buf
, plen
)) < 0)
217 if (atomicio(vwrite
, lfd
, buf
, n
) != n
)
221 else if (pfd
[0].revents
& POLLHUP
) {
223 shutdown(nfd
, SHUT_RD
);
229 if(pfd
[1].revents
& POLLIN
) {
230 if ((n
= read(wfd
, buf
, plen
)) < 0)
235 if (atomicio(vwrite
, nfd
, buf
, n
) != n
)
239 else if (pfd
[1].revents
& POLLHUP
) {
241 shutdown(nfd
, SHUT_WR
);
249 int main(int argc
, char **argv
)
251 char *tor_server
= "localhost";
252 char *tor_port
= "9050";
253 char *remote_server
= NULL
;
255 char local_onion
[255];
256 char buf
[255], buf2
[255];
261 tor_server
= argv
[1];
264 if (argc
> 3 || (tor_server
&& (!strcasecmp(tor_server
, "--help") || !strcasecmp(tor_server
, "-h")))) {
265 fprintf(stderr
, "tor-smtp [tor-server] [tor-port]\n");
266 fprintf(stderr
, " (default localhost 9050)\n");
270 printf("220 Welcome\r\n");
274 if ((linelen
= readline(0, buf
, sizeof(buf
), 30)) < 0) {
275 printf("421 connection timed out\r\n");
282 if (!strncasecmp("QUIT", buf
, 4)) {
283 printf("221 closing connection\r\n");
287 if (!strncasecmp("EHLO ", buf
, 5) || !strncasecmp("HELO ", buf
, 5))
290 printf("500 invalid command\r\n");
295 tmp
= strtok(buf
+5, " ");
297 printf("500 invalid EHLO/HELO: missing local name\n");
301 remote_server
= strtok(NULL
, " ");
302 if (!remote_server
) {
303 printf("500 invalid EHLO/HELO: missing remote name\n");
306 strncpy(local_onion
, tmp
, sizeof(local_onion
));
307 fd
= connect_via_tor(tor_server
, tor_port
, remote_server
, 25);
309 printf("421 could not connect\r\n");
314 linelen
= readline(fd
, buf2
, sizeof(buf2
), 120);
316 printf("421 could not connect\r\n");
320 if (strncmp("220 ", buf2
, 4)) {
321 printf("421 server seems to not like us\r\n");
322 DEBUG("answer was: %s\n", buf2
);
327 strncpy(buf
+5, local_onion
, sizeof(buf
)-5);
328 linelen
= strlen(buf
);
329 *(buf
+linelen
) = '\r';
330 *(buf
+linelen
+1) = '\n';
331 write(fd
, buf
, linelen
+2);
333 close(2); /* get rid of stderr */
334 /* now "all" we have to do is do a bidirectional copy of fd and stdio */