tftpd.c (173852) | tftpd.c (207608) |
---|---|
1/* 2 * Copyright (c) 1983, 1993 3 * The Regents of the University of California. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright --- 27 unchanged lines hidden (view full) --- 36"@(#) Copyright (c) 1983, 1993\n\ 37 The Regents of the University of California. All rights reserved.\n"; 38#endif /* not lint */ 39 40#ifndef lint 41#if 0 42static char sccsid[] = "@(#)tftpd.c 8.1 (Berkeley) 6/4/93"; 43#endif | 1/* 2 * Copyright (c) 1983, 1993 3 * The Regents of the University of California. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright --- 27 unchanged lines hidden (view full) --- 36"@(#) Copyright (c) 1983, 1993\n\ 37 The Regents of the University of California. All rights reserved.\n"; 38#endif /* not lint */ 39 40#ifndef lint 41#if 0 42static char sccsid[] = "@(#)tftpd.c 8.1 (Berkeley) 6/4/93"; 43#endif |
44static const char rcsid[] = 45 "$FreeBSD: head/libexec/tftpd/tftpd.c 173852 2007-11-23 00:05:29Z edwin $"; | |
46#endif /* not lint */ | 44#endif /* not lint */ |
45#include <sys/cdefs.h> 46__FBSDID("$FreeBSD: head/libexec/tftpd/tftpd.c 207608 2010-05-04 06:19:19Z imp $"); |
|
47 48/* 49 * Trivial file transfer protocol server. 50 * 51 * This version includes many modifications by Jim Guyton 52 * <guyton@rand-unix>. 53 */ 54 55#include <sys/param.h> 56#include <sys/ioctl.h> 57#include <sys/stat.h> 58#include <sys/socket.h> | 47 48/* 49 * Trivial file transfer protocol server. 50 * 51 * This version includes many modifications by Jim Guyton 52 * <guyton@rand-unix>. 53 */ 54 55#include <sys/param.h> 56#include <sys/ioctl.h> 57#include <sys/stat.h> 58#include <sys/socket.h> |
59#include <sys/types.h> 60#include <sys/time.h> | |
61 62#include <netinet/in.h> 63#include <arpa/tftp.h> | 59 60#include <netinet/in.h> 61#include <arpa/tftp.h> |
64#include <arpa/inet.h> | |
65 66#include <ctype.h> 67#include <errno.h> 68#include <fcntl.h> | 62 63#include <ctype.h> 64#include <errno.h> 65#include <fcntl.h> |
69#include <libutil.h> | |
70#include <netdb.h> 71#include <pwd.h> | 66#include <netdb.h> 67#include <pwd.h> |
72#include <setjmp.h> 73#include <signal.h> | |
74#include <stdio.h> 75#include <stdlib.h> 76#include <string.h> 77#include <syslog.h> | 68#include <stdio.h> 69#include <stdlib.h> 70#include <string.h> 71#include <syslog.h> |
72#include <tcpd.h> |
|
78#include <unistd.h> 79 | 73#include <unistd.h> 74 |
80#include "tftpsubs.h" | 75#include "tftp-file.h" 76#include "tftp-io.h" 77#include "tftp-utils.h" 78#include "tftp-transfer.h" 79#include "tftp-options.h" |
81 | 80 |
82#define TIMEOUT 5 83#define MAX_TIMEOUTS 5 | 81static void tftp_wrq(int peer, char *, ssize_t); 82static void tftp_rrq(int peer, char *, ssize_t); |
84 | 83 |
85int peer; 86int rexmtval = TIMEOUT; 87int max_rexmtval = 2*TIMEOUT; 88 89#define PKTSIZE SEGSIZE+4 90char buf[PKTSIZE]; 91char ackbuf[PKTSIZE]; 92struct sockaddr_storage from; 93 94void tftp(struct tftphdr *, int); 95static void unmappedaddr(struct sockaddr_in6 *); 96 | |
97/* 98 * Null-terminated directory prefix list for absolute pathname requests and 99 * search list for relative pathname requests. 100 * 101 * MAXDIRS should be at least as large as the number of arguments that 102 * inetd allows (currently 20). 103 */ 104#define MAXDIRS 20 105static struct dirlist { 106 const char *name; 107 int len; 108} dirs[MAXDIRS+1]; 109static int suppress_naks; 110static int logging; 111static int ipchroot; 112static int create_new = 0; 113static char *newfile_format = "%Y%m%d"; 114static int increase_name = 0; | 84/* 85 * Null-terminated directory prefix list for absolute pathname requests and 86 * search list for relative pathname requests. 87 * 88 * MAXDIRS should be at least as large as the number of arguments that 89 * inetd allows (currently 20). 90 */ 91#define MAXDIRS 20 92static struct dirlist { 93 const char *name; 94 int len; 95} dirs[MAXDIRS+1]; 96static int suppress_naks; 97static int logging; 98static int ipchroot; 99static int create_new = 0; 100static char *newfile_format = "%Y%m%d"; 101static int increase_name = 0; |
115static mode_t mask = S_IWGRP|S_IWOTH; | 102static mode_t mask = S_IWGRP | S_IWOTH; |
116 | 103 |
117static const char *errtomsg(int); 118static void nak(int); 119static void oack(void); | 104struct formats; 105static void tftp_recvfile(int peer, const char *mode); 106static void tftp_xmitfile(int peer, const char *mode); 107static int validate_access(int peer, char **, int); 108static char peername[NI_MAXHOST]; |
120 | 109 |
121static void timer(int); 122static void justquit(int); | 110FILE *file; |
123 | 111 |
112struct formats { 113 const char *f_mode; 114 int f_convert; 115} formats[] = { 116 { "netascii", 1 }, 117 { "octet", 0 }, 118 { NULL, 0 } 119}; 120 |
|
124int 125main(int argc, char *argv[]) 126{ 127 struct tftphdr *tp; | 121int 122main(int argc, char *argv[]) 123{ 124 struct tftphdr *tp; |
128 socklen_t fromlen, len; 129 int n; 130 int ch, on; 131 struct sockaddr_storage me; 132 char *chroot_dir = NULL; 133 struct passwd *nobody; 134 const char *chuser = "nobody"; | 125 int peer; 126 socklen_t peerlen, len; 127 ssize_t n; 128 int ch; 129 char *chroot_dir = NULL; 130 struct passwd *nobody; 131 const char *chuser = "nobody"; 132 char recvbuffer[MAXPKTSIZE]; 133 int allow_ro = 1, allow_wo = 1; |
135 136 tzset(); /* syslog in localtime */ | 134 135 tzset(); /* syslog in localtime */ |
136 acting_as_client = 0; |
|
137 | 137 |
138 openlog("tftpd", LOG_PID | LOG_NDELAY, LOG_FTP); 139 while ((ch = getopt(argc, argv, "cCF:lns:u:U:wW")) != -1) { | 138 tftp_openlog("tftpd", LOG_PID | LOG_NDELAY, LOG_FTP); 139 while ((ch = getopt(argc, argv, "cCd:F:lnoOp:s:u:U:wW")) != -1) { |
140 switch (ch) { 141 case 'c': 142 ipchroot = 1; 143 break; 144 case 'C': 145 ipchroot = 2; 146 break; | 140 switch (ch) { 141 case 'c': 142 ipchroot = 1; 143 break; 144 case 'C': 145 ipchroot = 2; 146 break; |
147 case 'd': 148 if (atoi(optarg) != 0) 149 debug += atoi(optarg); 150 else 151 debug |= debug_finds(optarg); 152 break; |
|
147 case 'F': 148 newfile_format = optarg; 149 break; 150 case 'l': 151 logging = 1; 152 break; 153 case 'n': 154 suppress_naks = 1; 155 break; | 153 case 'F': 154 newfile_format = optarg; 155 break; 156 case 'l': 157 logging = 1; 158 break; 159 case 'n': 160 suppress_naks = 1; 161 break; |
162 case 'o': 163 options_rfc_enabled = 0; 164 break; 165 case 'O': 166 options_extra_enabled = 0; 167 break; 168 case 'p': 169 packetdroppercentage = atoi(optarg); 170 tftp_log(LOG_INFO, 171 "Randomly dropping %d out of 100 packets", 172 packetdroppercentage); 173 break; |
|
156 case 's': 157 chroot_dir = optarg; 158 break; 159 case 'u': 160 chuser = optarg; 161 break; 162 case 'U': 163 mask = strtol(optarg, NULL, 0); 164 break; 165 case 'w': 166 create_new = 1; 167 break; 168 case 'W': 169 create_new = 1; 170 increase_name = 1; 171 break; 172 default: | 174 case 's': 175 chroot_dir = optarg; 176 break; 177 case 'u': 178 chuser = optarg; 179 break; 180 case 'U': 181 mask = strtol(optarg, NULL, 0); 182 break; 183 case 'w': 184 create_new = 1; 185 break; 186 case 'W': 187 create_new = 1; 188 increase_name = 1; 189 break; 190 default: |
173 syslog(LOG_WARNING, "ignoring unknown option -%c", ch); | 191 tftp_log(LOG_WARNING, 192 "ignoring unknown option -%c", ch); |
174 } 175 } 176 if (optind < argc) { 177 struct dirlist *dirp; 178 179 /* Get list of directory prefixes. Skip relative pathnames. */ 180 for (dirp = dirs; optind < argc && dirp < &dirs[MAXDIRS]; 181 optind++) { --- 4 unchanged lines hidden (view full) --- 186 } 187 } 188 } 189 else if (chroot_dir) { 190 dirs->name = "/"; 191 dirs->len = 1; 192 } 193 if (ipchroot > 0 && chroot_dir == NULL) { | 193 } 194 } 195 if (optind < argc) { 196 struct dirlist *dirp; 197 198 /* Get list of directory prefixes. Skip relative pathnames. */ 199 for (dirp = dirs; optind < argc && dirp < &dirs[MAXDIRS]; 200 optind++) { --- 4 unchanged lines hidden (view full) --- 205 } 206 } 207 } 208 else if (chroot_dir) { 209 dirs->name = "/"; 210 dirs->len = 1; 211 } 212 if (ipchroot > 0 && chroot_dir == NULL) { |
194 syslog(LOG_ERR, "-c requires -s"); | 213 tftp_log(LOG_ERR, "-c requires -s"); |
195 exit(1); 196 } 197 198 umask(mask); 199 | 214 exit(1); 215 } 216 217 umask(mask); 218 |
200 on = 1; 201 if (ioctl(0, FIONBIO, &on) < 0) { 202 syslog(LOG_ERR, "ioctl(FIONBIO): %m"); 203 exit(1); | 219 { 220 int on = 1; 221 if (ioctl(0, FIONBIO, &on) < 0) { 222 tftp_log(LOG_ERR, "ioctl(FIONBIO): %s", strerror(errno)); 223 exit(1); 224 } |
204 } | 225 } |
205 fromlen = sizeof (from); 206 n = recvfrom(0, buf, sizeof (buf), 0, 207 (struct sockaddr *)&from, &fromlen); | 226 227 /* Find out who we are talking to and what we are going to do */ 228 peerlen = sizeof(peer_sock); 229 n = recvfrom(0, recvbuffer, MAXPKTSIZE, 0, 230 (struct sockaddr *)&peer_sock, &peerlen); |
208 if (n < 0) { | 231 if (n < 0) { |
209 syslog(LOG_ERR, "recvfrom: %m"); | 232 tftp_log(LOG_ERR, "recvfrom: %s", strerror(errno)); |
210 exit(1); 211 } | 233 exit(1); 234 } |
235 getnameinfo((struct sockaddr *)&peer_sock, peer_sock.ss_len, 236 peername, sizeof(peername), NULL, 0, NI_NUMERICHOST); 237 |
|
212 /* 213 * Now that we have read the message out of the UDP 214 * socket, we fork and exit. Thus, inetd will go back 215 * to listening to the tftp port, and the next request 216 * to come in will start up a new instance of tftpd. 217 * 218 * We do this so that inetd can run tftpd in "wait" mode. 219 * The problem with tftpd running in "nowait" mode is that --- 15 unchanged lines hidden (view full) --- 235 * 236 * This may drop some request, but those 237 * will be resent by the clients when 238 * they timeout. The positive effect of 239 * this flush is to (try to) prevent more 240 * than one tftpd being started up to service 241 * a single request from a single client. 242 */ | 238 /* 239 * Now that we have read the message out of the UDP 240 * socket, we fork and exit. Thus, inetd will go back 241 * to listening to the tftp port, and the next request 242 * to come in will start up a new instance of tftpd. 243 * 244 * We do this so that inetd can run tftpd in "wait" mode. 245 * The problem with tftpd running in "nowait" mode is that --- 15 unchanged lines hidden (view full) --- 261 * 262 * This may drop some request, but those 263 * will be resent by the clients when 264 * they timeout. The positive effect of 265 * this flush is to (try to) prevent more 266 * than one tftpd being started up to service 267 * a single request from a single client. 268 */ |
243 fromlen = sizeof from; 244 i = recvfrom(0, buf, sizeof (buf), 0, 245 (struct sockaddr *)&from, &fromlen); | 269 peerlen = sizeof peer_sock; 270 i = recvfrom(0, recvbuffer, MAXPKTSIZE, 0, 271 (struct sockaddr *)&peer_sock, &peerlen); |
246 if (i > 0) { 247 n = i; 248 } 249 } else { 250 break; 251 } 252 } 253 if (pid < 0) { | 272 if (i > 0) { 273 n = i; 274 } 275 } else { 276 break; 277 } 278 } 279 if (pid < 0) { |
254 syslog(LOG_ERR, "fork: %m"); | 280 tftp_log(LOG_ERR, "fork: %s", strerror(errno)); |
255 exit(1); 256 } else if (pid != 0) { 257 exit(0); 258 } 259 } 260 261 /* | 281 exit(1); 282 } else if (pid != 0) { 283 exit(0); 284 } 285 } 286 287 /* |
288 * See if the client is allowed to talk to me. 289 * (This needs to be done before the chroot()) 290 */ 291 { 292 struct request_info req; 293 294 request_init(&req, RQ_CLIENT_ADDR, peername, 0); 295 request_set(&req, RQ_DAEMON, "tftpd", 0); 296 297 if (hosts_access(&req) == 0) { 298 if (debug&DEBUG_ACCESS) 299 tftp_log(LOG_WARNING, 300 "Access denied by 'tftpd' entry " 301 "in /etc/hosts.allow"); 302 303 /* 304 * Full access might be disabled, but maybe the 305 * client is allowed to do read-only access. 306 */ 307 request_set(&req, RQ_DAEMON, "tftpd-ro", 0); 308 allow_ro = hosts_access(&req); 309 310 request_set(&req, RQ_DAEMON, "tftpd-wo", 0); 311 allow_wo = hosts_access(&req); 312 313 if (allow_ro == 0 && allow_wo == 0) { 314 tftp_log(LOG_WARNING, 315 "Unauthorized access from %s", peername); 316 exit(1); 317 } 318 319 if (debug&DEBUG_ACCESS) { 320 if (allow_ro) 321 tftp_log(LOG_WARNING, 322 "But allowed readonly access " 323 "via 'tftpd-ro' entry"); 324 if (allow_wo) 325 tftp_log(LOG_WARNING, 326 "But allowed writeonly access " 327 "via 'tftpd-wo' entry"); 328 } 329 } else 330 if (debug&DEBUG_ACCESS) 331 tftp_log(LOG_WARNING, 332 "Full access allowed" 333 "in /etc/hosts.allow"); 334 } 335 336 /* |
|
262 * Since we exit here, we should do that only after the above 263 * recvfrom to keep inetd from constantly forking should there 264 * be a problem. See the above comment about system clogging. 265 */ 266 if (chroot_dir) { 267 if (ipchroot > 0) { 268 char *tempchroot; 269 struct stat sb; 270 int statret; 271 struct sockaddr_storage ss; 272 char hbuf[NI_MAXHOST]; 273 | 337 * Since we exit here, we should do that only after the above 338 * recvfrom to keep inetd from constantly forking should there 339 * be a problem. See the above comment about system clogging. 340 */ 341 if (chroot_dir) { 342 if (ipchroot > 0) { 343 char *tempchroot; 344 struct stat sb; 345 int statret; 346 struct sockaddr_storage ss; 347 char hbuf[NI_MAXHOST]; 348 |
274 memcpy(&ss, &from, from.ss_len); | 349 statret = -1; 350 memcpy(&ss, &peer_sock, peer_sock.ss_len); |
275 unmappedaddr((struct sockaddr_in6 *)&ss); 276 getnameinfo((struct sockaddr *)&ss, ss.ss_len, 277 hbuf, sizeof(hbuf), NULL, 0, 278 NI_NUMERICHOST); 279 asprintf(&tempchroot, "%s/%s", chroot_dir, hbuf); 280 if (ipchroot == 2) 281 statret = stat(tempchroot, &sb); 282 if (ipchroot == 1 || 283 (statret == 0 && (sb.st_mode & S_IFDIR))) 284 chroot_dir = tempchroot; 285 } 286 /* Must get this before chroot because /etc might go away */ 287 if ((nobody = getpwnam(chuser)) == NULL) { | 351 unmappedaddr((struct sockaddr_in6 *)&ss); 352 getnameinfo((struct sockaddr *)&ss, ss.ss_len, 353 hbuf, sizeof(hbuf), NULL, 0, 354 NI_NUMERICHOST); 355 asprintf(&tempchroot, "%s/%s", chroot_dir, hbuf); 356 if (ipchroot == 2) 357 statret = stat(tempchroot, &sb); 358 if (ipchroot == 1 || 359 (statret == 0 && (sb.st_mode & S_IFDIR))) 360 chroot_dir = tempchroot; 361 } 362 /* Must get this before chroot because /etc might go away */ 363 if ((nobody = getpwnam(chuser)) == NULL) { |
288 syslog(LOG_ERR, "%s: no such user", chuser); | 364 tftp_log(LOG_ERR, "%s: no such user", chuser); |
289 exit(1); 290 } 291 if (chroot(chroot_dir)) { | 365 exit(1); 366 } 367 if (chroot(chroot_dir)) { |
292 syslog(LOG_ERR, "chroot: %s: %m", chroot_dir); | 368 tftp_log(LOG_ERR, "chroot: %s: %s", 369 chroot_dir, strerror(errno)); |
293 exit(1); 294 } 295 chdir("/"); 296 setgroups(1, &nobody->pw_gid); 297 setuid(nobody->pw_uid); 298 } 299 | 370 exit(1); 371 } 372 chdir("/"); 373 setgroups(1, &nobody->pw_gid); 374 setuid(nobody->pw_uid); 375 } 376 |
300 len = sizeof(me); 301 if (getsockname(0, (struct sockaddr *)&me, &len) == 0) { 302 switch (me.ss_family) { | 377 len = sizeof(me_sock); 378 if (getsockname(0, (struct sockaddr *)&me_sock, &len) == 0) { 379 switch (me_sock.ss_family) { |
303 case AF_INET: | 380 case AF_INET: |
304 ((struct sockaddr_in *)&me)->sin_port = 0; | 381 ((struct sockaddr_in *)&me_sock)->sin_port = 0; |
305 break; 306 case AF_INET6: | 382 break; 383 case AF_INET6: |
307 ((struct sockaddr_in6 *)&me)->sin6_port = 0; | 384 ((struct sockaddr_in6 *)&me_sock)->sin6_port = 0; |
308 break; 309 default: 310 /* unsupported */ 311 break; 312 } 313 } else { | 385 break; 386 default: 387 /* unsupported */ 388 break; 389 } 390 } else { |
314 memset(&me, 0, sizeof(me)); 315 me.ss_family = from.ss_family; 316 me.ss_len = from.ss_len; | 391 memset(&me_sock, 0, sizeof(me_sock)); 392 me_sock.ss_family = peer_sock.ss_family; 393 me_sock.ss_len = peer_sock.ss_len; |
317 } | 394 } |
318 alarm(0); | |
319 close(0); 320 close(1); | 395 close(0); 396 close(1); |
321 peer = socket(from.ss_family, SOCK_DGRAM, 0); | 397 peer = socket(peer_sock.ss_family, SOCK_DGRAM, 0); |
322 if (peer < 0) { | 398 if (peer < 0) { |
323 syslog(LOG_ERR, "socket: %m"); | 399 tftp_log(LOG_ERR, "socket: %s", strerror(errno)); |
324 exit(1); 325 } | 400 exit(1); 401 } |
326 if (bind(peer, (struct sockaddr *)&me, me.ss_len) < 0) { 327 syslog(LOG_ERR, "bind: %m"); | 402 if (bind(peer, (struct sockaddr *)&me_sock, me_sock.ss_len) < 0) { 403 tftp_log(LOG_ERR, "bind: %s", strerror(errno)); |
328 exit(1); 329 } | 404 exit(1); 405 } |
330 if (connect(peer, (struct sockaddr *)&from, from.ss_len) < 0) { 331 syslog(LOG_ERR, "connect: %m"); 332 exit(1); 333 } 334 tp = (struct tftphdr *)buf; | 406 407 tp = (struct tftphdr *)recvbuffer; |
335 tp->th_opcode = ntohs(tp->th_opcode); | 408 tp->th_opcode = ntohs(tp->th_opcode); |
336 if (tp->th_opcode == RRQ || tp->th_opcode == WRQ) 337 tftp(tp, n); | 409 if (tp->th_opcode == RRQ) { 410 if (allow_ro) 411 tftp_rrq(peer, tp->th_stuff, n - 1); 412 else { 413 tftp_log(LOG_WARNING, 414 "%s read access denied", peername); 415 exit(1); 416 } 417 } 418 if (tp->th_opcode == WRQ) { 419 if (allow_wo) 420 tftp_wrq(peer, tp->th_stuff, n - 1); 421 else { 422 tftp_log(LOG_WARNING, 423 "%s write access denied", peername); 424 exit(1); 425 } 426 } |
338 exit(1); 339} 340 341static void 342reduce_path(char *fn) 343{ 344 char *slash, *ptr; 345 --- 18 unchanged lines hidden (view full) --- 364 if (ptr < fn) 365 break; 366 slash += 3; 367 while (*slash) 368 *++ptr = *++slash; 369 } 370} 371 | 427 exit(1); 428} 429 430static void 431reduce_path(char *fn) 432{ 433 char *slash, *ptr; 434 --- 18 unchanged lines hidden (view full) --- 453 if (ptr < fn) 454 break; 455 slash += 3; 456 while (*slash) 457 *++ptr = *++slash; 458 } 459} 460 |
372struct formats; 373int validate_access(char **, int); 374void xmitfile(struct formats *); 375void recvfile(struct formats *); | 461static char * 462parse_header(int peer, char *recvbuffer, ssize_t size, 463 char **filename, char **mode) 464{ 465 char *cp; 466 int i; 467 struct formats *pf; |
376 | 468 |
377struct formats { 378 const char *f_mode; 379 int (*f_validate)(char **, int); 380 void (*f_send)(struct formats *); 381 void (*f_recv)(struct formats *); 382 int f_convert; 383} formats[] = { 384 { "netascii", validate_access, xmitfile, recvfile, 1 }, 385 { "octet", validate_access, xmitfile, recvfile, 0 }, 386#ifdef notdef 387 { "mail", validate_user, sendmail, recvmail, 1 }, 388#endif 389 { 0, NULL, NULL, NULL, 0 } 390}; | 469 *mode = NULL; 470 cp = recvbuffer; |
391 | 471 |
392struct options { 393 const char *o_type; 394 char *o_request; 395 int o_reply; /* turn into union if need be */ 396} options[] = { 397 { "tsize", NULL, 0 }, /* OPT_TSIZE */ 398 { "timeout", NULL, 0 }, /* OPT_TIMEOUT */ 399 { NULL, NULL, 0 } 400}; | 472 i = get_field(peer, recvbuffer, size); 473 if (i >= PATH_MAX) { 474 tftp_log(LOG_ERR, "Bad option - filename too long"); 475 send_error(peer, EBADOP); 476 exit(1); 477 } 478 *filename = recvbuffer; 479 tftp_log(LOG_INFO, "Filename: '%s'", *filename); 480 cp += i; |
401 | 481 |
402enum opt_enum { 403 OPT_TSIZE = 0, 404 OPT_TIMEOUT, 405}; | 482 i = get_field(peer, cp, size); 483 *mode = cp; 484 cp += i; |
406 | 485 |
486 /* Find the file transfer mode */ 487 for (cp = *mode; *cp; cp++) 488 if (isupper(*cp)) 489 *cp = tolower(*cp); 490 for (pf = formats; pf->f_mode; pf++) 491 if (strcmp(pf->f_mode, *mode) == 0) 492 break; 493 if (pf->f_mode == NULL) { 494 tftp_log(LOG_ERR, 495 "Bad option - Unknown transfer mode (%s)", *mode); 496 send_error(peer, EBADOP); 497 exit(1); 498 } 499 tftp_log(LOG_INFO, "Mode: '%s'", *mode); 500 501 return (cp + 1); 502} 503 |
|
407/* | 504/* |
408 * Handle initial connection protocol. | 505 * WRQ - receive a file from the client |
409 */ 410void | 506 */ 507void |
411tftp(struct tftphdr *tp, int size) | 508tftp_wrq(int peer, char *recvbuffer, ssize_t size) |
412{ 413 char *cp; | 509{ 510 char *cp; |
414 int i, first = 1, has_options = 0, ecode; 415 struct formats *pf; 416 char *filename, *mode, *option, *ccp; | 511 int has_options = 0, ecode; 512 char *filename, *mode; |
417 char fnbuf[PATH_MAX]; 418 | 513 char fnbuf[PATH_MAX]; 514 |
419 cp = tp->th_stuff; 420again: 421 while (cp < buf + size) { 422 if (*cp == '\0') 423 break; 424 cp++; 425 } 426 if (*cp != '\0') { 427 nak(EBADOP); 428 exit(1); 429 } 430 i = cp - tp->th_stuff; 431 if (i >= sizeof(fnbuf)) { 432 nak(EBADOP); 433 exit(1); 434 } 435 memcpy(fnbuf, tp->th_stuff, i); 436 fnbuf[i] = '\0'; | 515 cp = parse_header(peer, recvbuffer, size, &filename, &mode); 516 size -= (cp - recvbuffer) + 1; 517 518 strcpy(fnbuf, filename); |
437 reduce_path(fnbuf); 438 filename = fnbuf; | 519 reduce_path(fnbuf); 520 filename = fnbuf; |
439 if (first) { 440 mode = ++cp; 441 first = 0; 442 goto again; | 521 522 if (size > 0) { 523 if (options_rfc_enabled) 524 has_options = !parse_options(peer, cp, size); 525 else 526 tftp_log(LOG_INFO, "Options found but not enabled"); |
443 } | 527 } |
444 for (cp = mode; *cp; cp++) 445 if (isupper(*cp)) 446 *cp = tolower(*cp); 447 for (pf = formats; pf->f_mode; pf++) 448 if (strcmp(pf->f_mode, mode) == 0) 449 break; 450 if (pf->f_mode == 0) { 451 nak(EBADOP); 452 exit(1); | 528 529 ecode = validate_access(peer, &filename, WRQ); 530 if (ecode == 0) { 531 if (has_options) 532 send_oack(peer); 533 else 534 send_ack(peer, 0); |
453 } | 535 } |
454 while (++cp < buf + size) { 455 for (i = 2, ccp = cp; i > 0; ccp++) { 456 if (ccp >= buf + size) { 457 /* 458 * Don't reject the request, just stop trying 459 * to parse the option and get on with it. 460 * Some Apple Open Firmware versions have 461 * trailing garbage on the end of otherwise 462 * valid requests. 463 */ 464 goto option_fail; 465 } else if (*ccp == '\0') 466 i--; 467 } 468 for (option = cp; *cp; cp++) 469 if (isupper(*cp)) 470 *cp = tolower(*cp); 471 for (i = 0; options[i].o_type != NULL; i++) 472 if (strcmp(option, options[i].o_type) == 0) { 473 options[i].o_request = ++cp; 474 has_options = 1; 475 } 476 cp = ccp-1; | 536 if (logging) { 537 tftp_log(LOG_INFO, "%s: write request for %s: %s", peername, 538 filename, errtomsg(ecode)); |
477 } 478 | 539 } 540 |
479option_fail: 480 if (options[OPT_TIMEOUT].o_request) { 481 int to = atoi(options[OPT_TIMEOUT].o_request); 482 if (to < 1 || to > 255) { 483 nak(EBADOP); 484 exit(1); 485 } 486 else if (to <= max_rexmtval) 487 options[OPT_TIMEOUT].o_reply = rexmtval = to; | 541 tftp_recvfile(peer, mode); 542 exit(0); 543} 544 545/* 546 * RRQ - send a file to the client 547 */ 548void 549tftp_rrq(int peer, char *recvbuffer, ssize_t size) 550{ 551 char *cp; 552 int has_options = 0, ecode; 553 char *filename, *mode; 554 char fnbuf[PATH_MAX]; 555 556 cp = parse_header(peer, recvbuffer, size, &filename, &mode); 557 size -= (cp - recvbuffer) + 1; 558 559 strcpy(fnbuf, filename); 560 reduce_path(fnbuf); 561 filename = fnbuf; 562 563 if (size > 0) { 564 if (options_rfc_enabled) 565 has_options = !parse_options(peer, cp, size); |
488 else | 566 else |
489 options[OPT_TIMEOUT].o_request = NULL; | 567 tftp_log(LOG_INFO, "Options found but not enabled"); |
490 } 491 | 568 } 569 |
492 ecode = (*pf->f_validate)(&filename, tp->th_opcode); 493 if (has_options && ecode == 0) 494 oack(); 495 if (logging) { 496 char hbuf[NI_MAXHOST]; | 570 ecode = validate_access(peer, &filename, RRQ); 571 if (ecode == 0) { 572 if (has_options) { 573 int n; 574 char lrecvbuffer[MAXPKTSIZE]; 575 struct tftphdr *rp = (struct tftphdr *)lrecvbuffer; |
497 | 576 |
498 getnameinfo((struct sockaddr *)&from, from.ss_len, 499 hbuf, sizeof(hbuf), NULL, 0, 0); 500 syslog(LOG_INFO, "%s: %s request for %s: %s", hbuf, 501 tp->th_opcode == WRQ ? "write" : "read", 502 filename, errtomsg(ecode)); | 577 send_oack(peer); 578 n = receive_packet(peer, lrecvbuffer, MAXPKTSIZE, 579 NULL, timeoutpacket); 580 if (n < 0) { 581 if (debug&DEBUG_SIMPLE) 582 tftp_log(LOG_DEBUG, "Aborting: %s", 583 rp_strerror(n)); 584 return; 585 } 586 if (rp->th_opcode != ACK) { 587 if (debug&DEBUG_SIMPLE) 588 tftp_log(LOG_DEBUG, 589 "Expected ACK, got %s on OACK", 590 packettype(rp->th_opcode)); 591 return; 592 } 593 } |
503 } | 594 } |
595 596 if (logging) 597 tftp_log(LOG_INFO, "%s: read request for %s: %s", peername, 598 filename, errtomsg(ecode)); 599 |
|
504 if (ecode) { 505 /* 506 * Avoid storms of naks to a RRQ broadcast for a relative 507 * bootfile pathname from a diskless Sun. 508 */ 509 if (suppress_naks && *filename != '/' && ecode == ENOTFOUND) 510 exit(0); | 600 if (ecode) { 601 /* 602 * Avoid storms of naks to a RRQ broadcast for a relative 603 * bootfile pathname from a diskless Sun. 604 */ 605 if (suppress_naks && *filename != '/' && ecode == ENOTFOUND) 606 exit(0); |
511 nak(ecode); | 607 tftp_log(LOG_ERR, "Prevent NAK storm"); 608 send_error(peer, ecode); |
512 exit(1); 513 } | 609 exit(1); 610 } |
514 if (tp->th_opcode == WRQ) 515 (*pf->f_recv)(pf); 516 else 517 (*pf->f_send)(pf); 518 exit(0); | 611 tftp_xmitfile(peer, mode); |
519} 520 | 612} 613 |
521 522FILE *file; 523 | |
524/* 525 * Find the next value for YYYYMMDD.nn when the file to be written should 526 * be unique. Due to the limitations of nn, we will fail if nn reaches 100. 527 * Besides, that is four updates per hour on a file, which is kind of 528 * execessive anyway. 529 */ 530static int 531find_next_name(char *filename, int *fd) 532{ 533 int i; 534 time_t tval; 535 size_t len; 536 struct tm lt; 537 char yyyymmdd[MAXPATHLEN]; 538 char newname[MAXPATHLEN]; | 614/* 615 * Find the next value for YYYYMMDD.nn when the file to be written should 616 * be unique. Due to the limitations of nn, we will fail if nn reaches 100. 617 * Besides, that is four updates per hour on a file, which is kind of 618 * execessive anyway. 619 */ 620static int 621find_next_name(char *filename, int *fd) 622{ 623 int i; 624 time_t tval; 625 size_t len; 626 struct tm lt; 627 char yyyymmdd[MAXPATHLEN]; 628 char newname[MAXPATHLEN]; |
539 struct stat sb; 540 int ret; | |
541 542 /* Create the YYYYMMDD part of the filename */ 543 time(&tval); 544 lt = *localtime(&tval); 545 len = strftime(yyyymmdd, sizeof(yyyymmdd), newfile_format, <); 546 if (len == 0) { 547 syslog(LOG_WARNING, 548 "Filename suffix too long (%d characters maximum)", 549 MAXPATHLEN); 550 return (EACCESS); 551 } 552 553 /* Make sure the new filename is not too long */ 554 if (strlen(filename) > MAXPATHLEN - len - 5) { 555 syslog(LOG_WARNING, | 629 630 /* Create the YYYYMMDD part of the filename */ 631 time(&tval); 632 lt = *localtime(&tval); 633 len = strftime(yyyymmdd, sizeof(yyyymmdd), newfile_format, <); 634 if (len == 0) { 635 syslog(LOG_WARNING, 636 "Filename suffix too long (%d characters maximum)", 637 MAXPATHLEN); 638 return (EACCESS); 639 } 640 641 /* Make sure the new filename is not too long */ 642 if (strlen(filename) > MAXPATHLEN - len - 5) { 643 syslog(LOG_WARNING, |
556 "Filename too long (%d characters, %d maximum)", | 644 "Filename too long (%zd characters, %zd maximum)", |
557 strlen(filename), MAXPATHLEN - len - 5); 558 return (EACCESS); 559 } 560 561 /* Find the first file which doesn't exist */ 562 for (i = 0; i < 100; i++) { 563 sprintf(newname, "%s.%s.%02d", filename, yyyymmdd, i); 564 *fd = open(newname, --- 14 unchanged lines hidden (view full) --- 579 * readable/writable. 580 * If we were invoked with arguments 581 * from inetd then the file must also be 582 * in one of the given directory prefixes. 583 * Note also, full path name must be 584 * given as we have no login directory. 585 */ 586int | 645 strlen(filename), MAXPATHLEN - len - 5); 646 return (EACCESS); 647 } 648 649 /* Find the first file which doesn't exist */ 650 for (i = 0; i < 100; i++) { 651 sprintf(newname, "%s.%s.%02d", filename, yyyymmdd, i); 652 *fd = open(newname, --- 14 unchanged lines hidden (view full) --- 667 * readable/writable. 668 * If we were invoked with arguments 669 * from inetd then the file must also be 670 * in one of the given directory prefixes. 671 * Note also, full path name must be 672 * given as we have no login directory. 673 */ 674int |
587validate_access(char **filep, int mode) | 675validate_access(int peer, char **filep, int mode) |
588{ 589 struct stat stbuf; 590 int fd; 591 int error; 592 struct dirlist *dirp; 593 static char pathname[MAXPATHLEN]; 594 char *filename = *filep; 595 --- 59 unchanged lines hidden (view full) --- 655 err = EACCESS; 656 } 657 } 658 if (dirp->name != NULL) 659 *filep = filename = pathname; 660 else if (mode == RRQ) 661 return (err); 662 } | 676{ 677 struct stat stbuf; 678 int fd; 679 int error; 680 struct dirlist *dirp; 681 static char pathname[MAXPATHLEN]; 682 char *filename = *filep; 683 --- 59 unchanged lines hidden (view full) --- 743 err = EACCESS; 744 } 745 } 746 if (dirp->name != NULL) 747 *filep = filename = pathname; 748 else if (mode == RRQ) 749 return (err); 750 } |
663 if (options[OPT_TSIZE].o_request) { 664 if (mode == RRQ) 665 options[OPT_TSIZE].o_reply = stbuf.st_size; 666 else 667 /* XXX Allows writes of all sizes. */ 668 options[OPT_TSIZE].o_reply = 669 atoi(options[OPT_TSIZE].o_request); 670 } | 751 752 /* 753 * This option is handled here because it (might) require(s) the 754 * size of the file. 755 */ 756 option_tsize(peer, NULL, mode, &stbuf); 757 |
671 if (mode == RRQ) 672 fd = open(filename, O_RDONLY); 673 else { 674 if (create_new) { 675 if (increase_name) { 676 error = find_next_name(filename, &fd); 677 if (error > 0) 678 return (error + 100); --- 10 unchanged lines hidden (view full) --- 689 file = fdopen(fd, (mode == RRQ)? "r":"w"); 690 if (file == NULL) { 691 close(fd); 692 return (errno + 100); 693 } 694 return (0); 695} 696 | 758 if (mode == RRQ) 759 fd = open(filename, O_RDONLY); 760 else { 761 if (create_new) { 762 if (increase_name) { 763 error = find_next_name(filename, &fd); 764 if (error > 0) 765 return (error + 100); --- 10 unchanged lines hidden (view full) --- 776 file = fdopen(fd, (mode == RRQ)? "r":"w"); 777 if (file == NULL) { 778 close(fd); 779 return (errno + 100); 780 } 781 return (0); 782} 783 |
697int timeouts; 698jmp_buf timeoutbuf; 699 700void 701timer(int sig __unused) | 784static void 785tftp_xmitfile(int peer, const char *mode) |
702{ | 786{ |
703 if (++timeouts > MAX_TIMEOUTS) 704 exit(1); 705 longjmp(timeoutbuf, 1); 706} | 787 uint16_t block; 788 uint32_t amount; 789 time_t now; 790 struct tftp_stats ts; |
707 | 791 |
708/* 709 * Send the requested file. 710 */ 711void 712xmitfile(struct formats *pf) 713{ 714 struct tftphdr *dp; 715 struct tftphdr *ap; /* ack packet */ 716 int size, n; 717 volatile unsigned short block; | 792 now = time(NULL); 793 if (debug&DEBUG_SIMPLE) 794 tftp_log(LOG_DEBUG, "Transmitting file"); |
718 | 795 |
719 signal(SIGALRM, timer); 720 dp = r_init(); 721 ap = (struct tftphdr *)ackbuf; | 796 read_init(0, file, mode); |
722 block = 1; | 797 block = 1; |
723 do { 724 size = readit(file, &dp, pf->f_convert); 725 if (size < 0) { 726 nak(errno + 100); 727 goto abort; 728 } 729 dp->th_opcode = htons((u_short)DATA); 730 dp->th_block = htons((u_short)block); 731 timeouts = 0; 732 (void)setjmp(timeoutbuf); 733 734send_data: 735 { 736 int i, t = 1; 737 for (i = 0; ; i++){ 738 if (send(peer, dp, size + 4, 0) != size + 4) { 739 sleep(t); 740 t = (t < 32) ? t<< 1 : t; 741 if (i >= 12) { 742 syslog(LOG_ERR, "write: %m"); 743 goto abort; 744 } 745 } 746 break; 747 } 748 } 749 read_ahead(file, pf->f_convert); 750 for ( ; ; ) { 751 alarm(rexmtval); /* read the ack */ 752 n = recv(peer, ackbuf, sizeof (ackbuf), 0); 753 alarm(0); 754 if (n < 0) { 755 syslog(LOG_ERR, "read: %m"); 756 goto abort; 757 } 758 ap->th_opcode = ntohs((u_short)ap->th_opcode); 759 ap->th_block = ntohs((u_short)ap->th_block); 760 761 if (ap->th_opcode == ERROR) 762 goto abort; 763 764 if (ap->th_opcode == ACK) { 765 if (ap->th_block == block) 766 break; 767 /* Re-synchronize with the other side */ 768 (void) synchnet(peer); 769 if (ap->th_block == (block -1)) 770 goto send_data; 771 } 772 773 } 774 block++; 775 } while (size == SEGSIZE); 776abort: 777 (void) fclose(file); | 798 tftp_send(peer, &block, &ts); 799 read_close(); 800 if (debug&DEBUG_SIMPLE) 801 tftp_log(LOG_INFO, "Sent %d bytes in %d seconds", 802 amount, time(NULL) - now); |
778} 779 | 803} 804 |
780void 781justquit(int sig __unused) | 805static void 806tftp_recvfile(int peer, const char *mode) |
782{ | 807{ |
783 exit(0); 784} | 808 uint32_t filesize; 809 uint16_t block; 810 struct timeval now1, now2; 811 struct tftp_stats ts; |
785 | 812 |
813 gettimeofday(&now1, NULL); 814 if (debug&DEBUG_SIMPLE) 815 tftp_log(LOG_DEBUG, "Receiving file"); |
|
786 | 816 |
787/* 788 * Receive a file. 789 */ 790void 791recvfile(struct formats *pf) 792{ 793 struct tftphdr *dp; 794 struct tftphdr *ap; /* ack buffer */ 795 int n, size; 796 volatile unsigned short block; | 817 write_init(0, file, mode); |
797 | 818 |
798 signal(SIGALRM, timer); 799 dp = w_init(); 800 ap = (struct tftphdr *)ackbuf; | |
801 block = 0; | 819 block = 0; |
802 do { 803 timeouts = 0; 804 ap->th_opcode = htons((u_short)ACK); 805 ap->th_block = htons((u_short)block); 806 block++; 807 (void) setjmp(timeoutbuf); 808send_ack: 809 if (send(peer, ackbuf, 4, 0) != 4) { 810 syslog(LOG_ERR, "write: %m"); 811 goto abort; 812 } 813 write_behind(file, pf->f_convert); 814 for ( ; ; ) { 815 alarm(rexmtval); 816 n = recv(peer, dp, PKTSIZE, 0); 817 alarm(0); 818 if (n < 0) { /* really? */ 819 syslog(LOG_ERR, "read: %m"); 820 goto abort; 821 } 822 dp->th_opcode = ntohs((u_short)dp->th_opcode); 823 dp->th_block = ntohs((u_short)dp->th_block); 824 if (dp->th_opcode == ERROR) 825 goto abort; 826 if (dp->th_opcode == DATA) { 827 if (dp->th_block == block) { 828 break; /* normal */ 829 } 830 /* Re-synchronize with the other side */ 831 (void) synchnet(peer); 832 if (dp->th_block == (block-1)) 833 goto send_ack; /* rexmit */ 834 } 835 } 836 /* size = write(file, dp->th_data, n - 4); */ 837 size = writeit(file, &dp, n - 4, pf->f_convert); 838 if (size != (n-4)) { /* ahem */ 839 if (size < 0) nak(errno + 100); 840 else nak(ENOSPACE); 841 goto abort; 842 } 843 } while (size == SEGSIZE); 844 write_behind(file, pf->f_convert); 845 (void) fclose(file); /* close data file */ | 820 tftp_receive(peer, &block, &ts, NULL, 0); |
846 | 821 |
847 ap->th_opcode = htons((u_short)ACK); /* send the "final" ack */ 848 ap->th_block = htons((u_short)(block)); 849 (void) send(peer, ackbuf, 4, 0); | 822 write_close(); |
850 | 823 |
851 signal(SIGALRM, justquit); /* just quit on timeout */ 852 alarm(rexmtval); 853 n = recv(peer, buf, sizeof (buf), 0); /* normally times out and quits */ 854 alarm(0); 855 if (n >= 4 && /* if read some data */ 856 dp->th_opcode == DATA && /* and got a data block */ 857 block == dp->th_block) { /* then my last ack was lost */ 858 (void) send(peer, ackbuf, 4, 0); /* resend final ack */ 859 } 860abort: 861 return; 862} 863 864struct errmsg { 865 int e_code; 866 const char *e_msg; 867} errmsgs[] = { 868 { EUNDEF, "Undefined error code" }, 869 { ENOTFOUND, "File not found" }, 870 { EACCESS, "Access violation" }, 871 { ENOSPACE, "Disk full or allocation exceeded" }, 872 { EBADOP, "Illegal TFTP operation" }, 873 { EBADID, "Unknown transfer ID" }, 874 { EEXISTS, "File already exists" }, 875 { ENOUSER, "No such user" }, 876 { EOPTNEG, "Option negotiation" }, 877 { -1, 0 } 878}; 879 880static const char * 881errtomsg(int error) 882{ 883 static char ebuf[20]; 884 struct errmsg *pe; 885 if (error == 0) 886 return "success"; 887 for (pe = errmsgs; pe->e_code >= 0; pe++) 888 if (pe->e_code == error) 889 return pe->e_msg; 890 snprintf(ebuf, sizeof(buf), "error %d", error); 891 return ebuf; 892} 893 894/* 895 * Send a nak packet (error message). 896 * Error code passed in is one of the 897 * standard TFTP codes, or a UNIX errno 898 * offset by 100. 899 */ 900static void 901nak(int error) 902{ 903 struct tftphdr *tp; 904 int length; 905 struct errmsg *pe; 906 907 tp = (struct tftphdr *)buf; 908 tp->th_opcode = htons((u_short)ERROR); 909 tp->th_code = htons((u_short)error); 910 for (pe = errmsgs; pe->e_code >= 0; pe++) 911 if (pe->e_code == error) 912 break; 913 if (pe->e_code < 0) { 914 pe->e_msg = strerror(error - 100); 915 tp->th_code = EUNDEF; /* set 'undef' errorcode */ 916 } 917 strcpy(tp->th_msg, pe->e_msg); 918 length = strlen(pe->e_msg); 919 tp->th_msg[length] = '\0'; 920 length += 5; 921 if (send(peer, buf, length, 0) != length) 922 syslog(LOG_ERR, "nak: %m"); 923} 924 925/* translate IPv4 mapped IPv6 address to IPv4 address */ 926static void 927unmappedaddr(struct sockaddr_in6 *sin6) 928{ 929 struct sockaddr_in *sin4; 930 u_int32_t addr; 931 int port; 932 933 if (sin6->sin6_family != AF_INET6 || 934 !IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) 935 return; 936 sin4 = (struct sockaddr_in *)sin6; 937 addr = *(u_int32_t *)&sin6->sin6_addr.s6_addr[12]; 938 port = sin6->sin6_port; 939 memset(sin4, 0, sizeof(struct sockaddr_in)); 940 sin4->sin_addr.s_addr = addr; 941 sin4->sin_port = port; 942 sin4->sin_family = AF_INET; 943 sin4->sin_len = sizeof(struct sockaddr_in); 944} 945 946/* 947 * Send an oack packet (option acknowledgement). 948 */ 949static void 950oack(void) 951{ 952 struct tftphdr *tp, *ap; 953 int size, i, n; 954 char *bp; 955 956 tp = (struct tftphdr *)buf; 957 bp = buf + 2; 958 size = sizeof(buf) - 2; 959 tp->th_opcode = htons((u_short)OACK); 960 for (i = 0; options[i].o_type != NULL; i++) { 961 if (options[i].o_request) { 962 n = snprintf(bp, size, "%s%c%d", options[i].o_type, 963 0, options[i].o_reply); 964 bp += n+1; 965 size -= n+1; 966 if (size < 0) { 967 syslog(LOG_ERR, "oack: buffer overflow"); 968 exit(1); 969 } | 824 if (debug&DEBUG_SIMPLE) { 825 double f; 826 if (now1.tv_usec > now2.tv_usec) { 827 now2.tv_usec += 1000000; 828 now2.tv_sec--; |
970 } | 829 } |
971 } 972 size = bp - buf; 973 ap = (struct tftphdr *)ackbuf; 974 signal(SIGALRM, timer); 975 timeouts = 0; | |
976 | 830 |
977 (void)setjmp(timeoutbuf); 978 if (send(peer, buf, size, 0) != size) { 979 syslog(LOG_INFO, "oack: %m"); 980 exit(1); | 831 f = now2.tv_sec - now1.tv_sec + 832 (now2.tv_usec - now1.tv_usec) / 100000.0; 833 tftp_log(LOG_INFO, 834 "Download of %d bytes in %d blocks completed after %0.1f seconds\n", 835 filesize, block, f); |
981 } 982 | 836 } 837 |
983 for (;;) { 984 alarm(rexmtval); 985 n = recv(peer, ackbuf, sizeof (ackbuf), 0); 986 alarm(0); 987 if (n < 0) { 988 syslog(LOG_ERR, "recv: %m"); 989 exit(1); 990 } 991 ap->th_opcode = ntohs((u_short)ap->th_opcode); 992 ap->th_block = ntohs((u_short)ap->th_block); 993 if (ap->th_opcode == ERROR) 994 exit(1); 995 if (ap->th_opcode == ACK && ap->th_block == 0) 996 break; 997 } | 838 return; |
998} | 839} |
840 |
|