⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 tftpd.c

📁 unix下tftp服务器源码
💻 C
📖 第 1 页 / 共 3 页
字号:
  if ( to < 10000UL || to > 255000000UL || *vend )    return 0;    rexmtval = timeout = to;  maxtimeout = rexmtval*TIMEOUT_LIMIT;    sprintf(*ret = b_ret, "%lu", to);  return(1);}/* * Parse RFC2347 style options */voiddo_opt(char *opt, char *val, char **ap){  struct options *po;  char *ret;    /* Global option-parsing variables initialization */  blksize_set = 0;    if ( !*opt )    return;    for (po = options; po->o_opt; po++)    if (!strcasecmp(po->o_opt, opt)) {      if (po->o_fnc(val, &ret)) {	if (*ap + strlen(opt) + strlen(ret) + 2 >=	    ackbuf + sizeof(ackbuf)) {	  nak(EOPTNEG, "Insufficient space for options");	  exit(0);	}	*ap = strrchr(strcpy(strrchr(strcpy(*ap, opt),'\0') + 1,			     ret),'\0') + 1;      } else {	nak(EOPTNEG, "Unsupported option(s) requested");	exit(0);      }      break;    }  return;}#ifdef WITH_REGEX/* * This is called by the remap engine when it encounters macros such * as \i.  It should write the output in "output" if non-NULL, and * return the length of the output (generated or not). * * Return -1 on failure. */intrewrite_macros(char macro, char *output);intrewrite_macros(char macro, char *output){  char *p;  switch (macro) {  case 'i':    p = inet_ntoa(from.sin_addr);    if ( output )      strcpy(output, p);    return strlen(p);      case 'x':    if ( output )      sprintf(output, "%08X",	      ntohl(from.sin_addr.s_addr));    return 8;  default:    return -1;  }}/* * Modify the filename, if applicable.  If it returns NULL, deny the access. */char *rewrite_access(char *filename, int mode, const char **msg){  if ( rewrite_rules ) {    char *newname = rewrite_string(filename, rewrite_rules, mode != RRQ,				   rewrite_macros, msg);    filename = newname;  }  return filename;}#elsechar *rewrite_access(char *filename, int mode, const char **msg){  (void)mode;			/* Avoid warning */  (void)msg;  return filename;}#endifFILE *file;/* * Validate file access.  Since we * have no uid or gid, for now require * file to exist and be publicly * readable/writable, unless -p specified. * If we were invoked with arguments * from inetd then the file must also be * in one of the given directory prefixes. * Note also, full path name must be * given as we have no login directory. */intvalidate_access(char *filename, int mode,		struct formats *pf, const char **errmsg){  struct stat stbuf;  int   i, len;  int	fd, wmode, rmode;  char *cp;  const char **dirp;  char stdio_mode[3];    tsize_ok = 0;  *errmsg = NULL;    if (!secure) {    if (*filename != '/') {      *errmsg = "Only absolute filenames allowed";      return (EACCESS);    }    /*     * prevent tricksters from getting around the directory     * restrictions     */    len = strlen(filename);    for ( i = 1 ; i < len-3 ; i++ ) {      cp = filename + i;      if ( *cp == '.' && memcmp(cp-1, "/../", 4) == 0 ) {	*errmsg = "Reverse path not allowed";	return(EACCESS);      }    }    for (dirp = dirs; *dirp; dirp++)      if (strncmp(filename, *dirp, strlen(*dirp)) == 0)	break;    if (*dirp==0 && dirp!=dirs) {      *errmsg = "Forbidden directory";      return (EACCESS);    }  }    /*   * We use different a different permissions scheme if `cancreate' is   * set.   */  wmode = O_WRONLY |    (cancreate ? O_CREAT : 0) |    (unixperms ? O_TRUNC : 0) |    (pf->f_convert ? O_TEXT : O_BINARY);  rmode = O_RDONLY |    (pf->f_convert ? O_TEXT : O_BINARY);  fd = open(filename, mode == RRQ ? rmode : wmode, 0666);  if (fd < 0) {    switch (errno) {    case ENOENT:    case ENOTDIR:      return ENOTFOUND;    case ENOSPC:      return ENOSPACE;    case EEXIST:      return EEXISTS;    default:      return errno+100;    }  }  if ( fstat(fd, &stbuf) < 0 )    exit(EX_OSERR);		/* This shouldn't happen */  if (mode == RRQ) {    if ( !unixperms && (stbuf.st_mode & (S_IREAD >> 6)) == 0 ) {      *errmsg = "File must have global read permissions";      return (EACCESS);    }    tsize = stbuf.st_size;    /* We don't know the tsize if conversion is needed */    tsize_ok = !pf->f_convert;  } else {    if ( !unixperms ) {      if ( (stbuf.st_mode & (S_IWRITE >> 6)) == 0 ) {	*errmsg = "File must have global write permissions";	return (EACCESS);      }      /* We didn't get to truncate the file at open() time */#ifdef HAVE_FTRUNCATE      if ( ftruncate(fd, (off_t)0) ) {	*errmsg = "Cannot reset file size";	return(EACCESS);      }#endif    }    tsize = 0;    tsize_ok = 1;  }  stdio_mode[0] = (mode == RRQ) ? 'r':'w';  stdio_mode[1] = (pf->f_convert) ? 't':'b';  stdio_mode[2] = '\0';  file = fdopen(fd, stdio_mode);  if (file == NULL)    exit(EX_OSERR);		/* Internal error */  return (0);}/* * Send the requested file. */voidtftp_sendfile(struct formats *pf, struct tftphdr *oap, int oacklen){  struct tftphdr *dp;  struct tftphdr *ap;		/* ack packet */  static u_short block = 1;	/* Static to avoid longjmp funnies */  u_short ap_opcode, ap_block;  unsigned long r_timeout;  int size, n;    if (oap) {    timeout = rexmtval;    (void)sigsetjmp(timeoutbuf,1);  oack:    r_timeout = timeout;    if (send(peer, oap, oacklen, 0) != oacklen) {      syslog(LOG_WARNING, "tftpd: oack: %m\n");      goto abort;    }    for ( ; ; ) {      n = recv_time(peer, ackbuf, sizeof(ackbuf), 0, &r_timeout);      if (n < 0) {	syslog(LOG_WARNING, "tftpd: read: %m\n");	goto abort;      }      ap = (struct tftphdr *)ackbuf;      ap_opcode = ntohs((u_short)ap->th_opcode);      ap_block = ntohs((u_short)ap->th_block);            if (ap_opcode == ERROR) {	syslog(LOG_WARNING, "tftp: client does not accept options\n");	goto abort;      }      if (ap_opcode == ACK) {	if (ap_block == 0)	  break;	/* Resynchronize with the other side */	(void)synchnet(peer);	goto oack;      }    }  }  dp = r_init();  do {    size = readit(file, &dp, pf->f_convert);    if (size < 0) {      nak(errno + 100, NULL);      goto abort;    }    dp->th_opcode = htons((u_short)DATA);    dp->th_block = htons((u_short)block);    timeout = rexmtval;    (void) sigsetjmp(timeoutbuf,1);        r_timeout = timeout;    if (send(peer, dp, size + 4, 0) != size + 4) {      syslog(LOG_WARNING, "tftpd: write: %m");      goto abort;    }    read_ahead(file, pf->f_convert);    for ( ; ; ) {      n = recv_time(peer, ackbuf, sizeof (ackbuf), 0, &r_timeout);      if (n < 0) {	syslog(LOG_WARNING, "tftpd: read(ack): %m");	goto abort;      }      ap = (struct tftphdr *)ackbuf;      ap_opcode = ntohs((u_short)ap->th_opcode);      ap_block = ntohs((u_short)ap->th_block);            if (ap_opcode == ERROR)	goto abort;            if (ap_opcode == ACK) {	if (ap_block == block) {	  break;	}				/* Re-synchronize with the other side */	(void) synchnet(peer);	/*	 * RFC1129/RFC1350: We MUST NOT re-send the DATA	 * packet in response to an invalid ACK.  Doing so	 * would cause the Sorcerer's Apprentice bug.	 */      }          }    block++;  } while (size == segsize); abort:  (void) fclose(file);}/* Bail out signal handler */voidjustquit(int sig){  (void)sig;			/* Suppress unused warning */  exit(0);}/* * Receive a file. */voidtftp_recvfile(struct formats *pf, struct tftphdr *oap, int oacklen){  struct tftphdr *dp;  int n, size;  /* These are "static" to avoid longjmp funnies */  static struct tftphdr *ap;    /* ack buffer */  static u_short block = 0;  static int acksize;  u_short dp_opcode, dp_block;  unsigned long r_timeout;  dp = w_init();  do {    timeout = rexmtval;        if (!block && oap) {      ap = (struct tftphdr *)ackbuf;      acksize = oacklen;    } else {      ap = (struct tftphdr *)ackbuf;      ap->th_opcode = htons((u_short)ACK);      ap->th_block = htons((u_short)block);      acksize = 4;    }    block++;    (void) sigsetjmp(timeoutbuf,1);  send_ack:    r_timeout = timeout;    if (send(peer, ackbuf, acksize, 0) != acksize) {      syslog(LOG_WARNING, "tftpd: write(ack): %m");      goto abort;    }    write_behind(file, pf->f_convert);    for ( ; ; ) {      n = recv_time(peer, dp, PKTSIZE, 0, &r_timeout);      if (n < 0) {		/* really? */	syslog(LOG_WARNING, "tftpd: read: %m");	goto abort;      }      dp_opcode = ntohs((u_short)dp->th_opcode);      dp_block = ntohs((u_short)dp->th_block);      if (dp_opcode == ERROR)	goto abort;      if (dp_opcode == DATA) {	if (dp_block == block) {	  break;   /* normal */	}				/* Re-synchronize with the other side */	(void) synchnet(peer);	if (dp_block == (block-1))	  goto send_ack;		/* rexmit */      }    }    /*  size = write(file, dp->th_data, n - 4); */    size = writeit(file, &dp, n - 4, pf->f_convert);    if (size != (n-4)) {			/* ahem */      if (size < 0) nak(errno + 100, NULL);      else nak(ENOSPACE, NULL);      goto abort;    }  } while (size == segsize);  write_behind(file, pf->f_convert);  (void) fclose(file);		/* close data file */    ap->th_opcode = htons((u_short)ACK);    /* send the "final" ack */  ap->th_block = htons((u_short)(block));  (void) send(peer, ackbuf, 4, 0);    timeout_quit = 1;		/* just quit on timeout */  n = recv_time(peer, buf, sizeof (buf), 0, &timeout); /* normally times out and quits */  timeout_quit = 0;  if (n >= 4 &&			/* if read some data */      dp_opcode == DATA &&	/* and got a data block */      block == dp_block) {	/* then my last ack was lost */    (void) send(peer, ackbuf, 4, 0);     /* resend final ack */  } abort:  return;}static const char * const errmsgs[] ={  "Undefined error code", 			/* 0 - EUNDEF */  "File not found",				/* 1 - ENOTFOUND */  "Access denied",				/* 2 - EACCESS */  "Disk full or allocation exceeded", 		/* 3 - ENOSPACE */  "Illegal TFTP operation",			/* 4 - EBADOP */  "Unknown transfer ID",			/* 5 - EBADID */  "File already exists",			/* 6 - EEXISTS */  "No such user",				/* 7 - ENOUSER */  "Failure to negotiate RFC2347 options" 	/* 8 - EOPTNEG */};#define ERR_CNT (sizeof(errmsgs)/sizeof(const char *))/* * Send a nak packet (error message). * Error code passed in is one of the * standard TFTP codes, or a UNIX errno * offset by 100. */static voidnak(int error, const char *msg){  struct tftphdr *tp;  int length;    tp = (struct tftphdr *)buf;  tp->th_opcode = htons((u_short)ERROR);  if ( error >= 100 ) {    /* This is a Unix errno+100 */    if ( !msg )      msg = strerror(error - 100);    error = EUNDEF;  } else {    if ( (unsigned)error >= ERR_CNT )      error = EUNDEF;    if ( !msg )      msg = errmsgs[error];  }  tp->th_code = htons((u_short)error);  length = strlen(msg)+1;  memcpy(tp->th_msg, msg, length);  length += 4;			/* Add space for header */    if ( verbosity >= 2 ) {    syslog(LOG_INFO, "sending NAK (%d, %s) to %s",	   error, tp->th_msg, inet_ntoa(from.sin_addr));  }    if (send(peer, buf, length, 0) != length)    syslog(LOG_WARNING, "nak: %m");}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -