| version 1.1.2.4, 2009/11/14 00:37:20 | version 1.3.2.1, 2012/07/22 22:54:53 | 
| Line 1 | Line 1 | 
 |  | /************************************************************************* | 
 |  | * (C) 2010 AITNET - Sofia/Bulgaria - <office@aitbg.com> | 
 |  | *  by Michael Pounov <misho@aitbg.com> | 
 |  | * | 
 |  | * $Author$ | 
 |  | * $Id$ | 
 |  | * | 
 |  | ************************************************************************* | 
 |  | The ELWIX and AITNET software is distributed under the following | 
 |  | terms: | 
 |  |  | 
 |  | All of the documentation and software included in the ELWIX and AITNET | 
 |  | Releases is copyrighted by ELWIX - Sofia/Bulgaria <info@elwix.org> | 
 |  |  | 
 |  | Copyright 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 | 
 |  | by Michael Pounov <misho@elwix.org>.  All rights reserved. | 
 |  |  | 
 |  | Redistribution and use in source and binary forms, with or without | 
 |  | modification, are permitted provided that the following conditions | 
 |  | are met: | 
 |  | 1. Redistributions of source code must retain the above copyright | 
 |  | notice, this list of conditions and the following disclaimer. | 
 |  | 2. 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. | 
 |  | 3. All advertising materials mentioning features or use of this software | 
 |  | must display the following acknowledgement: | 
 |  | This product includes software developed by Michael Pounov <misho@elwix.org> | 
 |  | ELWIX - Embedded LightWeight unIX and its contributors. | 
 |  | 4. Neither the name of AITNET 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 AITNET 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 REGENTS 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. | 
 |  | */ | 
 | #include "global.h" | #include "global.h" | 
 | #include "upd.h" | #include "upd.h" | 
 |  |  | 
 |  |  | 
 |  | int Kill; | 
 |  |  | 
 |  |  | 
 | static inline int ChkImg(const char *csImg, char *psDir) | static inline int ChkImg(const char *csImg, char *psDir) | 
 | { | { | 
 | int res = 0; | int res = 0; | 
| Line 197  int Rollback(const char *csImg) | Line 245  int Rollback(const char *csImg) | 
 |  |  | 
 | int tFTP(const char *csImg, const char *psDir) | int tFTP(const char *csImg, const char *psDir) | 
 | { | { | 
| int res = 0; | int src, dst, len; | 
| char szDir[MAXPATHLEN]; | u_char buf[BUFSIZ]; | 
|  | char szDir[MAXPATHLEN], szFile[MAXPATHLEN]; | 
|  | struct stat ss, ds; | 
 |  |  | 
 | if (ChkImg(csImg, szDir) == -1) | if (ChkImg(csImg, szDir) == -1) | 
 | return -1; | return -1; | 
 |  | else { | 
 |  | memset(&ss, 0, sizeof ss); | 
 |  | if (stat(szDir, &ss) == -1) { | 
 |  | printf("Error:: Unable to find image %s #%d - %s\n", | 
 |  | szDir, errno, strerror(errno)); | 
 |  | return -1; | 
 |  | } | 
 |  | } | 
 |  |  | 
 | VERB(3) printf("tFTP procedure for %s to %s\n", szDir, psDir); | VERB(3) printf("tFTP procedure for %s to %s\n", szDir, psDir); | 
 |  | strlcpy(szFile, psDir, MAXPATHLEN); | 
 |  | strlcat(szFile, "/", MAXPATHLEN); | 
 |  | strlcat(szFile, csImg, MAXPATHLEN); | 
 |  | memset(&ds, 0, sizeof ds); | 
 |  | if (stat(szFile, &ds) == -1 && ENOENT != errno) { | 
 |  | printf("Error:: Unable to stat target %s #%d - %s\n", | 
 |  | szFile, errno, strerror(errno)); | 
 |  | return -1; | 
 |  | } | 
 |  |  | 
| res = Backup(csImg, psDir); | if (ss.st_dev == ds.st_dev && ss.st_ino == ds.st_ino) { | 
|  | printf("Error:: Unable to copy into self ...\n"); | 
|  | return -1; | 
|  | } | 
 |  |  | 
| if (!res) { | dst = open(szFile, O_WRONLY | O_CREAT | O_TRUNC, 0644); | 
|  | if (dst == -1) { | 
|  | printf("Error:: in create backup %s #%d - %s\n", | 
|  | szFile, errno, strerror(errno)); | 
|  | return -1; | 
|  | } | 
|  | src = open(szDir, O_RDONLY); | 
|  | if (src == -1) { | 
|  | printf("Error:: in open image %s #%d - %s\n", | 
|  | szDir, errno, strerror(errno)); | 
|  | close(dst); | 
|  | unlink(szFile); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | while ((len = read(src, buf, BUFSIZ)) > 0) | 
|  | if (write(dst, buf, len) == -1) { | 
|  | printf("Error:: in write backup #%d - %s\n", | 
|  | errno, strerror(errno)); | 
|  | close(src); | 
|  | close(dst); | 
|  | unlink(szFile); | 
|  |  | 
|  | len = -1; | 
|  | break; | 
|  | } | 
|  |  | 
|  | close(src); | 
|  | close(dst); | 
|  |  | 
|  | if (!len) { | 
 | syslog(LOG_NOTICE, "Export tFTP image %s to %s", csImg, psDir); | syslog(LOG_NOTICE, "Export tFTP image %s to %s", csImg, psDir); | 
 | VERB(1) printf("Export tFTP image %s to %s\n", csImg, psDir); | VERB(1) printf("Export tFTP image %s to %s\n", csImg, psDir); | 
 | } | } | 
| return res; | return len; | 
 | } | } | 
 |  |  | 
| int Backup(const char *csImg, const char *psDir) | int Backup(const char *csImg) | 
 | { | { | 
 | int src, dst, len; | int src, dst, len; | 
 | u_char buf[BUFSIZ]; | u_char buf[BUFSIZ]; | 
| Line 223  int Backup(const char *csImg, const char *psDir) | Line 323  int Backup(const char *csImg, const char *psDir) | 
 | if (ChkImg(csImg, szDir) == -1) | if (ChkImg(csImg, szDir) == -1) | 
 | return -1; | return -1; | 
 |  |  | 
| if (!psDir) | getcwd(szFile, MAXPATHLEN); | 
| getcwd(szFile, MAXPATHLEN); |  | 
| else |  | 
| strlcpy(szFile, psDir, MAXPATHLEN); |  | 
 | strlcat(szFile, "/", MAXPATHLEN); | strlcat(szFile, "/", MAXPATHLEN); | 
 | strlcat(szFile, FIRMWARE_BAK, MAXPATHLEN); | strlcat(szFile, FIRMWARE_BAK, MAXPATHLEN); | 
 |  | VERB(3) printf("Backup procedure for %s\n", szDir); | 
 |  |  | 
 | if (!psDir) |  | 
 | VERB(3) printf("Backup procedure for %s\n", szDir); |  | 
 |  |  | 
 | dst = open(szFile, O_WRONLY | O_CREAT | O_TRUNC, 0644); | dst = open(szFile, O_WRONLY | O_CREAT | O_TRUNC, 0644); | 
 | if (dst == -1) { | if (dst == -1) { | 
 | printf("Error:: in create backup %s #%d - %s\n", | printf("Error:: in create backup %s #%d - %s\n", | 
| Line 263  int Backup(const char *csImg, const char *psDir) | Line 358  int Backup(const char *csImg, const char *psDir) | 
 | close(src); | close(src); | 
 | close(dst); | close(dst); | 
 |  |  | 
| if (!psDir && !len) { | if (!len) { | 
 | syslog(LOG_NOTICE, "Backup image %s", csImg); | syslog(LOG_NOTICE, "Backup image %s", csImg); | 
 | VERB(1) printf("Backup image %s\n", csImg); | VERB(1) printf("Backup image %s\n", csImg); | 
 | } | } | 
| Line 290  int Clean(const char *csImg) | Line 385  int Clean(const char *csImg) | 
 |  |  | 
 | syslog(LOG_NOTICE, "Clean backup for image %s", csImg); | syslog(LOG_NOTICE, "Clean backup for image %s", csImg); | 
 | VERB(1) printf("Clean backup for image %s\n", csImg); | VERB(1) printf("Clean backup for image %s\n", csImg); | 
 |  | return 0; | 
 |  | } | 
 |  |  | 
 |  | // ---------------------------------- | 
 |  |  | 
 |  | static void Signal(int sig) | 
 |  | { | 
 |  | int stat; | 
 |  |  | 
 |  | switch (sig) { | 
 |  | case SIGHUP: | 
 |  | VERB(5) printf("Info(5):: Signal arrived #%d\n", sig); | 
 |  | break; | 
 |  | case SIGCHLD: | 
 |  | while (waitpid(-1, &stat, WNOHANG) > 0); | 
 |  | break; | 
 |  | case SIGTERM: | 
 |  | Kill = 1; | 
 |  | VERB(5) printf("Info(5):: Signal arrived #%d\n", sig); | 
 |  | break; | 
 |  | } | 
 |  | } | 
 |  |  | 
 |  | int Daemonize(struct sockaddr_in sin, const char *csTFTP) | 
 |  | { | 
 |  | int s, n = 1; | 
 |  | pid_t pid; | 
 |  | fd_set rfd; | 
 |  | u_short seq = 0xffff; | 
 |  | int clilen, len, f, retry; | 
 |  | struct sockaddr_in cli, rcv; | 
 |  | struct sigaction sa; | 
 |  | u_char buf[TFTP_BUF], *pos; | 
 |  | char szFName[STRSIZ]; | 
 |  | struct tftphdr *tftp = (struct tftphdr*) buf; | 
 |  | struct timeval tv = { TFTP_TIMEOUT, 0 }; | 
 |  |  | 
 |  | VERB(3) printf("Daemonize procedure for %s:%d to %s\n", | 
 |  | inet_ntoa(sin.sin_addr), ntohs(sin.sin_port), csTFTP); | 
 |  |  | 
 |  | memset(&sa, 0, sizeof sa); | 
 |  | sigemptyset(&sa.sa_mask); | 
 |  | sa.sa_handler = Signal; | 
 |  | sigaction(SIGCHLD, &sa, NULL); | 
 |  | sigaction(SIGTERM, &sa, NULL); | 
 |  | sigaction(SIGHUP, &sa, NULL); | 
 |  |  | 
 |  | s = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); | 
 |  | if (s == -1) { | 
 |  | printf("Error:: in create socket #%d - %s\n", errno, strerror(errno)); | 
 |  | return -1; | 
 |  | } | 
 |  | if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &n, sizeof n) == -1) { | 
 |  | printf("Error:: in socket options #%d - %s\n", errno, strerror(errno)); | 
 |  | close(s); | 
 |  | return -1; | 
 |  | } | 
 |  | if (bind(s, (struct sockaddr*) &sin, sizeof sin) == -1) { | 
 |  | printf("Error:: in bind #%d - %s\n", errno, strerror(errno)); | 
 |  | close(s); | 
 |  | return -1; | 
 |  | } | 
 |  |  | 
 |  | switch ((pid = fork())) { | 
 |  | case -1: | 
 |  | printf("Error:: in socket options #%d - %s\n", errno, strerror(errno)); | 
 |  | close(s); | 
 |  | return -1; | 
 |  | case 0: | 
 |  | setsid(); | 
 |  |  | 
 |  | while (!Kill) { | 
 |  | FD_ZERO(&rfd); | 
 |  | FD_SET(s, &rfd); | 
 |  | if (select(s + 1, &rfd, NULL, NULL, NULL) == -1) | 
 |  | continue; | 
 |  |  | 
 |  | memset(buf, 0, TFTP_BUF); | 
 |  | clilen = sizeof cli; | 
 |  | len = recvfrom(s, buf, TFTP_BUF, 0, (struct sockaddr*) &cli, | 
 |  | (socklen_t*) &clilen); | 
 |  | if (len == -1) { | 
 |  | VERB(5) printf("Error:: in recvfrom #%d - %s\n", | 
 |  | errno, strerror(errno)); | 
 |  | continue; | 
 |  | } | 
 |  |  | 
 |  | tftp = (struct tftphdr*) (pos = buf); | 
 |  | VERB(5) printf("Info(5):: Received packet from %s len %d with opcode=%hd\n", | 
 |  | inet_ntoa(cli.sin_addr), len, ntohs(tftp->th_opcode)); | 
 |  | switch (ntohs(tftp->th_opcode)) { | 
 |  | case WRQ: | 
 |  | len -= 2; | 
 |  | pos += 2; | 
 |  | strlcpy(szFName, (char*) pos, sizeof szFName); | 
 |  | VERB(5) printf("Info(5):: Get filename %s\n", szFName); | 
 |  | len -= strlen((char*) pos) + 1; | 
 |  | pos += strlen((char*) pos) + 1; | 
 |  | VERB(5) printf("Info(5):: Get mode %s len=%d\n", pos, len); | 
 |  | if (strncmp((char*) pos, "octet", len)) { | 
 |  | memset(buf, 0, TFTP_BUF); | 
 |  | tftp->th_opcode = htons(ERROR); | 
 |  | tftp->th_code = htons(EBADOP); | 
 |  | strncpy(tftp->th_data, "Error:: mode not supported", 27); | 
 |  | sendto(s, buf, 31, 0, (struct sockaddr*) &cli, sizeof cli); | 
 |  | continue; | 
 |  | } | 
 |  | VERB(2) printf("Info(2):: Receive file %s from %s\n", | 
 |  | szFName, inet_ntoa(cli.sin_addr)); | 
 |  |  | 
 |  | f = open(szFName, O_WRONLY | O_CREAT | O_EXCL, 0644); | 
 |  | if (f == -1) { | 
 |  | memset(buf, 0, TFTP_BUF); | 
 |  | tftp->th_opcode = htons(ERROR); | 
 |  | switch (errno) { | 
 |  | case EACCES: | 
 |  | case EPERM: | 
 |  | tftp->th_code = htons(EACCESS); | 
 |  | break; | 
 |  | case EEXIST: | 
 |  | tftp->th_code = htons(EEXISTS); | 
 |  | break; | 
 |  | default: | 
 |  | tftp->th_code = htons(ENOSPACE); | 
 |  | } | 
 |  | snprintf(tftp->th_data, STRSIZ, "Error:: in file %s", | 
 |  | strerror(errno)); | 
 |  | sendto(s, buf, strlen(tftp->th_data) + 4, 0, | 
 |  | (struct sockaddr*) &cli, sizeof cli); | 
 |  | continue; | 
 |  | } | 
 |  |  | 
 |  | memset(buf, 0, TFTP_BUF); | 
 |  | tftp->th_opcode = htons(ACK); | 
 |  | tftp->th_block = seq = 0; | 
 |  | sendto(s, tftp, 4, 0, (struct sockaddr*) &cli, sizeof cli); | 
 |  |  | 
 |  | for (retry = TFTP_RETRY; retry;) { | 
 |  | FD_ZERO(&rfd); | 
 |  | FD_SET(s, &rfd); | 
 |  | if (select(s + 1, &rfd, NULL, NULL, &tv) < 1) { | 
 |  | retry--; | 
 |  | continue; | 
 |  | } | 
 |  | memset(buf, 0, TFTP_BUF); | 
 |  | clilen = sizeof sin; | 
 |  | if ((len = recvfrom(s, buf, TFTP_BUF, 0, | 
 |  | (struct sockaddr*) &rcv, | 
 |  | (socklen_t*) &clilen)) == -1) { | 
 |  | VERB(5) printf("Error:: in recvfrom #%d - %s\n", | 
 |  | errno, strerror(errno)); | 
 |  | retry--; | 
 |  | continue; | 
 |  | } else | 
 |  | if (cli.sin_addr.s_addr != rcv.sin_addr.s_addr) | 
 |  | continue; | 
 |  |  | 
 |  | VERB(5) printf("Info(5):: received opcode=%d block=%d seq=%d\n", | 
 |  | ntohs(tftp->th_opcode), ntohs(tftp->th_block), seq); | 
 |  | if (ERROR == ntohs(tftp->th_opcode)) { | 
 |  | syslog(LOG_ERR, "Error:: in tftp receiving #%d - %s", | 
 |  | tftp->th_code, tftp->th_data); | 
 |  | close(f); | 
 |  | unlink(szFName); | 
 |  | break; | 
 |  | } | 
 |  | if (DATA == ntohs(tftp->th_opcode) && | 
 |  | ++seq == ntohs(tftp->th_block)) { | 
 |  | if (write(f, tftp->th_data, len - 4) == -1) { | 
 |  | syslog(LOG_ERR, "Error:: Can`t write to file %s\n", | 
 |  | szFName); | 
 |  | close(f); | 
 |  | unlink(szFName); | 
 |  | break; | 
 |  | } | 
 |  |  | 
 |  | memset(buf, 0, TFTP_BUF); | 
 |  | tftp->th_opcode = htons(ACK); | 
 |  | tftp->th_block = htons(seq); | 
 |  | sendto(s, tftp, 4, 0, (struct sockaddr*) &rcv, sizeof rcv); | 
 |  | VERB(5) printf("Info(5):: Send ACK for %s block=%d\n", | 
 |  | inet_ntoa(cli.sin_addr), seq); | 
 |  |  | 
 |  | if (SEGSIZE > len - 4) { | 
 |  | close(f); | 
 |  | syslog(LOG_INFO, "TFTP transfer complete to %s", | 
 |  | szFName); | 
 |  | break; | 
 |  | } | 
 |  | } else { | 
 |  | memset(buf, 0, TFTP_BUF); | 
 |  | tftp->th_opcode = htons(ERROR); | 
 |  | tftp->th_code = htons(EBADID); | 
 |  | snprintf(tftp->th_data, STRSIZ, "Error:: in transfer #%d", | 
 |  | seq); | 
 |  | sendto(s, buf, strlen(tftp->th_data) + 4, 0, | 
 |  | (struct sockaddr*) &cli, sizeof cli); | 
 |  | retry--; | 
 |  | } | 
 |  | } | 
 |  | if (!retry) { | 
 |  | close(f); | 
 |  | unlink(szFName); | 
 |  | syslog(LOG_ERR, "Error:: transfer aborted - timeout!"); | 
 |  | } | 
 |  |  | 
 |  | break; | 
 |  | case RRQ: | 
 |  | len -= 2; | 
 |  | pos += 2; | 
 |  | strlcpy(szFName, (char*) pos, sizeof szFName); | 
 |  | VERB(5) printf("Info(5):: Get filename %s\n", szFName); | 
 |  | len -= strlen((char*) pos) + 1; | 
 |  | pos += strlen((char*) pos) + 1; | 
 |  | VERB(5) printf("Info(5):: Get mode %s len=%d\n", pos, len); | 
 |  | if (strncmp((char*) pos, "octet", len)) { | 
 |  | memset(buf, 0, TFTP_BUF); | 
 |  | tftp->th_opcode = htons(ERROR); | 
 |  | tftp->th_code = htons(EBADOP); | 
 |  | strncpy(tftp->th_data, "Error:: mode not supported", 27); | 
 |  | sendto(s, buf, 31, 0, (struct sockaddr*) &cli, sizeof cli); | 
 |  | continue; | 
 |  | } | 
 |  | VERB(2) printf("Info(2):: Send file %s to %s\n", | 
 |  | szFName, inet_ntoa(cli.sin_addr)); | 
 |  |  | 
 |  | f = open(szFName, O_RDONLY); | 
 |  | if (f == -1) { | 
 |  | memset(buf, 0, TFTP_BUF); | 
 |  | tftp->th_opcode = htons(ERROR); | 
 |  | switch (errno) { | 
 |  | case EACCES: | 
 |  | case EPERM: | 
 |  | tftp->th_code = htons(EACCESS); | 
 |  | break; | 
 |  | case ENOENT: | 
 |  | tftp->th_code = htons(ENOTFOUND); | 
 |  | break; | 
 |  | default: | 
 |  | tftp->th_code = htons(ENOSPACE); | 
 |  | } | 
 |  | snprintf(tftp->th_data, STRSIZ, "Error:: in file %s", | 
 |  | strerror(errno)); | 
 |  | sendto(s, buf, strlen(tftp->th_data) + 4, 0, | 
 |  | (struct sockaddr*) &cli, sizeof cli); | 
 |  | continue; | 
 |  | } else | 
 |  | seq = 1; | 
 |  |  | 
 |  | for (retry = TFTP_RETRY; retry;) { | 
 |  | tftp->th_opcode = htons(DATA); | 
 |  | tftp->th_block = htons(seq); | 
 |  | if ((len = read(f, tftp->th_data, SEGSIZE)) == -1) { | 
 |  | syslog(LOG_ERR, "Error:: Can`t read from file %s\n", | 
 |  | szFName); | 
 |  | close(f); | 
 |  | break; | 
 |  | } else | 
 |  | sendto(s, buf, len + 4, 0, | 
 |  | (struct sockaddr*) &cli, sizeof cli); | 
 |  |  | 
 |  | FD_ZERO(&rfd); | 
 |  | FD_SET(s, &rfd); | 
 |  | if (select(s + 1, &rfd, NULL, NULL, &tv) < 1) { | 
 |  | retry--; | 
 |  | continue; | 
 |  | } | 
 |  | memset(buf, 0, TFTP_BUF); | 
 |  | clilen = sizeof sin; | 
 |  | if (recvfrom(s, buf, TFTP_BUF, 0, (struct sockaddr*) &rcv, | 
 |  | (socklen_t*) &clilen) == -1) { | 
 |  | VERB(5) printf("Error:: in recvfrom #%d - %s\n", | 
 |  | errno, strerror(errno)); | 
 |  | retry--; | 
 |  | continue; | 
 |  | } else | 
 |  | if (cli.sin_addr.s_addr != rcv.sin_addr.s_addr) | 
 |  | continue; | 
 |  |  | 
 |  | VERB(5) printf("Info(5):: received opcode=%d block=%d seq=%d\n", | 
 |  | ntohs(tftp->th_opcode), ntohs(tftp->th_block), seq); | 
 |  | if (ERROR == ntohs(tftp->th_opcode)) { | 
 |  | syslog(LOG_ERR, "Error:: in tftp sending #%d - %s", | 
 |  | tftp->th_code, tftp->th_data); | 
 |  | if (lseek(f, len * -1, SEEK_CUR) == -1) { | 
 |  | syslog(LOG_ERR, "Error:: revert sending #%d - %s", | 
 |  | errno, strerror(errno)); | 
 |  | close(f); | 
 |  | break; | 
 |  | } | 
 |  | retry--; | 
 |  | continue; | 
 |  | } | 
 |  | if (ACK == ntohs(tftp->th_opcode) && | 
 |  | seq == ntohs(tftp->th_block)) { | 
 |  | seq++; | 
 |  | if (SEGSIZE > len) { | 
 |  | close(f); | 
 |  | syslog(LOG_INFO, "TFTP transfer complete to %s", | 
 |  | szFName); | 
 |  | break; | 
 |  | } | 
 |  | } else { | 
 |  | memset(buf, 0, TFTP_BUF); | 
 |  | tftp->th_opcode = htons(ERROR); | 
 |  | tftp->th_code = htons(EBADID); | 
 |  | snprintf(tftp->th_data, STRSIZ, "Error:: in transfer #%d", | 
 |  | seq); | 
 |  | sendto(s, buf, strlen(tftp->th_data) + 4, 0, | 
 |  | (struct sockaddr*) &cli, sizeof cli); | 
 |  | retry--; | 
 |  | } | 
 |  | } | 
 |  | if (!retry) { | 
 |  | close(f); | 
 |  | syslog(LOG_ERR, "Error:: transfer aborted - timeout!"); | 
 |  | } | 
 |  |  | 
 |  | break; | 
 |  | case DATA: | 
 |  | if (seq == 0xffff) | 
 |  | VERB(5) printf("Error:: not specified operation!\n"); | 
 |  | break; | 
 |  | } | 
 |  | } | 
 |  |  | 
 |  | break; | 
 |  | } | 
 |  |  | 
 |  |  | 
 |  | close(s); | 
 | return 0; | return 0; | 
 | } | } |