📄 tftpd.c
字号:
/* Copyright (c) 2005, Google Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following disclaimer * in the documentation and/or other materials provided with the * distribution. * * Neither the name of Google Inc. nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * --- * Author: Markus Gutschke * * This file demonstrates how to add a core dump interface to an existing * service. Typically, you would want to add the call to GetCoreDump() * to an existing interface exposed by your server. But if no such interface * exists, you could also adapt the TFTP code in this module to run as a * thread in your server. * * The code in this module does not perform any type of access control. * As corefiles can expose security sensitive data (e.g. passwords), you * would need to add appropriate access controls when using this (or similar) * code in a production environment. */#include <arpa/inet.h>#include <arpa/tftp.h>#include <errno.h>#include <fcntl.h>#include <getopt.h>#include <google/coredumper.h>#include <netinet/in.h>#include <pthread.h>#include <stdio.h>#include <stdlib.h>#include <string.h>#include <sys/socket.h>#include <sys/stat.h>#include <sys/time.h>#include <sys/types.h>#include <unistd.h>#undef NO_THREADS /* Support only one connection at a time. */#undef CAN_READ_FILES /* Supports reading files from "/tftpboot/..." */#undef CAN_WRITE_FILES /* Supports updating files in "/tftpboot/..." */#define CAN_READ_CORES /* Supports serving "core" snapshot files. */#ifndef TFTPHDRSIZE #define TFTPHDRSIZE 4#endif/* The "Request" structure contains all the parameters that get passed * from the main server loop to the thread that is processing a given * TFTP connection. */typedef struct Request { int debug; int id; int fd; struct tftphdr *tftp; char buf[TFTPHDRSIZE + SEGSIZE]; size_t count; struct sockaddr_in addr; socklen_t len; const char *core_name; char **dirs; int no_ack; int sanitize;} Request;#define DBG(...) do { \ if (debug) \ fprintf(stderr, __VA_ARGS__); \ } while (0)/* tftp_thread() runs in its own thread (unless NO_THREADS is defined). This * function processes a single TFTP connection. */static void *tftp_thread(void *arg) {#define debug (request->debug) Request *request = (Request *)arg; struct tftphdr *tftp = request->tftp; int fd = -1; int src = -1; int err = EBADOP; int ioerr = 0; struct sockaddr_in addr; socklen_t len; char *raw_file_name, *mode, *msg, msg_buf[80]; char *file_name = NULL; /* Create a new socket for this connection. */ if ((fd = socket(PF_INET, SOCK_DGRAM, 0)) < 0) { perror("socket"); exit(1); } /* Connect this socket to the outgoing interface. */ len = sizeof(addr); if (getsockname(request->fd, (struct sockaddr *)&addr, &len) >= 0 && len >= sizeof(struct sockaddr_in)) { DBG("Responding to %s:%d\n",inet_ntoa(addr.sin_addr),ntohs(addr.sin_port)); addr.sin_port = 0; bind(fd, (struct sockaddr *)&addr, len); } /* Get file name and transfer mode from the incoming TFTP packet. */ if (!(raw_file_name = request->buf + 2, mode = memchr(raw_file_name, '\000', request->count - 1 - (raw_file_name - request->buf))) || !(++mode, memchr(mode, '\000', request->count - (mode - request->buf)))){ char *ptr; msg = "Truncated request"; error: /* Send error message back to client. */ DBG("%s\n", msg); ptr = strrchr(strcpy(tftp->th_msg, msg), '\000') + 1; tftp->th_opcode = htons(ERROR); tftp->th_code = htons(err); sendto(fd, tftp, ptr-request->buf, 0, (struct sockaddr *)&request->addr, request->len); goto done; } /* Only text and binary transfer modes are supported. */ if (strcasecmp(mode, "netascii") && strcasecmp(mode, "octet")) { msg = "Unsupported transfer mode"; goto error; } /* Check whether client requested a "core" snapshot of the running process.*/ if (!strcmp(raw_file_name, request->core_name)) { #ifdef CAN_READ_CORES /* Core files must be transferred in binary. */ if (strcasecmp(mode, "octet")) { err = EBADOP; msg = "Core files must be transferred in binary"; goto error; } /* Writing core files is not a supported operation. */ if (ntohs(tftp->th_opcode) == WRQ) { err = EBADOP; msg = "Core files cannot be written"; goto error; } /* Here we go. Create a snapshot of this process. */ src = GetCoreDump(); /* If we failed to created a core file, report error to the client. */ if (src < 0) { err = ENOTFOUND; *msg_buf = '\000'; msg = strerror_r(errno, msg_buf, sizeof(msg_buf)); goto error; } #else err = ENOTFOUND; msg = "Core file support is not enabled"; goto error; #endif } else { #if defined(CAN_READ_FILES) || defined(CAN_WRITE_FILES) /* TFTP is a very simple protocol, which does not support any user * authentication/authorization. So, we have to be very conservative * when accessing files. Unless overridden on the command line, this * server will only access files underneath the "/tftpboot" directory. * It only serves world-readable files, and it only allow writing to * world-writable files. */ static char *tftpdirs[] = { "/tftpboot", NULL }; char **dirs = tftpdirs; struct stat sb; /* Unless the user requested otherwise, restrict to "/tftpboot/..." */ if (*request->dirs) { dirs = request->dirs; } /* If "sanitize" option is set, prepend "/tftpboot" (or name of the first * source directory listed on the command line) to any absolute file * names. */ if (*raw_file_name == '/' && request->sanitize) { char *path = dirs[0]; strcat(strcat(strcpy(malloc(strlen(path) + strlen(raw_file_name) + 2), path), "/"), raw_file_name); } else { file_name = strdup(raw_file_name); } /* Check file attributes. */ memset(&sb, 0, sizeof(sb)); if (*file_name == '/') { int ok = 0; char **ptr; /* Search for file in all source directories (normally just * "/tftpboot") */ for (ptr = &dirs[0]; *ptr; ptr++) { if (!strncmp(*ptr, file_name, strlen(*ptr)) && stat(file_name, &sb) >= 0 && S_ISREG(sb.st_mode)) { ok++; break; } } if (!ok) { file_not_found: /* Only pre-existing files can be accessed. */ if (request->no_ack) goto done; else { err = ENOTFOUND; msg = "File not found"; goto error; } } } else { char **ptr, *absolute_file_name = NULL; /* Search for file in all source directories (normally just * "/tftpboot") */ for (ptr = &dirs[0]; *ptr; ptr++) { absolute_file_name = strcat(strcat(strcpy(malloc(strlen(*ptr) + strlen(file_name) + 2), *ptr), "/"), file_name); if (stat(absolute_file_name, &sb) >= 0 && S_ISREG(sb.st_mode)) break; free(absolute_file_name); absolute_file_name = NULL; } if (!absolute_file_name) goto file_not_found; free(file_name); file_name = absolute_file_name; } /* Check whether the necessary support for reading/writing is compiled * into this server, and whether the file is world-readable/writable. */ if (ntohs(tftp->th_opcode) == WRQ) { #ifdef CAN_WRITE_FILES if (!(sb.st_mode & S_IWOTH) || (src = open(file_name, O_WRONLY)) < 0) #endif { access_denied: err = EACCESS; msg = "Access denied"; goto error; } } else { #ifdef CAN_READ_FILES if (!(sb.st_mode & S_IROTH) || (src = open(file_name, O_RDONLY)) < 0) #endif goto access_denied; } #else err = ENOTFOUND; msg = "File operations are not enabled"; goto error; #endif } if (ntohs(tftp->th_opcode) == RRQ) { DBG("received RRQ <%s, %s>\n", raw_file_name, mode); #if defined(CAN_READ_FILES) || defined(CAN_READ_CORES) unsigned short block = 0; int count; /* Mainloop for serving files to clients. */ do {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -