]>
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>
46 /* for BSD: htons is in there: */
47 #include <arpa/inet.h>
50 //#define DEBUG(str,args...) fprintf(stderr, str, ##args);
51 #define DEBUG(str,args...) do {} while(0)
53 int read_bytes_timeout(int fd
, char *buf
, int bytes
, int timeout
)
55 # define DEBUG_RETURN(str,args...) do { DEBUG(str, ##args); return 0; } while (0)
56 struct pollfd pollfd
= { .fd
= fd
, .events
= POLLIN
};
57 struct timeval tv_start
, tv_now
;
60 gettimeofday(&tv_start
, NULL
);
62 while (got
< bytes
&& timeout
>= 0) {
63 if (poll(&pollfd
, 1, timeout
*1000) <= 0)
64 DEBUG_RETURN("poll failed: %d (got %d)\n", errno
, got
);
65 gettimeofday(&tv_now
, NULL
);
66 timeout
-= tv_now
.tv_sec
- tv_start
.tv_sec
;
69 tmp
= read(fd
, buf
+got
, bytes
-got
);
71 DEBUG_RETURN("read failed: %d\n", errno
);
78 int connect_via_tor(const char *tor_server
, const char *tor_port
,
79 const char *remote_server
, short remote_port
)
82 struct addrinfo
*res
= NULL
;
83 char socks_init
[] = { 5, 1, 0 };
84 char buf
[255+7]; /* must be less than 255+7+1 bytes long */
85 int remote_server_len
= strlen(remote_server
);
87 if (remote_server_len
> sizeof(buf
)-7/*5 leading bytes and port */)
90 fd
= socket(PF_INET
, SOCK_STREAM
, 0);
92 DEBUG("failed to create socket\n");
96 #define DEBUG_OUT(str, args...) do { DEBUG(str,##args); goto out_close; } while (0)
98 if (getaddrinfo(tor_server
, tor_port
, NULL
, &res
))
99 DEBUG_OUT("getaddrinfo\n");
101 DEBUG_OUT("getaddrinfo returned empty set\n");
103 if (connect(fd
, res
->ai_addr
, res
->ai_addrlen
))
104 DEBUG_OUT("failed to connect\n");
106 /* now that we are connected, set up SOCKS */
107 if (write(fd
, &socks_init
, sizeof(socks_init
)) != sizeof(socks_init
))
109 if (!read_bytes_timeout(fd
, &buf
[0], 2, 120))
111 if (buf
[0] != 5 || buf
[1] != 0)
118 buf
[4] = remote_server_len
;
119 strcpy(buf
+5, remote_server
);
120 *(short*) (buf
+5+remote_server_len
) = htons(remote_port
);
121 if (write(fd
, buf
, 5+remote_server_len
+2) != 5+remote_server_len
+2)
123 if (!read_bytes_timeout(fd
, buf
, 4, 120))
125 if (buf
[0] != 5 || buf
[1] != 0)
127 /* read rest of the response */
130 if (!read_bytes_timeout(fd
, buf
, 6, 120))
133 /* TODO handle other responses */
143 /* read at most buflen characters into buffer,
144 * until linefeed is encountered.
145 * NB: if more characters are received they are discarded!
147 int readline(int fd
, char *buf
, int buflen
, int timeout
)
149 # define DEBUG_RETURN(str,args...) do { DEBUG(str, ##args); return 0; } while (0)
150 struct pollfd pollfd
= { .fd
= fd
, .events
= POLLIN
};
151 struct timeval tv_start
, tv_now
;
155 gettimeofday(&tv_start
, NULL
);
157 while (got
< buflen
&& timeout
>= 0) {
158 if (poll(&pollfd
, 1, timeout
*1000) <= 0)
159 DEBUG_RETURN("poll failed: %d (got %d)\n", errno
, got
);
160 gettimeofday(&tv_now
, NULL
);
161 timeout
-= tv_now
.tv_sec
- tv_start
.tv_sec
;
164 tmp
= read(fd
, buf
+got
, buflen
-got
);
166 DEBUG_RETURN("read failed: %d\n", errno
);
167 match
= memchr(buf
+got
, '\n', tmp
);
169 if (*(match
-1) == '\r')
182 * Loop that polls on the network file descriptor and stdin.
187 struct pollfd pfd
[2];
188 unsigned char buf
[8192];
189 int n
, wfd
= fileno(stdin
);
190 int lfd
= fileno(stdout
);
195 /* Setup Network FD */
197 pfd
[0].events
= POLLIN
;
199 /* Set up STDIN FD. */
201 pfd
[1].events
= POLLIN
;
203 while (pfd
[0].fd
!= -1) {
204 if ((n
= poll(pfd
, 2, -1)) < 0) {
212 if (pfd
[0].revents
& POLLIN
) {
213 if ((n
= read(nfd
, buf
, plen
)) < 0)
218 if (atomicio(vwrite
, lfd
, buf
, n
) != n
)
222 else if (pfd
[0].revents
& POLLHUP
) {
224 shutdown(nfd
, SHUT_RD
);
230 if(pfd
[1].revents
& POLLIN
) {
231 if ((n
= read(wfd
, buf
, plen
)) < 0)
236 if (atomicio(vwrite
, nfd
, buf
, n
) != n
)
240 else if (pfd
[1].revents
& POLLHUP
) {
242 shutdown(nfd
, SHUT_WR
);
250 int main(int argc
, char **argv
)
252 char *tor_server
= "localhost";
253 char *tor_port
= "9050";
254 char *remote_server
= NULL
;
256 char local_onion
[255];
257 char buf
[255], buf2
[255];
262 tor_server
= argv
[1];
265 if (argc
> 3 || (tor_server
&& (!strcasecmp(tor_server
, "--help") || !strcasecmp(tor_server
, "-h")))) {
266 fprintf(stderr
, "tor-smtp [tor-server] [tor-port]\n");
267 fprintf(stderr
, " (default localhost 9050)\n");
271 openlog("tor-smtp", LOG_PID
, LOG_MAIL
);
273 printf("220 Welcome\r\n");
277 if ((linelen
= readline(0, buf
, sizeof(buf
), 30)) < 0) {
278 printf("421 connection timed out\r\n");
285 if (!strncasecmp("QUIT", buf
, 4)) {
286 printf("221 closing connection\r\n");
290 if (!strncasecmp("EHLO ", buf
, 5) || !strncasecmp("HELO ", buf
, 5))
293 printf("500 invalid command\r\n");
298 tmp
= strtok(buf
+5, " ");
300 printf("500 invalid EHLO/HELO: missing local name\n");
304 remote_server
= strtok(NULL
, " ");
305 if (!remote_server
) {
306 printf("500 invalid EHLO/HELO: missing remote name\n");
309 strncpy(local_onion
, tmp
, sizeof(local_onion
));
310 fd
= connect_via_tor(tor_server
, tor_port
, remote_server
, 25);
312 printf("421 could not connect\r\n");
314 syslog(LOG_WARNING
, "tor connection to %s failed",
319 linelen
= readline(fd
, buf2
, sizeof(buf2
), 120);
321 printf("421 could not connect\r\n");
323 syslog(LOG_WARNING
, "no welcome message from %s", remote_server
);
326 if (strncmp("220 ", buf2
, 4)) {
327 printf("421 server seems to not like us\r\n");
328 DEBUG("answer was: %s\n", buf2
);
333 strncpy(buf
+5, local_onion
, sizeof(buf
)-5);
334 linelen
= strlen(buf
);
335 *(buf
+linelen
) = '\r';
336 *(buf
+linelen
+1) = '\n';
337 write(fd
, buf
, linelen
+2);
339 close(2); /* get rid of stderr */
340 /* now "all" we have to do is do a bidirectional copy of fd and stdio */