1 /* socket.c - twoftpd routines for handling sockets
2 * Copyright (C) 2008 Bruce Guenter <bruce@untroubled.org>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 */
18 #include <errno.h>
19 #include <string.h>
20 #include <unistd.h>
21 #include <bglibs/fmt.h>
22 #include <bglibs/socket.h>
23 #include "twoftpd.h"
24 #include "backend.h"
25 #include <bglibs/sysdeps.h>
26 #include <bglibs/unix.h>
27
28 /* State variables */
29 static int socket_fd = -1;
30 static ipv4addr socket_ip;
31 static unsigned short socket_port;
32 static ipv4addr remote_ip;
33 static unsigned short remote_port;
34 static ipv4addr client_ip;
35 static ipv4addr server_ip;
36 static enum { NONE, PASV, PORT } connect_mode = NONE;
37
respond_timedoutconn(void)38 static int respond_timedoutconn(void)
39 {
40 return respond(425, 1, "Timed out waiting for connection.");
41 }
42
respond_connfailed(void)43 static int respond_connfailed(void)
44 {
45 return respond_syserr(425, "Connection failed");
46 }
47
respond_connaborted(void)48 static int respond_connaborted(void)
49 {
50 return respond(425, 1, "Connection aborted by incoming command.");
51 }
52
accept_connection(void)53 static int accept_connection(void)
54 {
55 int fd;
56 iopoll_fd pf[2];
57
58 pf[0].fd = 0;
59 pf[0].events = IOPOLL_READ;
60 pf[0].revents = 0;
61 pf[1].fd = socket_fd;
62 pf[1].events = IOPOLL_READ;
63 switch (iopoll_restart(pf, 2, connect_timeout*1000)) {
64 case 2: break;
65 case 1: break;
66 case 0: respond_timedoutconn(); return -1;
67 default: respond_connfailed(); return -1;
68 }
69 if (pf[0].revents) {
70 respond_connaborted();
71 return -1;
72 }
73 if ((fd = socket_accept4(socket_fd, &remote_ip, &remote_port)) == -1) {
74 respond_connfailed();
75 return -1;
76 }
77 close(socket_fd);
78 socket_fd = -1;
79 connect_mode = NONE;
80 if (!nonblock_on(fd)) {
81 respond_syserr(425, "Could not set flags on socket");
82 close(fd);
83 return -1;
84 }
85 return fd;
86 }
87
make_connect_socket(void)88 static int make_connect_socket(void)
89 {
90 int fd;
91 char buf;
92
93 fd = -1;
94 if (bind_port_fd != -1) {
95 if (write(bind_port_fd, &buf, 1) == 1 &&
96 read(bind_port_fd, &buf, 1) == 1 &&
97 buf == 0)
98 fd = socket_recvfd(bind_port_fd);
99 }
100 if (fd == -1) {
101 if ((fd = socket_tcp()) == -1) {
102 respond_syserr(425, "Could not allocate a socket");
103 return -1;
104 }
105 if (!socket_reuse(fd) ||
106 !socket_bind4(fd, &server_ip, 0)) {
107 respond_syserr(425, "Could not set flags on socket");
108 close(fd);
109 return -1;
110 }
111 }
112 return fd;
113 }
114
start_connection(void)115 static int start_connection(void)
116 {
117 int fd;
118 iopoll_fd pf[2];
119
120 if ((fd = make_connect_socket()) == -1) return -1;
121 if (!nonblock_on(fd)) {
122 respond_syserr(425, "Could not set flags on socket");
123 close(fd);
124 return -1;
125 }
126
127 if (socket_connect4(fd, &remote_ip, remote_port)) return fd;
128
129 if (errno != EINPROGRESS && errno != EWOULDBLOCK) {
130 respond_connfailed();
131 return -1;
132 }
133
134 pf[0].fd = 0;
135 pf[0].events = IOPOLL_READ;
136 pf[0].revents = 0;
137 pf[1].fd = fd;
138 pf[1].events = IOPOLL_WRITE;
139 switch (iopoll_restart(pf, 2, connect_timeout*1000)) {
140 case 0:
141 respond_timedoutconn();
142 break;
143 case 2:
144 case 1:
145 if (pf[0].revents) {
146 respond_connaborted();
147 break;
148 }
149 if (socket_connected(fd)) return fd;
150 /* No break, fall through */
151 default:
152 respond_connfailed();
153 }
154 close(fd);
155 return -1;
156 }
157
make_connection_fd(void)158 static int make_connection_fd(void)
159 {
160 int fd;
161
162 if (connect_mode == NONE) {
163 fd = -1;
164 respond(425, 1, "No PORT or PASV commands have been issued.");
165 }
166 else {
167 fd = (connect_mode == PASV) ? accept_connection() : start_connection();
168 if (fd != -1) respond(150, 1, "Opened data connection.");
169 }
170 respond_start_xfer();
171 return fd;
172 }
173
make_in_connection(void)174 int make_in_connection(void)
175 {
176 return make_connection_fd();
177 }
178
make_out_connection(void)179 int make_out_connection(void)
180 {
181 int fd;
182 if ((fd = make_connection_fd()) != -1) {
183 socket_cork(fd);
184 if (!socket_linger(fd, 1, timeout)) {
185 respond_syserr(425, "Could not set flags on socket");
186 close(fd);
187 fd = -1;
188 }
189 }
190 return fd;
191 }
192
close_out_connection(int out)193 int close_out_connection(int out)
194 {
195 socket_uncork(out);
196 return close(out) == 0;
197 }
198
strtoc(const char * s,const char ** end)199 static unsigned char strtoc(const char* s, const char** end)
200 {
201 long tmp;
202 tmp = strtol(s, (char**)end, 10);
203 if (tmp < 0 || tmp > 0xff) *end = s;
204 return tmp;
205 }
206
scan_ip(const char * s,char sep,ipv4addr * addr,const char ** endptr)207 static int scan_ip(const char* s, char sep,
208 ipv4addr* addr, const char** endptr)
209 {
210 const char* end;
211 addr->addr[0] = strtoc(s, &end); if (*end != sep) return 0;
212 addr->addr[1] = strtoc(end+1, &end); if (*end != sep) return 0;
213 addr->addr[2] = strtoc(end+1, &end); if (*end != sep) return 0;
214 addr->addr[3] = strtoc(end+1, &end);
215 *endptr = end;
216 return 1;
217 }
218
parse_localip(const char * s)219 int parse_localip(const char* s)
220 {
221 const char* end;
222 return scan_ip(s, '.', &server_ip, &end) && *end == 0;
223 }
224
parse_remoteip(const char * s)225 int parse_remoteip(const char* s)
226 {
227 const char* end;
228 return scan_ip(s, '.', &client_ip, &end) && *end == 0;
229 }
230
parse_addr(const char * s)231 static int parse_addr(const char* s)
232 {
233 const char* end;
234 if (!scan_ip(s, ',', &remote_ip, &end) || *end != ',') return 0;
235 remote_port = strtoc(end+1, &end) << 8; if (*end != ',') return 0;
236 remote_port |= strtoc(end+1, &end); if (*end != 0) return 0;
237 connect_mode = PORT;
238 return 1;
239 }
240
make_accept_socket(void)241 static int make_accept_socket(void)
242 {
243 if (socket_fd != -1) close(socket_fd);
244 if ((socket_fd = socket_tcp()) != -1) {
245 if (socket_bind4(socket_fd, &server_ip, 0) &&
246 socket_listen(socket_fd, 1) &&
247 socket_getaddr4(socket_fd, &socket_ip, &socket_port)) {
248 connect_mode = PASV;
249 return 1;
250 }
251 else {
252 close(socket_fd);
253 socket_fd = -1;
254 }
255 }
256 connect_mode = NONE;
257 return 0;
258 }
259
handle_pasv(void)260 int handle_pasv(void)
261 {
262 char buffer[6*4+25];
263 if (!make_accept_socket())
264 return respond_syserr(425, "Could not create socket");
265 buffer[fmt_multi(buffer, "{Entering Passive Mode (}u{,}u{,}u{,}u{,}u{,}u{).",
266 socket_ip.addr[0], socket_ip.addr[1],
267 socket_ip.addr[2], socket_ip.addr[3],
268 (socket_port>>8)&0xff, socket_port&0xff)] = 0;
269 return respond(227, 1, buffer);
270 }
271
handle_port(void)272 int handle_port(void)
273 {
274 if (!parse_addr(req_param))
275 return respond(501, 1, "Can't parse your PORT address.");
276 if (memcmp(&remote_ip, &client_ip, sizeof client_ip))
277 return respond(501, 1, "PORT IP does not match client address.");
278 return respond_ok();
279 }
280