1 |
|
|
/* $OpenBSD: tftpd.c,v 1.39 2017/05/26 17:38:46 florian Exp $ */ |
2 |
|
|
|
3 |
|
|
/* |
4 |
|
|
* Copyright (c) 2012 David Gwynne <dlg@uq.edu.au> |
5 |
|
|
* |
6 |
|
|
* Permission to use, copy, modify, and distribute this software for any |
7 |
|
|
* purpose with or without fee is hereby granted, provided that the above |
8 |
|
|
* copyright notice and this permission notice appear in all copies. |
9 |
|
|
* |
10 |
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
11 |
|
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
12 |
|
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
13 |
|
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
14 |
|
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
15 |
|
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
16 |
|
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
17 |
|
|
*/ |
18 |
|
|
|
19 |
|
|
/* |
20 |
|
|
* Copyright (c) 1983 Regents of the University of California. |
21 |
|
|
* All rights reserved. |
22 |
|
|
* |
23 |
|
|
* Redistribution and use in source and binary forms, with or without |
24 |
|
|
* modification, are permitted provided that the following conditions |
25 |
|
|
* are met: |
26 |
|
|
* 1. Redistributions of source code must retain the above copyright |
27 |
|
|
* notice, this list of conditions and the following disclaimer. |
28 |
|
|
* 2. Redistributions in binary form must reproduce the above copyright |
29 |
|
|
* notice, this list of conditions and the following disclaimer in the |
30 |
|
|
* documentation and/or other materials provided with the distribution. |
31 |
|
|
* 3. Neither the name of the University nor the names of its contributors |
32 |
|
|
* may be used to endorse or promote products derived from this software |
33 |
|
|
* without specific prior written permission. |
34 |
|
|
* |
35 |
|
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND |
36 |
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
37 |
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
38 |
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
39 |
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
40 |
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
41 |
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
42 |
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
43 |
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
44 |
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
45 |
|
|
* SUCH DAMAGE. |
46 |
|
|
*/ |
47 |
|
|
|
48 |
|
|
/* |
49 |
|
|
* Trivial file transfer protocol server. |
50 |
|
|
* |
51 |
|
|
* This version is based on src/libexec/tftpd which includes many |
52 |
|
|
* modifications by Jim Guyton <guyton@rand-unix>. |
53 |
|
|
* |
54 |
|
|
* It was restructured to be a persistent event driven daemon |
55 |
|
|
* supporting concurrent connections by dlg for use at the University |
56 |
|
|
* of Queensland in the Faculty of Engineering Architecture and |
57 |
|
|
* Information Technology. |
58 |
|
|
*/ |
59 |
|
|
|
60 |
|
|
#include <sys/types.h> |
61 |
|
|
#include <sys/queue.h> |
62 |
|
|
#include <sys/socket.h> |
63 |
|
|
#include <sys/stat.h> |
64 |
|
|
#include <sys/uio.h> |
65 |
|
|
#include <sys/un.h> |
66 |
|
|
|
67 |
|
|
#include <netinet/in.h> |
68 |
|
|
#include <arpa/inet.h> |
69 |
|
|
#include <arpa/tftp.h> |
70 |
|
|
#include <netdb.h> |
71 |
|
|
|
72 |
|
|
#include <err.h> |
73 |
|
|
#include <ctype.h> |
74 |
|
|
#include <errno.h> |
75 |
|
|
#include <event.h> |
76 |
|
|
#include <fcntl.h> |
77 |
|
|
#include <paths.h> |
78 |
|
|
#include <poll.h> |
79 |
|
|
#include <pwd.h> |
80 |
|
|
#include <stdio.h> |
81 |
|
|
#include <stdlib.h> |
82 |
|
|
#include <string.h> |
83 |
|
|
#include <stdarg.h> |
84 |
|
|
#include <syslog.h> |
85 |
|
|
#include <unistd.h> |
86 |
|
|
#include <limits.h> |
87 |
|
|
#include <vis.h> |
88 |
|
|
|
89 |
|
|
#define TIMEOUT 5 /* packet rexmt timeout */ |
90 |
|
|
#define TIMEOUT_MIN 1 /* minimal packet rexmt timeout */ |
91 |
|
|
#define TIMEOUT_MAX 255 /* maximal packet rexmt timeout */ |
92 |
|
|
|
93 |
|
|
#define RETRIES 5 |
94 |
|
|
|
95 |
|
|
#define SEEDPATH "/etc/random.seed" |
96 |
|
|
|
97 |
|
|
struct formats; |
98 |
|
|
|
99 |
|
|
enum opt_enum { |
100 |
|
|
OPT_TSIZE = 0, |
101 |
|
|
OPT_TIMEOUT, |
102 |
|
|
OPT_BLKSIZE, |
103 |
|
|
NOPT |
104 |
|
|
}; |
105 |
|
|
|
106 |
|
|
static char *opt_names[] = { |
107 |
|
|
"tsize", |
108 |
|
|
"timeout", |
109 |
|
|
"blksize" |
110 |
|
|
}; |
111 |
|
|
|
112 |
|
|
struct opt_client { |
113 |
|
|
char *o_request; |
114 |
|
|
long long o_reply; |
115 |
|
|
}; |
116 |
|
|
|
117 |
|
|
|
118 |
|
|
struct tftp_server { |
119 |
|
|
struct event ev; |
120 |
|
|
TAILQ_ENTRY(tftp_server) entry; |
121 |
|
|
int s; |
122 |
|
|
}; |
123 |
|
|
|
124 |
|
|
TAILQ_HEAD(, tftp_server) tftp_servers; |
125 |
|
|
|
126 |
|
|
struct tftp_client { |
127 |
|
|
char buf[SEGSIZE_MAX + 4]; |
128 |
|
|
struct event sev; |
129 |
|
|
struct sockaddr_storage ss; |
130 |
|
|
|
131 |
|
|
struct timeval tv; |
132 |
|
|
|
133 |
|
|
TAILQ_ENTRY(tftp_client) entry; |
134 |
|
|
|
135 |
|
|
struct opt_client *options; |
136 |
|
|
|
137 |
|
|
size_t segment_size; |
138 |
|
|
size_t packet_size; |
139 |
|
|
size_t buflen; |
140 |
|
|
|
141 |
|
|
FILE *file; |
142 |
|
|
int (*fgetc)(struct tftp_client *); |
143 |
|
|
int (*fputc)(struct tftp_client *, int); |
144 |
|
|
|
145 |
|
|
u_int retries; |
146 |
|
|
u_int16_t block; |
147 |
|
|
|
148 |
|
|
int opcode; |
149 |
|
|
int newline; |
150 |
|
|
|
151 |
|
|
int sock; |
152 |
|
|
}; |
153 |
|
|
|
154 |
|
|
__dead void usage(void); |
155 |
|
|
const char *getip(void *); |
156 |
|
|
int rdaemon(int); |
157 |
|
|
|
158 |
|
|
void rewrite_connect(const char *); |
159 |
|
|
void rewrite_events(void); |
160 |
|
|
void rewrite_map(struct tftp_client *, const char *); |
161 |
|
|
void rewrite_req(int, short, void *); |
162 |
|
|
void rewrite_res(int, short, void *); |
163 |
|
|
|
164 |
|
|
int tftpd_listen(const char *, const char *, int); |
165 |
|
|
void tftpd_events(void); |
166 |
|
|
void tftpd_recv(int, short, void *); |
167 |
|
|
int retry(struct tftp_client *); |
168 |
|
|
int tftp_flush(struct tftp_client *); |
169 |
|
|
void tftp_end(struct tftp_client *); |
170 |
|
|
|
171 |
|
|
void tftp(struct tftp_client *, struct tftphdr *, size_t); |
172 |
|
|
void tftp_open(struct tftp_client *, const char *); |
173 |
|
|
void nak(struct tftp_client *, int); |
174 |
|
|
int oack(struct tftp_client *); |
175 |
|
|
void oack_done(int, short, void *); |
176 |
|
|
|
177 |
|
|
void sendfile(struct tftp_client *); |
178 |
|
|
void recvfile(struct tftp_client *); |
179 |
|
|
int fget_octet(struct tftp_client *); |
180 |
|
|
int fput_octet(struct tftp_client *, int); |
181 |
|
|
int fget_netascii(struct tftp_client *); |
182 |
|
|
int fput_netascii(struct tftp_client *, int); |
183 |
|
|
void file_read(struct tftp_client *); |
184 |
|
|
int tftp_wrq_ack_packet(struct tftp_client *); |
185 |
|
|
void tftp_rrq_ack(int, short, void *); |
186 |
|
|
void tftp_wrq_ack(struct tftp_client *client); |
187 |
|
|
void tftp_wrq(int, short, void *); |
188 |
|
|
void tftp_wrq_end(int, short, void *); |
189 |
|
|
|
190 |
|
|
int parse_options(struct tftp_client *, char *, size_t, |
191 |
|
|
struct opt_client *); |
192 |
|
|
int validate_access(struct tftp_client *, const char *); |
193 |
|
|
|
194 |
|
|
struct tftp_client * |
195 |
|
|
client_alloc(void); |
196 |
|
|
void client_free(struct tftp_client *client); |
197 |
|
|
|
198 |
|
|
struct formats { |
199 |
|
|
const char *f_mode; |
200 |
|
|
int (*f_getc)(struct tftp_client *); |
201 |
|
|
int (*f_putc)(struct tftp_client *, int); |
202 |
|
|
} formats[] = { |
203 |
|
|
{ "octet", fget_octet, fput_octet }, |
204 |
|
|
{ "netascii", fget_netascii, fput_netascii }, |
205 |
|
|
{ NULL, NULL } |
206 |
|
|
}; |
207 |
|
|
|
208 |
|
|
struct errmsg { |
209 |
|
|
int e_code; |
210 |
|
|
const char *e_msg; |
211 |
|
|
} errmsgs[] = { |
212 |
|
|
{ EUNDEF, "Undefined error code" }, |
213 |
|
|
{ ENOTFOUND, "File not found" }, |
214 |
|
|
{ EACCESS, "Access violation" }, |
215 |
|
|
{ ENOSPACE, "Disk full or allocation exceeded" }, |
216 |
|
|
{ EBADOP, "Illegal TFTP operation" }, |
217 |
|
|
{ EBADID, "Unknown transfer ID" }, |
218 |
|
|
{ EEXISTS, "File already exists" }, |
219 |
|
|
{ ENOUSER, "No such user" }, |
220 |
|
|
{ EOPTNEG, "Option negotiation failed" }, |
221 |
|
|
{ -1, NULL } |
222 |
|
|
}; |
223 |
|
|
|
224 |
|
|
struct loggers { |
225 |
|
|
__dead void (*err)(int, const char *, ...) |
226 |
|
|
__attribute__((__format__ (printf, 2, 3))); |
227 |
|
|
__dead void (*errx)(int, const char *, ...) |
228 |
|
|
__attribute__((__format__ (printf, 2, 3))); |
229 |
|
|
void (*warn)(const char *, ...) |
230 |
|
|
__attribute__((__format__ (printf, 1, 2))); |
231 |
|
|
void (*warnx)(const char *, ...) |
232 |
|
|
__attribute__((__format__ (printf, 1, 2))); |
233 |
|
|
void (*info)(const char *, ...) |
234 |
|
|
__attribute__((__format__ (printf, 1, 2))); |
235 |
|
|
void (*debug)(const char *, ...) |
236 |
|
|
__attribute__((__format__ (printf, 1, 2))); |
237 |
|
|
}; |
238 |
|
|
|
239 |
|
|
const struct loggers conslogger = { |
240 |
|
|
err, |
241 |
|
|
errx, |
242 |
|
|
warn, |
243 |
|
|
warnx, |
244 |
|
|
warnx, /* info */ |
245 |
|
|
warnx /* debug */ |
246 |
|
|
}; |
247 |
|
|
|
248 |
|
|
__dead void syslog_err(int, const char *, ...) |
249 |
|
|
__attribute__((__format__ (printf, 2, 3))); |
250 |
|
|
__dead void syslog_errx(int, const char *, ...) |
251 |
|
|
__attribute__((__format__ (printf, 2, 3))); |
252 |
|
|
void syslog_warn(const char *, ...) |
253 |
|
|
__attribute__((__format__ (printf, 1, 2))); |
254 |
|
|
void syslog_warnx(const char *, ...) |
255 |
|
|
__attribute__((__format__ (printf, 1, 2))); |
256 |
|
|
void syslog_info(const char *, ...) |
257 |
|
|
__attribute__((__format__ (printf, 1, 2))); |
258 |
|
|
void syslog_debug(const char *, ...) |
259 |
|
|
__attribute__((__format__ (printf, 1, 2))); |
260 |
|
|
void syslog_vstrerror(int, int, const char *, va_list) |
261 |
|
|
__attribute__((__format__ (printf, 3, 0))); |
262 |
|
|
|
263 |
|
|
const struct loggers syslogger = { |
264 |
|
|
syslog_err, |
265 |
|
|
syslog_errx, |
266 |
|
|
syslog_warn, |
267 |
|
|
syslog_warnx, |
268 |
|
|
syslog_info, |
269 |
|
|
syslog_debug |
270 |
|
|
}; |
271 |
|
|
|
272 |
|
|
const struct loggers *logger = &conslogger; |
273 |
|
|
|
274 |
|
|
#define lerr(_e, _f...) logger->err((_e), _f) |
275 |
|
|
#define lerrx(_e, _f...) logger->errx((_e), _f) |
276 |
|
|
#define lwarn(_f...) logger->warn(_f) |
277 |
|
|
#define lwarnx(_f...) logger->warnx(_f) |
278 |
|
|
#define linfo(_f...) logger->info(_f) |
279 |
|
|
#define ldebug(_f...) logger->debug(_f) |
280 |
|
|
|
281 |
|
|
__dead void |
282 |
|
|
usage(void) |
283 |
|
|
{ |
284 |
|
|
extern char *__progname; |
285 |
|
|
fprintf(stderr, "usage: %s [-46cdv] [-l address] [-p port] [-r socket]" |
286 |
|
|
" directory\n", __progname); |
287 |
|
|
exit(1); |
288 |
|
|
} |
289 |
|
|
|
290 |
|
|
int cancreate = 0; |
291 |
|
|
int verbose = 0; |
292 |
|
|
int debug = 0; |
293 |
|
|
|
294 |
|
|
int |
295 |
|
|
main(int argc, char *argv[]) |
296 |
|
|
{ |
297 |
|
|
extern char *__progname; |
298 |
|
|
|
299 |
|
|
int c; |
300 |
|
|
struct passwd *pw; |
301 |
|
|
|
302 |
|
|
char *dir = NULL; |
303 |
|
|
char *rewrite = NULL; |
304 |
|
|
|
305 |
|
|
char *addr = NULL; |
306 |
|
|
char *port = "tftp"; |
307 |
|
|
int family = AF_UNSPEC; |
308 |
|
|
int devnull = -1; |
309 |
|
|
|
310 |
|
|
while ((c = getopt(argc, argv, "46cdl:p:r:v")) != -1) { |
311 |
|
|
switch (c) { |
312 |
|
|
case '4': |
313 |
|
|
family = AF_INET; |
314 |
|
|
break; |
315 |
|
|
case '6': |
316 |
|
|
family = AF_INET6; |
317 |
|
|
break; |
318 |
|
|
case 'c': |
319 |
|
|
cancreate = 1; |
320 |
|
|
break; |
321 |
|
|
case 'd': |
322 |
|
|
verbose = debug = 1; |
323 |
|
|
break; |
324 |
|
|
case 'l': |
325 |
|
|
addr = optarg; |
326 |
|
|
break; |
327 |
|
|
case 'p': |
328 |
|
|
port = optarg; |
329 |
|
|
break; |
330 |
|
|
case 'r': |
331 |
|
|
rewrite = optarg; |
332 |
|
|
break; |
333 |
|
|
case 'v': |
334 |
|
|
verbose = 1; |
335 |
|
|
break; |
336 |
|
|
default: |
337 |
|
|
usage(); |
338 |
|
|
/* NOTREACHED */ |
339 |
|
|
} |
340 |
|
|
} |
341 |
|
|
|
342 |
|
|
argc -= optind; |
343 |
|
|
argv += optind; |
344 |
|
|
|
345 |
|
|
if (argc != 1) |
346 |
|
|
usage(); |
347 |
|
|
|
348 |
|
|
dir = argv[0]; |
349 |
|
|
|
350 |
|
|
if (geteuid() != 0) |
351 |
|
|
errx(1, "need root privileges"); |
352 |
|
|
|
353 |
|
|
pw = getpwnam("_tftpd"); |
354 |
|
|
if (pw == NULL) |
355 |
|
|
errx(1, "no _tftpd user"); |
356 |
|
|
|
357 |
|
|
if (!debug) { |
358 |
|
|
openlog(__progname, LOG_PID|LOG_NDELAY, LOG_DAEMON); |
359 |
|
|
tzset(); |
360 |
|
|
logger = &syslogger; |
361 |
|
|
devnull = open(_PATH_DEVNULL, O_RDWR, 0); |
362 |
|
|
if (devnull == -1) |
363 |
|
|
err(1, "open %s", _PATH_DEVNULL); |
364 |
|
|
} |
365 |
|
|
|
366 |
|
|
if (rewrite != NULL) |
367 |
|
|
rewrite_connect(rewrite); |
368 |
|
|
|
369 |
|
|
tftpd_listen(addr, port, family); |
370 |
|
|
|
371 |
|
|
if (chroot(dir)) |
372 |
|
|
err(1, "chroot %s", dir); |
373 |
|
|
if (chdir("/")) |
374 |
|
|
err(1, "chdir %s", dir); |
375 |
|
|
|
376 |
|
|
/* drop privs */ |
377 |
|
|
if (setgroups(1, &pw->pw_gid) || |
378 |
|
|
setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || |
379 |
|
|
setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) |
380 |
|
|
errx(1, "can't drop privileges"); |
381 |
|
|
|
382 |
|
|
if (!debug && rdaemon(devnull) == -1) |
383 |
|
|
err(1, "unable to daemonize"); |
384 |
|
|
|
385 |
|
|
if (pledge("stdio rpath wpath cpath fattr dns inet flock", NULL) == -1) |
386 |
|
|
lerr(1, "pledge"); |
387 |
|
|
|
388 |
|
|
event_init(); |
389 |
|
|
|
390 |
|
|
if (rewrite != NULL) |
391 |
|
|
rewrite_events(); |
392 |
|
|
|
393 |
|
|
tftpd_events(); |
394 |
|
|
|
395 |
|
|
event_dispatch(); |
396 |
|
|
|
397 |
|
|
exit(0); |
398 |
|
|
} |
399 |
|
|
|
400 |
|
|
struct rewritemap { |
401 |
|
|
struct event wrev; |
402 |
|
|
struct event rdev; |
403 |
|
|
struct evbuffer *wrbuf; |
404 |
|
|
struct evbuffer *rdbuf; |
405 |
|
|
|
406 |
|
|
TAILQ_HEAD(, tftp_client) clients; |
407 |
|
|
|
408 |
|
|
int s; |
409 |
|
|
}; |
410 |
|
|
|
411 |
|
|
struct rewritemap *rwmap = NULL; |
412 |
|
|
|
413 |
|
|
void |
414 |
|
|
rewrite_connect(const char *path) |
415 |
|
|
{ |
416 |
|
|
int s; |
417 |
|
|
struct sockaddr_un remote; |
418 |
|
|
size_t len; |
419 |
|
|
|
420 |
|
|
rwmap = malloc(sizeof(*rwmap)); |
421 |
|
|
if (rwmap == NULL) |
422 |
|
|
err(1, "rewrite event malloc"); |
423 |
|
|
|
424 |
|
|
rwmap->wrbuf = evbuffer_new(); |
425 |
|
|
if (rwmap->wrbuf == NULL) |
426 |
|
|
err(1, "rewrite wrbuf"); |
427 |
|
|
|
428 |
|
|
rwmap->rdbuf = evbuffer_new(); |
429 |
|
|
if (rwmap->rdbuf == NULL) |
430 |
|
|
err(1, "rewrite rdbuf"); |
431 |
|
|
|
432 |
|
|
TAILQ_INIT(&rwmap->clients); |
433 |
|
|
|
434 |
|
|
s = socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0); |
435 |
|
|
if (s == -1) |
436 |
|
|
err(1, "rewrite socket"); |
437 |
|
|
|
438 |
|
|
remote.sun_family = AF_UNIX; |
439 |
|
|
len = strlcpy(remote.sun_path, path, sizeof(remote.sun_path)); |
440 |
|
|
if (len >= sizeof(remote.sun_path)) |
441 |
|
|
errx(1, "rewrite socket path is too long"); |
442 |
|
|
|
443 |
|
|
len += sizeof(remote.sun_family) + 1; |
444 |
|
|
if (connect(s, (struct sockaddr *)&remote, len) == -1) |
445 |
|
|
err(1, "%s", path); |
446 |
|
|
|
447 |
|
|
rwmap->s = s; |
448 |
|
|
} |
449 |
|
|
|
450 |
|
|
void |
451 |
|
|
rewrite_events(void) |
452 |
|
|
{ |
453 |
|
|
event_set(&rwmap->wrev, rwmap->s, EV_WRITE, rewrite_req, NULL); |
454 |
|
|
event_set(&rwmap->rdev, rwmap->s, EV_READ | EV_PERSIST, rewrite_res, NULL); |
455 |
|
|
event_add(&rwmap->rdev, NULL); |
456 |
|
|
} |
457 |
|
|
|
458 |
|
|
void |
459 |
|
|
rewrite_map(struct tftp_client *client, const char *filename) |
460 |
|
|
{ |
461 |
|
|
char *nicebuf; |
462 |
|
|
|
463 |
|
|
if (stravis(&nicebuf, filename, VIS_SAFE|VIS_OCTAL) == -1) |
464 |
|
|
lerr(1, "rwmap stravis"); |
465 |
|
|
|
466 |
|
|
if (evbuffer_add_printf(rwmap->wrbuf, "%s %s %s\n", getip(&client->ss), |
467 |
|
|
client->opcode == WRQ ? "write" : "read", nicebuf) == -1) |
468 |
|
|
lerr(1, "rwmap printf"); |
469 |
|
|
|
470 |
|
|
free(nicebuf); |
471 |
|
|
|
472 |
|
|
TAILQ_INSERT_TAIL(&rwmap->clients, client, entry); |
473 |
|
|
|
474 |
|
|
event_add(&rwmap->wrev, NULL); |
475 |
|
|
} |
476 |
|
|
|
477 |
|
|
void |
478 |
|
|
rewrite_req(int fd, short events, void *arg) |
479 |
|
|
{ |
480 |
|
|
if (evbuffer_write(rwmap->wrbuf, fd) == -1) { |
481 |
|
|
switch (errno) { |
482 |
|
|
case EINTR: |
483 |
|
|
case EAGAIN: |
484 |
|
|
event_add(&rwmap->wrev, NULL); |
485 |
|
|
return; |
486 |
|
|
} |
487 |
|
|
|
488 |
|
|
lerr(1, "rewrite socket write"); |
489 |
|
|
} |
490 |
|
|
|
491 |
|
|
if (EVBUFFER_LENGTH(rwmap->wrbuf)) |
492 |
|
|
event_add(&rwmap->wrev, NULL); |
493 |
|
|
} |
494 |
|
|
|
495 |
|
|
void |
496 |
|
|
rewrite_res(int fd, short events, void *arg) |
497 |
|
|
{ |
498 |
|
|
struct tftp_client *client; |
499 |
|
|
char *filename; |
500 |
|
|
size_t len; |
501 |
|
|
|
502 |
|
|
switch (evbuffer_read(rwmap->rdbuf, fd, PATH_MAX)) { |
503 |
|
|
case -1: |
504 |
|
|
switch (errno) { |
505 |
|
|
case EINTR: |
506 |
|
|
case EAGAIN: |
507 |
|
|
return; |
508 |
|
|
} |
509 |
|
|
lerr(1, "rewrite socket read"); |
510 |
|
|
case 0: |
511 |
|
|
lerrx(1, "rewrite socket closed"); |
512 |
|
|
default: |
513 |
|
|
break; |
514 |
|
|
} |
515 |
|
|
|
516 |
|
|
while ((filename = evbuffer_readln(rwmap->rdbuf, &len, |
517 |
|
|
EVBUFFER_EOL_LF)) != NULL) { |
518 |
|
|
client = TAILQ_FIRST(&rwmap->clients); |
519 |
|
|
if (client == NULL) |
520 |
|
|
lerrx(1, "unexpected rwmap reply"); |
521 |
|
|
|
522 |
|
|
TAILQ_REMOVE(&rwmap->clients, client, entry); |
523 |
|
|
|
524 |
|
|
tftp_open(client, filename); |
525 |
|
|
|
526 |
|
|
free(filename); |
527 |
|
|
}; |
528 |
|
|
} |
529 |
|
|
|
530 |
|
|
int |
531 |
|
|
tftpd_listen(const char *addr, const char *port, int family) |
532 |
|
|
{ |
533 |
|
|
struct tftp_server *server; |
534 |
|
|
|
535 |
|
|
struct addrinfo hints, *res, *res0; |
536 |
|
|
int error; |
537 |
|
|
int s; |
538 |
|
|
|
539 |
|
|
int cerrno = EADDRNOTAVAIL; |
540 |
|
|
const char *cause = "getaddrinfo"; |
541 |
|
|
|
542 |
|
|
int on = 1; |
543 |
|
|
|
544 |
|
|
memset(&hints, 0, sizeof(hints)); |
545 |
|
|
hints.ai_family = family; |
546 |
|
|
hints.ai_socktype = SOCK_DGRAM; |
547 |
|
|
hints.ai_flags = AI_PASSIVE; |
548 |
|
|
|
549 |
|
|
TAILQ_INIT(&tftp_servers); |
550 |
|
|
|
551 |
|
|
error = getaddrinfo(addr, port, &hints, &res0); |
552 |
|
|
if (error) { |
553 |
|
|
errx(1, "%s:%s: %s", addr ? addr : "*", port, |
554 |
|
|
gai_strerror(error)); |
555 |
|
|
} |
556 |
|
|
|
557 |
|
|
for (res = res0; res != NULL; res = res->ai_next) { |
558 |
|
|
s = socket(res->ai_family, res->ai_socktype | SOCK_NONBLOCK, |
559 |
|
|
res->ai_protocol); |
560 |
|
|
if (s == -1) { |
561 |
|
|
cause = "socket"; |
562 |
|
|
cerrno = errno; |
563 |
|
|
continue; |
564 |
|
|
} |
565 |
|
|
|
566 |
|
|
if (bind(s, res->ai_addr, res->ai_addrlen) == -1) { |
567 |
|
|
cause = "bind"; |
568 |
|
|
cerrno = errno; |
569 |
|
|
close(s); |
570 |
|
|
continue; |
571 |
|
|
} |
572 |
|
|
|
573 |
|
|
switch (res->ai_family) { |
574 |
|
|
case AF_INET: |
575 |
|
|
if (setsockopt(s, IPPROTO_IP, IP_RECVDSTADDR, |
576 |
|
|
&on, sizeof(on)) == -1) |
577 |
|
|
err(1, "setsockopt(IP_RECVDSTADDR)"); |
578 |
|
|
break; |
579 |
|
|
case AF_INET6: |
580 |
|
|
if (setsockopt(s, IPPROTO_IPV6, IPV6_RECVPKTINFO, |
581 |
|
|
&on, sizeof(on)) == -1) |
582 |
|
|
err(1, "setsockopt(IPV6_RECVPKTINFO)"); |
583 |
|
|
break; |
584 |
|
|
} |
585 |
|
|
|
586 |
|
|
server = malloc(sizeof(*server)); |
587 |
|
|
if (server == NULL) |
588 |
|
|
err(1, "malloc"); |
589 |
|
|
|
590 |
|
|
server->s = s; |
591 |
|
|
TAILQ_INSERT_TAIL(&tftp_servers, server, entry); |
592 |
|
|
} |
593 |
|
|
|
594 |
|
|
if (TAILQ_EMPTY(&tftp_servers)) |
595 |
|
|
errc(1, cerrno, "%s", cause); |
596 |
|
|
|
597 |
|
|
freeaddrinfo(res0); |
598 |
|
|
return (0); |
599 |
|
|
} |
600 |
|
|
|
601 |
|
|
void |
602 |
|
|
tftpd_events(void) |
603 |
|
|
{ |
604 |
|
|
struct tftp_server *server; |
605 |
|
|
TAILQ_FOREACH(server, &tftp_servers, entry) { |
606 |
|
|
event_set(&server->ev, server->s, EV_READ | EV_PERSIST, |
607 |
|
|
tftpd_recv, server); |
608 |
|
|
event_add(&server->ev, NULL); |
609 |
|
|
} |
610 |
|
|
} |
611 |
|
|
|
612 |
|
|
struct tftp_client * |
613 |
|
|
client_alloc(void) |
614 |
|
|
{ |
615 |
|
|
struct tftp_client *client; |
616 |
|
|
|
617 |
|
|
client = calloc(1, sizeof(*client)); |
618 |
|
|
if (client == NULL) |
619 |
|
|
return (NULL); |
620 |
|
|
|
621 |
|
|
client->segment_size = SEGSIZE; |
622 |
|
|
client->packet_size = SEGSIZE + 4; |
623 |
|
|
|
624 |
|
|
client->tv.tv_sec = TIMEOUT; |
625 |
|
|
client->tv.tv_usec = 0; |
626 |
|
|
|
627 |
|
|
client->sock = -1; |
628 |
|
|
client->file = NULL; |
629 |
|
|
client->newline = 0; |
630 |
|
|
|
631 |
|
|
return (client); |
632 |
|
|
} |
633 |
|
|
|
634 |
|
|
void |
635 |
|
|
client_free(struct tftp_client *client) |
636 |
|
|
{ |
637 |
|
|
free(client->options); |
638 |
|
|
|
639 |
|
|
if (client->file != NULL) |
640 |
|
|
fclose(client->file); |
641 |
|
|
|
642 |
|
|
close(client->sock); |
643 |
|
|
|
644 |
|
|
free(client); |
645 |
|
|
} |
646 |
|
|
|
647 |
|
|
void |
648 |
|
|
tftpd_recv(int fd, short events, void *arg) |
649 |
|
|
{ |
650 |
|
|
union { |
651 |
|
|
struct cmsghdr hdr; |
652 |
|
|
char buf[CMSG_SPACE(sizeof(struct sockaddr_storage))]; |
653 |
|
|
} cmsgbuf; |
654 |
|
|
struct cmsghdr *cmsg; |
655 |
|
|
struct msghdr msg; |
656 |
|
|
struct iovec iov; |
657 |
|
|
|
658 |
|
|
ssize_t n; |
659 |
|
|
struct sockaddr_storage s_in; |
660 |
|
|
int dobind = 1; |
661 |
|
|
int on = 1; |
662 |
|
|
|
663 |
|
|
struct tftphdr *tp; |
664 |
|
|
|
665 |
|
|
struct tftp_client *client; |
666 |
|
|
|
667 |
|
|
client = client_alloc(); |
668 |
|
|
if (client == NULL) { |
669 |
|
|
char buf[SEGSIZE_MAX + 4]; |
670 |
|
|
/* no memory! flush this request... */ |
671 |
|
|
recv(fd, buf, SEGSIZE_MAX + 4, 0); |
672 |
|
|
/* dont care if it fails */ |
673 |
|
|
return; |
674 |
|
|
} |
675 |
|
|
|
676 |
|
|
bzero(&msg, sizeof(msg)); |
677 |
|
|
iov.iov_base = client->buf; |
678 |
|
|
iov.iov_len = client->packet_size; |
679 |
|
|
msg.msg_name = &client->ss; |
680 |
|
|
msg.msg_namelen = sizeof(client->ss); |
681 |
|
|
msg.msg_iov = &iov; |
682 |
|
|
msg.msg_iovlen = 1; |
683 |
|
|
msg.msg_control = &cmsgbuf.buf; |
684 |
|
|
msg.msg_controllen = sizeof(cmsgbuf.buf); |
685 |
|
|
|
686 |
|
|
n = recvmsg(fd, &msg, 0); |
687 |
|
|
if (n == -1) { |
688 |
|
|
lwarn("recvmsg"); |
689 |
|
|
goto err; |
690 |
|
|
} |
691 |
|
|
if (n < 4) |
692 |
|
|
goto err; |
693 |
|
|
|
694 |
|
|
client->sock = socket(client->ss.ss_family, |
695 |
|
|
SOCK_DGRAM | SOCK_NONBLOCK, 0); |
696 |
|
|
if (client->sock == -1) { |
697 |
|
|
lwarn("socket"); |
698 |
|
|
goto err; |
699 |
|
|
} |
700 |
|
|
memset(&s_in, 0, sizeof(s_in)); |
701 |
|
|
s_in.ss_family = client->ss.ss_family; |
702 |
|
|
s_in.ss_len = client->ss.ss_len; |
703 |
|
|
|
704 |
|
|
/* get local address if possible */ |
705 |
|
|
for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; |
706 |
|
|
cmsg = CMSG_NXTHDR(&msg, cmsg)) { |
707 |
|
|
if (cmsg->cmsg_level == IPPROTO_IP && |
708 |
|
|
cmsg->cmsg_type == IP_RECVDSTADDR) { |
709 |
|
|
memcpy(&((struct sockaddr_in *)&s_in)->sin_addr, |
710 |
|
|
CMSG_DATA(cmsg), sizeof(struct in_addr)); |
711 |
|
|
if (((struct sockaddr_in *)&s_in)->sin_addr.s_addr == |
712 |
|
|
INADDR_BROADCAST) |
713 |
|
|
dobind = 0; |
714 |
|
|
break; |
715 |
|
|
} |
716 |
|
|
if (cmsg->cmsg_level == IPPROTO_IPV6 && |
717 |
|
|
cmsg->cmsg_type == IPV6_PKTINFO) { |
718 |
|
|
struct in6_pktinfo *ipi; |
719 |
|
|
|
720 |
|
|
ipi = (struct in6_pktinfo *)CMSG_DATA(cmsg); |
721 |
|
|
memcpy(&((struct sockaddr_in6 *)&s_in)->sin6_addr, |
722 |
|
|
&ipi->ipi6_addr, sizeof(struct in6_addr)); |
723 |
|
|
#ifdef __KAME__ |
724 |
|
|
if (IN6_IS_ADDR_LINKLOCAL(&ipi->ipi6_addr)) |
725 |
|
|
((struct sockaddr_in6 *)&s_in)->sin6_scope_id = |
726 |
|
|
ipi->ipi6_ifindex; |
727 |
|
|
#endif |
728 |
|
|
break; |
729 |
|
|
} |
730 |
|
|
} |
731 |
|
|
|
732 |
|
|
if (dobind) { |
733 |
|
|
setsockopt(client->sock, SOL_SOCKET, SO_REUSEADDR, |
734 |
|
|
&on, sizeof(on)); |
735 |
|
|
setsockopt(client->sock, SOL_SOCKET, SO_REUSEPORT, |
736 |
|
|
&on, sizeof(on)); |
737 |
|
|
|
738 |
|
|
if (bind(client->sock, (struct sockaddr *)&s_in, |
739 |
|
|
s_in.ss_len) < 0) { |
740 |
|
|
lwarn("bind to %s", getip(&s_in)); |
741 |
|
|
goto err; |
742 |
|
|
} |
743 |
|
|
} |
744 |
|
|
if (connect(client->sock, (struct sockaddr *)&client->ss, |
745 |
|
|
client->ss.ss_len) == -1) { |
746 |
|
|
lwarn("connect to %s", getip(&client->ss)); |
747 |
|
|
goto err; |
748 |
|
|
} |
749 |
|
|
|
750 |
|
|
tp = (struct tftphdr *)client->buf; |
751 |
|
|
client->opcode = ntohs(tp->th_opcode); |
752 |
|
|
if (client->opcode != RRQ && client->opcode != WRQ) { |
753 |
|
|
/* bad request */ |
754 |
|
|
goto err; |
755 |
|
|
} |
756 |
|
|
|
757 |
|
|
tftp(client, tp, n); |
758 |
|
|
|
759 |
|
|
return; |
760 |
|
|
|
761 |
|
|
err: |
762 |
|
|
client_free(client); |
763 |
|
|
} |
764 |
|
|
|
765 |
|
|
int |
766 |
|
|
parse_options(struct tftp_client *client, char *cp, size_t size, |
767 |
|
|
struct opt_client *options) |
768 |
|
|
{ |
769 |
|
|
char *option; |
770 |
|
|
char *ccp; |
771 |
|
|
int has_options = 0; |
772 |
|
|
int i; |
773 |
|
|
|
774 |
|
|
while (++cp < client->buf + size) { |
775 |
|
|
for (i = 2, ccp = cp; i > 0; ccp++) { |
776 |
|
|
if (ccp >= client->buf + size) { |
777 |
|
|
/* |
778 |
|
|
* Don't reject the request, just stop trying |
779 |
|
|
* to parse the option and get on with it. |
780 |
|
|
* Some Apple OpenFirmware versions have |
781 |
|
|
* trailing garbage on the end of otherwise |
782 |
|
|
* valid requests. |
783 |
|
|
*/ |
784 |
|
|
return (has_options); |
785 |
|
|
} else if (*ccp == '\0') |
786 |
|
|
i--; |
787 |
|
|
} |
788 |
|
|
|
789 |
|
|
for (option = cp; *cp; cp++) |
790 |
|
|
*cp = tolower((unsigned char)*cp); |
791 |
|
|
|
792 |
|
|
for (i = 0; i < NOPT; i++) { |
793 |
|
|
if (strcmp(option, opt_names[i]) == 0) { |
794 |
|
|
options[i].o_request = ++cp; |
795 |
|
|
has_options = 1; |
796 |
|
|
} |
797 |
|
|
} |
798 |
|
|
cp = ccp - 1; |
799 |
|
|
} |
800 |
|
|
|
801 |
|
|
return (has_options); |
802 |
|
|
} |
803 |
|
|
|
804 |
|
|
/* |
805 |
|
|
* Handle initial connection protocol. |
806 |
|
|
*/ |
807 |
|
|
void |
808 |
|
|
tftp(struct tftp_client *client, struct tftphdr *tp, size_t size) |
809 |
|
|
{ |
810 |
|
|
struct opt_client *options; |
811 |
|
|
|
812 |
|
|
char *cp; |
813 |
|
|
int i, first = 1, ecode, to; |
814 |
|
|
struct formats *pf; |
815 |
|
|
char *mode = NULL; |
816 |
|
|
char filename[PATH_MAX]; |
817 |
|
|
const char *errstr; |
818 |
|
|
|
819 |
|
|
if (size < 5) { |
820 |
|
|
ecode = EBADOP; |
821 |
|
|
goto error; |
822 |
|
|
} |
823 |
|
|
|
824 |
|
|
cp = tp->th_stuff; |
825 |
|
|
again: |
826 |
|
|
while (cp < client->buf + size) { |
827 |
|
|
if (*cp == '\0') |
828 |
|
|
break; |
829 |
|
|
cp++; |
830 |
|
|
} |
831 |
|
|
if (*cp != '\0') { |
832 |
|
|
ecode = EBADOP; |
833 |
|
|
goto error; |
834 |
|
|
} |
835 |
|
|
i = cp - tp->th_stuff; |
836 |
|
|
if (i >= sizeof(filename)) { |
837 |
|
|
ecode = EBADOP; |
838 |
|
|
goto error; |
839 |
|
|
} |
840 |
|
|
memcpy(filename, tp->th_stuff, i); |
841 |
|
|
filename[i] = '\0'; |
842 |
|
|
if (first) { |
843 |
|
|
mode = ++cp; |
844 |
|
|
first = 0; |
845 |
|
|
goto again; |
846 |
|
|
} |
847 |
|
|
for (cp = mode; *cp; cp++) |
848 |
|
|
*cp = tolower((unsigned char)*cp); |
849 |
|
|
|
850 |
|
|
for (pf = formats; pf->f_mode; pf++) { |
851 |
|
|
if (strcmp(pf->f_mode, mode) == 0) |
852 |
|
|
break; |
853 |
|
|
} |
854 |
|
|
if (pf->f_mode == 0) { |
855 |
|
|
ecode = EBADOP; |
856 |
|
|
goto error; |
857 |
|
|
} |
858 |
|
|
client->fgetc = pf->f_getc; |
859 |
|
|
client->fputc = pf->f_putc; |
860 |
|
|
|
861 |
|
|
client->options = options = calloc(NOPT, sizeof(*client->options)); |
862 |
|
|
if (options == NULL) { |
863 |
|
|
ecode = 100 + ENOMEM; |
864 |
|
|
goto error; |
865 |
|
|
} |
866 |
|
|
|
867 |
|
|
if (parse_options(client, cp, size, options)) { |
868 |
|
|
if (options[OPT_TIMEOUT].o_request != NULL) { |
869 |
|
|
to = strtonum(options[OPT_TIMEOUT].o_request, |
870 |
|
|
TIMEOUT_MIN, TIMEOUT_MAX, &errstr); |
871 |
|
|
if (errstr) { |
872 |
|
|
ecode = EBADOP; |
873 |
|
|
goto error; |
874 |
|
|
} |
875 |
|
|
options[OPT_TIMEOUT].o_reply = client->tv.tv_sec = to; |
876 |
|
|
} |
877 |
|
|
|
878 |
|
|
if (options[OPT_BLKSIZE].o_request) { |
879 |
|
|
client->segment_size = strtonum( |
880 |
|
|
options[OPT_BLKSIZE].o_request, |
881 |
|
|
SEGSIZE_MIN, SEGSIZE_MAX, &errstr); |
882 |
|
|
if (errstr) { |
883 |
|
|
ecode = EBADOP; |
884 |
|
|
goto error; |
885 |
|
|
} |
886 |
|
|
client->packet_size = client->segment_size + 4; |
887 |
|
|
options[OPT_BLKSIZE].o_reply = client->segment_size; |
888 |
|
|
} |
889 |
|
|
} else { |
890 |
|
|
free(options); |
891 |
|
|
client->options = NULL; |
892 |
|
|
} |
893 |
|
|
|
894 |
|
|
if (verbose) { |
895 |
|
|
char nicebuf[PATH_MAX]; |
896 |
|
|
|
897 |
|
|
(void)strnvis(nicebuf, filename, PATH_MAX, |
898 |
|
|
VIS_SAFE|VIS_OCTAL); |
899 |
|
|
|
900 |
|
|
linfo("%s: %s request for '%s'", getip(&client->ss), |
901 |
|
|
client->opcode == WRQ ? "write" : "read", nicebuf); |
902 |
|
|
} |
903 |
|
|
|
904 |
|
|
if (rwmap != NULL) |
905 |
|
|
rewrite_map(client, filename); |
906 |
|
|
else |
907 |
|
|
tftp_open(client, filename); |
908 |
|
|
|
909 |
|
|
return; |
910 |
|
|
|
911 |
|
|
error: |
912 |
|
|
nak(client, ecode); |
913 |
|
|
} |
914 |
|
|
|
915 |
|
|
void |
916 |
|
|
tftp_open(struct tftp_client *client, const char *filename) |
917 |
|
|
{ |
918 |
|
|
int ecode; |
919 |
|
|
|
920 |
|
|
ecode = validate_access(client, filename); |
921 |
|
|
if (ecode) |
922 |
|
|
goto error; |
923 |
|
|
|
924 |
|
|
if (client->options) { |
925 |
|
|
if (oack(client) == -1) |
926 |
|
|
goto error; |
927 |
|
|
|
928 |
|
|
free(client->options); |
929 |
|
|
client->options = NULL; |
930 |
|
|
} else if (client->opcode == WRQ) { |
931 |
|
|
recvfile(client); |
932 |
|
|
} else |
933 |
|
|
sendfile(client); |
934 |
|
|
|
935 |
|
|
return; |
936 |
|
|
error: |
937 |
|
|
nak(client, ecode); |
938 |
|
|
} |
939 |
|
|
|
940 |
|
|
/* |
941 |
|
|
* Validate file access. Since we |
942 |
|
|
* have no uid or gid, for now require |
943 |
|
|
* file to exist and be publicly |
944 |
|
|
* readable/writable. |
945 |
|
|
* If we were invoked with arguments |
946 |
|
|
* from inetd then the file must also be |
947 |
|
|
* in one of the given directory prefixes. |
948 |
|
|
* Note also, full path name must be |
949 |
|
|
* given as we have no login directory. |
950 |
|
|
*/ |
951 |
|
|
int |
952 |
|
|
validate_access(struct tftp_client *client, const char *filename) |
953 |
|
|
{ |
954 |
|
|
int mode = client->opcode; |
955 |
|
|
struct opt_client *options = client->options; |
956 |
|
|
struct stat stbuf; |
957 |
|
|
int fd, wmode; |
958 |
|
|
const char *errstr; |
959 |
|
|
|
960 |
|
|
if (strcmp(filename, SEEDPATH) == 0) { |
961 |
|
|
char *buf; |
962 |
|
|
if (mode != RRQ) |
963 |
|
|
return (EACCESS); |
964 |
|
|
|
965 |
|
|
buf = client->buf + sizeof(client->buf) - 512; |
966 |
|
|
arc4random_buf(buf, 512); |
967 |
|
|
client->file = fmemopen(buf, 512, "r"); |
968 |
|
|
if (client->file == NULL) |
969 |
|
|
return (errno + 100); |
970 |
|
|
|
971 |
|
|
return (0); |
972 |
|
|
} |
973 |
|
|
|
974 |
|
|
/* |
975 |
|
|
* We use a different permissions scheme if `cancreate' is |
976 |
|
|
* set. |
977 |
|
|
*/ |
978 |
|
|
wmode = O_TRUNC; |
979 |
|
|
if (stat(filename, &stbuf) < 0) { |
980 |
|
|
if (!cancreate) |
981 |
|
|
return (errno == ENOENT ? ENOTFOUND : EACCESS); |
982 |
|
|
else { |
983 |
|
|
if ((errno == ENOENT) && (mode != RRQ)) |
984 |
|
|
wmode |= O_CREAT; |
985 |
|
|
else |
986 |
|
|
return (EACCESS); |
987 |
|
|
} |
988 |
|
|
} else { |
989 |
|
|
if (mode == RRQ) { |
990 |
|
|
if ((stbuf.st_mode & (S_IRUSR >> 6)) == 0) |
991 |
|
|
return (EACCESS); |
992 |
|
|
} else { |
993 |
|
|
if ((stbuf.st_mode & (S_IWUSR >> 6)) == 0) |
994 |
|
|
return (EACCESS); |
995 |
|
|
} |
996 |
|
|
} |
997 |
|
|
|
998 |
|
|
if (options != NULL && options[OPT_TSIZE].o_request) { |
999 |
|
|
if (mode == RRQ) |
1000 |
|
|
options[OPT_TSIZE].o_reply = stbuf.st_size; |
1001 |
|
|
else { |
1002 |
|
|
/* allows writes of 65535 blocks * SEGSIZE_MAX bytes */ |
1003 |
|
|
options[OPT_TSIZE].o_reply = |
1004 |
|
|
strtonum(options[OPT_TSIZE].o_request, |
1005 |
|
|
1, 65535LL * SEGSIZE_MAX, &errstr); |
1006 |
|
|
if (errstr) |
1007 |
|
|
return (EOPTNEG); |
1008 |
|
|
} |
1009 |
|
|
} |
1010 |
|
|
fd = open(filename, mode == RRQ ? O_RDONLY : (O_WRONLY|wmode), 0666); |
1011 |
|
|
if (fd < 0) |
1012 |
|
|
return (errno + 100); |
1013 |
|
|
/* |
1014 |
|
|
* If the file was created, set default permissions. |
1015 |
|
|
*/ |
1016 |
|
|
if ((wmode & O_CREAT) && fchmod(fd, 0666) < 0) { |
1017 |
|
|
int serrno = errno; |
1018 |
|
|
|
1019 |
|
|
close(fd); |
1020 |
|
|
unlink(filename); |
1021 |
|
|
|
1022 |
|
|
return (serrno + 100); |
1023 |
|
|
} |
1024 |
|
|
client->file = fdopen(fd, mode == RRQ ? "r" : "w"); |
1025 |
|
|
if (client->file == NULL) { |
1026 |
|
|
close(fd); |
1027 |
|
|
return (errno + 100); |
1028 |
|
|
} |
1029 |
|
|
|
1030 |
|
|
return (0); |
1031 |
|
|
} |
1032 |
|
|
|
1033 |
|
|
int |
1034 |
|
|
fget_octet(struct tftp_client *client) |
1035 |
|
|
{ |
1036 |
|
|
return (getc(client->file)); |
1037 |
|
|
} |
1038 |
|
|
|
1039 |
|
|
int |
1040 |
|
|
fput_octet(struct tftp_client *client, int c) |
1041 |
|
|
{ |
1042 |
|
|
return (putc(c, client->file)); |
1043 |
|
|
} |
1044 |
|
|
|
1045 |
|
|
int |
1046 |
|
|
fget_netascii(struct tftp_client *client) |
1047 |
|
|
{ |
1048 |
|
|
int c = -1; |
1049 |
|
|
|
1050 |
|
|
switch (client->newline) { |
1051 |
|
|
case 0: |
1052 |
|
|
c = getc(client->file); |
1053 |
|
|
if (c == EOF) |
1054 |
|
|
break; |
1055 |
|
|
|
1056 |
|
|
if (c == '\n' || c == '\r') { |
1057 |
|
|
client->newline = c; |
1058 |
|
|
c = '\r'; |
1059 |
|
|
} |
1060 |
|
|
break; |
1061 |
|
|
case '\n': |
1062 |
|
|
client->newline = 0; |
1063 |
|
|
c = '\n'; |
1064 |
|
|
break; |
1065 |
|
|
case '\r': |
1066 |
|
|
client->newline = 0; |
1067 |
|
|
c = '\0'; |
1068 |
|
|
break; |
1069 |
|
|
} |
1070 |
|
|
|
1071 |
|
|
return (c); |
1072 |
|
|
} |
1073 |
|
|
|
1074 |
|
|
int |
1075 |
|
|
fput_netascii(struct tftp_client *client, int c) |
1076 |
|
|
{ |
1077 |
|
|
if (client->newline == '\r') { |
1078 |
|
|
client->newline = 0; |
1079 |
|
|
|
1080 |
|
|
if (c == '\0') |
1081 |
|
|
c = '\r'; |
1082 |
|
|
|
1083 |
|
|
} else if (c == '\r') { |
1084 |
|
|
client->newline = c; |
1085 |
|
|
return (c); |
1086 |
|
|
} |
1087 |
|
|
|
1088 |
|
|
return (putc(c, client->file)); |
1089 |
|
|
} |
1090 |
|
|
|
1091 |
|
|
void |
1092 |
|
|
sendfile(struct tftp_client *client) |
1093 |
|
|
{ |
1094 |
|
|
event_set(&client->sev, client->sock, EV_READ, tftp_rrq_ack, client); |
1095 |
|
|
client->block = 1; |
1096 |
|
|
|
1097 |
|
|
file_read(client); |
1098 |
|
|
} |
1099 |
|
|
|
1100 |
|
|
void |
1101 |
|
|
file_read(struct tftp_client *client) |
1102 |
|
|
{ |
1103 |
|
|
u_int8_t *buf; |
1104 |
|
|
struct tftphdr *dp; |
1105 |
|
|
int i; |
1106 |
|
|
int c; |
1107 |
|
|
|
1108 |
|
|
dp = (struct tftphdr *)client->buf; |
1109 |
|
|
dp->th_opcode = htons((u_short)DATA); |
1110 |
|
|
dp->th_block = htons(client->block); |
1111 |
|
|
buf = (u_int8_t *)dp->th_data; |
1112 |
|
|
|
1113 |
|
|
for (i = 0; i < client->segment_size; i++) { |
1114 |
|
|
c = client->fgetc(client); |
1115 |
|
|
if (c == EOF) { |
1116 |
|
|
if (ferror(client->file)) { |
1117 |
|
|
nak(client, 100 + EIO); |
1118 |
|
|
return; |
1119 |
|
|
} |
1120 |
|
|
|
1121 |
|
|
break; |
1122 |
|
|
} |
1123 |
|
|
buf[i] = c; |
1124 |
|
|
} |
1125 |
|
|
|
1126 |
|
|
client->buflen = i + 4; |
1127 |
|
|
client->retries = RETRIES; |
1128 |
|
|
|
1129 |
|
|
if (send(client->sock, client->buf, client->buflen, 0) == -1) { |
1130 |
|
|
lwarn("send(block)"); |
1131 |
|
|
client_free(client); |
1132 |
|
|
return; |
1133 |
|
|
} |
1134 |
|
|
|
1135 |
|
|
event_add(&client->sev, &client->tv); |
1136 |
|
|
} |
1137 |
|
|
|
1138 |
|
|
void |
1139 |
|
|
tftp_rrq_ack(int fd, short events, void *arg) |
1140 |
|
|
{ |
1141 |
|
|
struct tftp_client *client = arg; |
1142 |
|
|
struct tftphdr *ap; /* ack packet */ |
1143 |
|
|
char rbuf[SEGSIZE_MIN]; |
1144 |
|
|
ssize_t n; |
1145 |
|
|
|
1146 |
|
|
if (events & EV_TIMEOUT) { |
1147 |
|
|
if (retry(client) == -1) { |
1148 |
|
|
lwarn("%s: retry", getip(&client->ss)); |
1149 |
|
|
goto done; |
1150 |
|
|
} |
1151 |
|
|
|
1152 |
|
|
return; |
1153 |
|
|
} |
1154 |
|
|
|
1155 |
|
|
n = recv(fd, rbuf, sizeof(rbuf), 0); |
1156 |
|
|
if (n == -1) { |
1157 |
|
|
switch (errno) { |
1158 |
|
|
case EINTR: |
1159 |
|
|
case EAGAIN: |
1160 |
|
|
event_add(&client->sev, &client->tv); |
1161 |
|
|
return; |
1162 |
|
|
|
1163 |
|
|
default: |
1164 |
|
|
lwarn("%s: recv", getip(&client->ss)); |
1165 |
|
|
goto done; |
1166 |
|
|
} |
1167 |
|
|
} |
1168 |
|
|
|
1169 |
|
|
ap = (struct tftphdr *)rbuf; |
1170 |
|
|
ap->th_opcode = ntohs((u_short)ap->th_opcode); |
1171 |
|
|
ap->th_block = ntohs((u_short)ap->th_block); |
1172 |
|
|
|
1173 |
|
|
switch (ap->th_opcode) { |
1174 |
|
|
case ERROR: |
1175 |
|
|
goto done; |
1176 |
|
|
case ACK: |
1177 |
|
|
break; |
1178 |
|
|
default: |
1179 |
|
|
goto retry; |
1180 |
|
|
} |
1181 |
|
|
|
1182 |
|
|
if (ap->th_block != client->block) { |
1183 |
|
|
if (tftp_flush(client) == -1) { |
1184 |
|
|
lwarnx("%s: flush", getip(&client->ss)); |
1185 |
|
|
goto done; |
1186 |
|
|
} |
1187 |
|
|
|
1188 |
|
|
if (ap->th_block != (client->block - 1)) |
1189 |
|
|
goto done; |
1190 |
|
|
|
1191 |
|
|
goto retry; |
1192 |
|
|
} |
1193 |
|
|
|
1194 |
|
|
if (client->buflen != client->packet_size) { |
1195 |
|
|
/* this was the last packet in the stream */ |
1196 |
|
|
goto done; |
1197 |
|
|
} |
1198 |
|
|
|
1199 |
|
|
client->block++; |
1200 |
|
|
file_read(client); |
1201 |
|
|
return; |
1202 |
|
|
|
1203 |
|
|
retry: |
1204 |
|
|
event_add(&client->sev, &client->tv); |
1205 |
|
|
return; |
1206 |
|
|
|
1207 |
|
|
done: |
1208 |
|
|
client_free(client); |
1209 |
|
|
} |
1210 |
|
|
|
1211 |
|
|
int |
1212 |
|
|
tftp_flush(struct tftp_client *client) |
1213 |
|
|
{ |
1214 |
|
|
char rbuf[SEGSIZE_MIN]; |
1215 |
|
|
ssize_t n; |
1216 |
|
|
|
1217 |
|
|
for (;;) { |
1218 |
|
|
n = recv(client->sock, rbuf, sizeof(rbuf), 0); |
1219 |
|
|
if (n == -1) { |
1220 |
|
|
switch (errno) { |
1221 |
|
|
case EAGAIN: |
1222 |
|
|
return (0); |
1223 |
|
|
|
1224 |
|
|
case EINTR: |
1225 |
|
|
break; |
1226 |
|
|
|
1227 |
|
|
default: |
1228 |
|
|
return (-1); |
1229 |
|
|
} |
1230 |
|
|
} |
1231 |
|
|
} |
1232 |
|
|
} |
1233 |
|
|
|
1234 |
|
|
void |
1235 |
|
|
recvfile(struct tftp_client *client) |
1236 |
|
|
{ |
1237 |
|
|
event_set(&client->sev, client->sock, EV_READ, tftp_wrq, client); |
1238 |
|
|
tftp_wrq_ack(client); |
1239 |
|
|
} |
1240 |
|
|
|
1241 |
|
|
int |
1242 |
|
|
tftp_wrq_ack_packet(struct tftp_client *client) |
1243 |
|
|
{ |
1244 |
|
|
struct tftphdr *ap; /* ack packet */ |
1245 |
|
|
|
1246 |
|
|
ap = (struct tftphdr *)client->buf; |
1247 |
|
|
ap->th_opcode = htons((u_short)ACK); |
1248 |
|
|
ap->th_block = htons(client->block); |
1249 |
|
|
|
1250 |
|
|
client->buflen = 4; |
1251 |
|
|
client->retries = RETRIES; |
1252 |
|
|
|
1253 |
|
|
return (send(client->sock, client->buf, client->buflen, 0) != 4); |
1254 |
|
|
} |
1255 |
|
|
|
1256 |
|
|
void |
1257 |
|
|
tftp_wrq_ack(struct tftp_client *client) |
1258 |
|
|
{ |
1259 |
|
|
if (tftp_wrq_ack_packet(client) != 0) { |
1260 |
|
|
lwarn("tftp wrq ack"); |
1261 |
|
|
client_free(client); |
1262 |
|
|
return; |
1263 |
|
|
} |
1264 |
|
|
|
1265 |
|
|
client->block++; |
1266 |
|
|
event_add(&client->sev, &client->tv); |
1267 |
|
|
} |
1268 |
|
|
|
1269 |
|
|
void |
1270 |
|
|
tftp_wrq(int fd, short events, void *arg) |
1271 |
|
|
{ |
1272 |
|
|
char wbuf[SEGSIZE_MAX + 4]; |
1273 |
|
|
struct tftp_client *client = arg; |
1274 |
|
|
struct tftphdr *dp; |
1275 |
|
|
ssize_t n; |
1276 |
|
|
int i; |
1277 |
|
|
|
1278 |
|
|
if (events & EV_TIMEOUT) { |
1279 |
|
|
if (retry(client) == -1) { |
1280 |
|
|
lwarn("%s", getip(&client->ss)); |
1281 |
|
|
goto done; |
1282 |
|
|
} |
1283 |
|
|
|
1284 |
|
|
return; |
1285 |
|
|
} |
1286 |
|
|
|
1287 |
|
|
n = recv(fd, wbuf, client->packet_size, 0); |
1288 |
|
|
if (n == -1) { |
1289 |
|
|
switch (errno) { |
1290 |
|
|
case EINTR: |
1291 |
|
|
case EAGAIN: |
1292 |
|
|
goto retry; |
1293 |
|
|
|
1294 |
|
|
default: |
1295 |
|
|
lwarn("tftp_wrq recv"); |
1296 |
|
|
goto done; |
1297 |
|
|
} |
1298 |
|
|
} |
1299 |
|
|
|
1300 |
|
|
if (n < 4) |
1301 |
|
|
goto done; |
1302 |
|
|
|
1303 |
|
|
dp = (struct tftphdr *)wbuf; |
1304 |
|
|
dp->th_opcode = ntohs((u_short)dp->th_opcode); |
1305 |
|
|
dp->th_block = ntohs((u_short)dp->th_block); |
1306 |
|
|
|
1307 |
|
|
switch (dp->th_opcode) { |
1308 |
|
|
case ERROR: |
1309 |
|
|
goto done; |
1310 |
|
|
case DATA: |
1311 |
|
|
break; |
1312 |
|
|
default: |
1313 |
|
|
goto retry; |
1314 |
|
|
} |
1315 |
|
|
|
1316 |
|
|
if (dp->th_block != client->block) { |
1317 |
|
|
if (tftp_flush(client) == -1) { |
1318 |
|
|
lwarnx("%s: flush", getip(&client->ss)); |
1319 |
|
|
goto done; |
1320 |
|
|
} |
1321 |
|
|
|
1322 |
|
|
if (dp->th_block != (client->block - 1)) |
1323 |
|
|
goto done; |
1324 |
|
|
|
1325 |
|
|
goto retry; |
1326 |
|
|
} |
1327 |
|
|
|
1328 |
|
|
for (i = 4; i < n; i++) { |
1329 |
|
|
if (client->fputc(client, wbuf[i]) == EOF) { |
1330 |
|
|
lwarn("tftp wrq"); |
1331 |
|
|
goto done; |
1332 |
|
|
} |
1333 |
|
|
} |
1334 |
|
|
|
1335 |
|
|
if (n < client->packet_size) { |
1336 |
|
|
tftp_wrq_ack_packet(client); |
1337 |
|
|
fclose(client->file); |
1338 |
|
|
client->file = NULL; |
1339 |
|
|
event_set(&client->sev, client->sock, EV_READ, |
1340 |
|
|
tftp_wrq_end, client); |
1341 |
|
|
event_add(&client->sev, &client->tv); |
1342 |
|
|
return; |
1343 |
|
|
} |
1344 |
|
|
|
1345 |
|
|
tftp_wrq_ack(client); |
1346 |
|
|
return; |
1347 |
|
|
|
1348 |
|
|
retry: |
1349 |
|
|
event_add(&client->sev, &client->tv); |
1350 |
|
|
return; |
1351 |
|
|
done: |
1352 |
|
|
client_free(client); |
1353 |
|
|
} |
1354 |
|
|
|
1355 |
|
|
void |
1356 |
|
|
tftp_wrq_end(int fd, short events, void *arg) |
1357 |
|
|
{ |
1358 |
|
|
char wbuf[SEGSIZE_MAX + 4]; |
1359 |
|
|
struct tftp_client *client = arg; |
1360 |
|
|
struct tftphdr *dp; |
1361 |
|
|
ssize_t n; |
1362 |
|
|
|
1363 |
|
|
if (events & EV_TIMEOUT) { |
1364 |
|
|
/* this was the last packet, we can clean up */ |
1365 |
|
|
goto done; |
1366 |
|
|
} |
1367 |
|
|
|
1368 |
|
|
n = recv(fd, wbuf, client->packet_size, 0); |
1369 |
|
|
if (n == -1) { |
1370 |
|
|
switch (errno) { |
1371 |
|
|
case EINTR: |
1372 |
|
|
case EAGAIN: |
1373 |
|
|
goto retry; |
1374 |
|
|
|
1375 |
|
|
default: |
1376 |
|
|
lwarn("tftp_wrq_end recv"); |
1377 |
|
|
goto done; |
1378 |
|
|
} |
1379 |
|
|
} |
1380 |
|
|
|
1381 |
|
|
if (n < 4) |
1382 |
|
|
goto done; |
1383 |
|
|
|
1384 |
|
|
dp = (struct tftphdr *)wbuf; |
1385 |
|
|
dp->th_opcode = ntohs((u_short)dp->th_opcode); |
1386 |
|
|
dp->th_block = ntohs((u_short)dp->th_block); |
1387 |
|
|
|
1388 |
|
|
switch (dp->th_opcode) { |
1389 |
|
|
case ERROR: |
1390 |
|
|
goto done; |
1391 |
|
|
case DATA: |
1392 |
|
|
break; |
1393 |
|
|
default: |
1394 |
|
|
goto retry; |
1395 |
|
|
} |
1396 |
|
|
|
1397 |
|
|
if (dp->th_block != client->block) |
1398 |
|
|
goto done; |
1399 |
|
|
|
1400 |
|
|
retry: |
1401 |
|
|
if (retry(client) == -1) { |
1402 |
|
|
lwarn("%s", getip(&client->ss)); |
1403 |
|
|
goto done; |
1404 |
|
|
} |
1405 |
|
|
return; |
1406 |
|
|
done: |
1407 |
|
|
client_free(client); |
1408 |
|
|
return; |
1409 |
|
|
} |
1410 |
|
|
|
1411 |
|
|
|
1412 |
|
|
/* |
1413 |
|
|
* Send a nak packet (error message). |
1414 |
|
|
* Error code passed in is one of the |
1415 |
|
|
* standard TFTP codes, or a UNIX errno |
1416 |
|
|
* offset by 100. |
1417 |
|
|
*/ |
1418 |
|
|
void |
1419 |
|
|
nak(struct tftp_client *client, int error) |
1420 |
|
|
{ |
1421 |
|
|
struct tftphdr *tp; |
1422 |
|
|
struct errmsg *pe; |
1423 |
|
|
size_t length; |
1424 |
|
|
|
1425 |
|
|
tp = (struct tftphdr *)client->buf; |
1426 |
|
|
tp->th_opcode = htons((u_short)ERROR); |
1427 |
|
|
tp->th_code = htons((u_short)error); |
1428 |
|
|
|
1429 |
|
|
for (pe = errmsgs; pe->e_code >= 0; pe++) { |
1430 |
|
|
if (pe->e_code == error) |
1431 |
|
|
break; |
1432 |
|
|
} |
1433 |
|
|
if (pe->e_code < 0) { |
1434 |
|
|
pe->e_msg = strerror(error - 100); |
1435 |
|
|
tp->th_code = htons(EUNDEF); /* set 'undef' errorcode */ |
1436 |
|
|
} |
1437 |
|
|
|
1438 |
|
|
length = strlcpy(tp->th_msg, pe->e_msg, client->packet_size - 5) + 5; |
1439 |
|
|
if (length > client->packet_size) |
1440 |
|
|
length = client->packet_size; |
1441 |
|
|
|
1442 |
|
|
if (send(client->sock, client->buf, length, 0) != length) |
1443 |
|
|
lwarn("nak"); |
1444 |
|
|
|
1445 |
|
|
client_free(client); |
1446 |
|
|
} |
1447 |
|
|
|
1448 |
|
|
/* |
1449 |
|
|
* Send an oack packet (option acknowledgement). |
1450 |
|
|
*/ |
1451 |
|
|
int |
1452 |
|
|
oack(struct tftp_client *client) |
1453 |
|
|
{ |
1454 |
|
|
struct opt_client *options = client->options; |
1455 |
|
|
struct tftphdr *tp; |
1456 |
|
|
char *bp; |
1457 |
|
|
int i, n, size; |
1458 |
|
|
|
1459 |
|
|
tp = (struct tftphdr *)client->buf; |
1460 |
|
|
bp = (char *)tp->th_stuff; |
1461 |
|
|
size = sizeof(client->buf) - 2; |
1462 |
|
|
|
1463 |
|
|
tp->th_opcode = htons((u_short)OACK); |
1464 |
|
|
for (i = 0; i < NOPT; i++) { |
1465 |
|
|
if (options[i].o_request == NULL) |
1466 |
|
|
continue; |
1467 |
|
|
|
1468 |
|
|
n = snprintf(bp, size, "%s%c%lld", opt_names[i], '\0', |
1469 |
|
|
options[i].o_reply); |
1470 |
|
|
if (n == -1 || n >= size) { |
1471 |
|
|
lwarnx("oack: no buffer space"); |
1472 |
|
|
goto error; |
1473 |
|
|
} |
1474 |
|
|
|
1475 |
|
|
bp += n + 1; |
1476 |
|
|
size -= n + 1; |
1477 |
|
|
if (size < 0) { |
1478 |
|
|
lwarnx("oack: no buffer space"); |
1479 |
|
|
goto error; |
1480 |
|
|
} |
1481 |
|
|
} |
1482 |
|
|
|
1483 |
|
|
client->buflen = bp - client->buf; |
1484 |
|
|
client->retries = RETRIES; |
1485 |
|
|
|
1486 |
|
|
if (send(client->sock, client->buf, client->buflen, 0) == -1) { |
1487 |
|
|
lwarn("oack"); |
1488 |
|
|
goto error; |
1489 |
|
|
} |
1490 |
|
|
|
1491 |
|
|
/* no client ACK for write requests with options */ |
1492 |
|
|
if (client->opcode == WRQ) { |
1493 |
|
|
client->block = 1; |
1494 |
|
|
event_set(&client->sev, client->sock, EV_READ, |
1495 |
|
|
tftp_wrq, client); |
1496 |
|
|
} else |
1497 |
|
|
event_set(&client->sev, client->sock, EV_READ, |
1498 |
|
|
oack_done, client); |
1499 |
|
|
|
1500 |
|
|
event_add(&client->sev, &client->tv); |
1501 |
|
|
return (0); |
1502 |
|
|
|
1503 |
|
|
error: |
1504 |
|
|
return (-1); |
1505 |
|
|
} |
1506 |
|
|
|
1507 |
|
|
int |
1508 |
|
|
retry(struct tftp_client *client) |
1509 |
|
|
{ |
1510 |
|
|
if (--client->retries == 0) { |
1511 |
|
|
errno = ETIMEDOUT; |
1512 |
|
|
return (-1); |
1513 |
|
|
} |
1514 |
|
|
|
1515 |
|
|
if (send(client->sock, client->buf, client->buflen, 0) == -1) |
1516 |
|
|
return (-1); |
1517 |
|
|
|
1518 |
|
|
event_add(&client->sev, &client->tv); |
1519 |
|
|
|
1520 |
|
|
return (0); |
1521 |
|
|
} |
1522 |
|
|
|
1523 |
|
|
void |
1524 |
|
|
oack_done(int fd, short events, void *arg) |
1525 |
|
|
{ |
1526 |
|
|
struct tftp_client *client = arg; |
1527 |
|
|
struct tftphdr *ap; |
1528 |
|
|
ssize_t n; |
1529 |
|
|
|
1530 |
|
|
if (events & EV_TIMEOUT) { |
1531 |
|
|
if (retry(client) == -1) { |
1532 |
|
|
lwarn("%s", getip(&client->ss)); |
1533 |
|
|
goto done; |
1534 |
|
|
} |
1535 |
|
|
|
1536 |
|
|
return; |
1537 |
|
|
} |
1538 |
|
|
|
1539 |
|
|
n = recv(client->sock, client->buf, client->packet_size, 0); |
1540 |
|
|
if (n == -1) { |
1541 |
|
|
switch (errno) { |
1542 |
|
|
case EINTR: |
1543 |
|
|
case EAGAIN: |
1544 |
|
|
event_add(&client->sev, &client->tv); |
1545 |
|
|
return; |
1546 |
|
|
|
1547 |
|
|
default: |
1548 |
|
|
lwarn("%s: recv", getip(&client->ss)); |
1549 |
|
|
goto done; |
1550 |
|
|
} |
1551 |
|
|
} |
1552 |
|
|
|
1553 |
|
|
if (n < 4) |
1554 |
|
|
goto done; |
1555 |
|
|
|
1556 |
|
|
ap = (struct tftphdr *)client->buf; |
1557 |
|
|
ap->th_opcode = ntohs((u_short)ap->th_opcode); |
1558 |
|
|
ap->th_block = ntohs((u_short)ap->th_block); |
1559 |
|
|
|
1560 |
|
|
if (ap->th_opcode != ACK || ap->th_block != 0) |
1561 |
|
|
goto done; |
1562 |
|
|
|
1563 |
|
|
sendfile(client); |
1564 |
|
|
return; |
1565 |
|
|
|
1566 |
|
|
done: |
1567 |
|
|
client_free(client); |
1568 |
|
|
} |
1569 |
|
|
|
1570 |
|
|
const char * |
1571 |
|
|
getip(void *s) |
1572 |
|
|
{ |
1573 |
|
|
struct sockaddr *sa = s; |
1574 |
|
|
static char hbuf[NI_MAXHOST]; |
1575 |
|
|
|
1576 |
|
|
if (getnameinfo(sa, sa->sa_len, hbuf, sizeof(hbuf), |
1577 |
|
|
NULL, 0, NI_NUMERICHOST)) |
1578 |
|
|
strlcpy(hbuf, "0.0.0.0", sizeof(hbuf)); |
1579 |
|
|
|
1580 |
|
|
return(hbuf); |
1581 |
|
|
} |
1582 |
|
|
|
1583 |
|
|
/* daemon(3) clone, intended to be used in a "r"estricted environment */ |
1584 |
|
|
int |
1585 |
|
|
rdaemon(int devnull) |
1586 |
|
|
{ |
1587 |
|
|
if (devnull == -1) { |
1588 |
|
|
errno = EBADF; |
1589 |
|
|
return (-1); |
1590 |
|
|
} |
1591 |
|
|
if (fcntl(devnull, F_GETFL) == -1) |
1592 |
|
|
return (-1); |
1593 |
|
|
|
1594 |
|
|
switch (fork()) { |
1595 |
|
|
case -1: |
1596 |
|
|
return (-1); |
1597 |
|
|
case 0: |
1598 |
|
|
break; |
1599 |
|
|
default: |
1600 |
|
|
_exit(0); |
1601 |
|
|
} |
1602 |
|
|
|
1603 |
|
|
if (setsid() == -1) |
1604 |
|
|
return (-1); |
1605 |
|
|
|
1606 |
|
|
(void)dup2(devnull, STDIN_FILENO); |
1607 |
|
|
(void)dup2(devnull, STDOUT_FILENO); |
1608 |
|
|
(void)dup2(devnull, STDERR_FILENO); |
1609 |
|
|
if (devnull > 2) |
1610 |
|
|
(void)close(devnull); |
1611 |
|
|
|
1612 |
|
|
return (0); |
1613 |
|
|
} |
1614 |
|
|
|
1615 |
|
|
void |
1616 |
|
|
syslog_vstrerror(int e, int priority, const char *fmt, va_list ap) |
1617 |
|
|
{ |
1618 |
|
|
char *s; |
1619 |
|
|
|
1620 |
|
|
if (vasprintf(&s, fmt, ap) == -1) { |
1621 |
|
|
syslog(LOG_EMERG, "unable to alloc in syslog_vstrerror"); |
1622 |
|
|
exit(1); |
1623 |
|
|
} |
1624 |
|
|
|
1625 |
|
|
syslog(priority, "%s: %s", s, strerror(e)); |
1626 |
|
|
|
1627 |
|
|
free(s); |
1628 |
|
|
} |
1629 |
|
|
|
1630 |
|
|
void |
1631 |
|
|
syslog_err(int ecode, const char *fmt, ...) |
1632 |
|
|
{ |
1633 |
|
|
va_list ap; |
1634 |
|
|
|
1635 |
|
|
va_start(ap, fmt); |
1636 |
|
|
syslog_vstrerror(errno, LOG_CRIT, fmt, ap); |
1637 |
|
|
va_end(ap); |
1638 |
|
|
|
1639 |
|
|
exit(ecode); |
1640 |
|
|
} |
1641 |
|
|
|
1642 |
|
|
void |
1643 |
|
|
syslog_errx(int ecode, const char *fmt, ...) |
1644 |
|
|
{ |
1645 |
|
|
va_list ap; |
1646 |
|
|
|
1647 |
|
|
va_start(ap, fmt); |
1648 |
|
|
vsyslog(LOG_CRIT, fmt, ap); |
1649 |
|
|
va_end(ap); |
1650 |
|
|
|
1651 |
|
|
exit(ecode); |
1652 |
|
|
} |
1653 |
|
|
|
1654 |
|
|
void |
1655 |
|
|
syslog_warn(const char *fmt, ...) |
1656 |
|
|
{ |
1657 |
|
|
va_list ap; |
1658 |
|
|
|
1659 |
|
|
va_start(ap, fmt); |
1660 |
|
|
syslog_vstrerror(errno, LOG_ERR, fmt, ap); |
1661 |
|
|
va_end(ap); |
1662 |
|
|
} |
1663 |
|
|
|
1664 |
|
|
void |
1665 |
|
|
syslog_warnx(const char *fmt, ...) |
1666 |
|
|
{ |
1667 |
|
|
va_list ap; |
1668 |
|
|
|
1669 |
|
|
va_start(ap, fmt); |
1670 |
|
|
vsyslog(LOG_ERR, fmt, ap); |
1671 |
|
|
va_end(ap); |
1672 |
|
|
} |
1673 |
|
|
|
1674 |
|
|
void |
1675 |
|
|
syslog_info(const char *fmt, ...) |
1676 |
|
|
{ |
1677 |
|
|
va_list ap; |
1678 |
|
|
|
1679 |
|
|
va_start(ap, fmt); |
1680 |
|
|
vsyslog(LOG_INFO, fmt, ap); |
1681 |
|
|
va_end(ap); |
1682 |
|
|
} |
1683 |
|
|
|
1684 |
|
|
void |
1685 |
|
|
syslog_debug(const char *fmt, ...) |
1686 |
|
|
{ |
1687 |
|
|
va_list ap; |
1688 |
|
|
|
1689 |
|
|
if (!debug) |
1690 |
|
|
return; |
1691 |
|
|
|
1692 |
|
|
va_start(ap, fmt); |
1693 |
|
|
vsyslog(LOG_DEBUG, fmt, ap); |
1694 |
|
|
va_end(ap); |
1695 |
|
|
} |